mirror of
https://github.com/XRPLF/rippled.git
synced 2026-10-21 19:18:22 +00:00
Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55b265a8c2 | ||
|
|
02e40e7a00 | ||
|
|
f14395427e | ||
|
|
b707bac74f | ||
|
|
da2b5d5eaf | ||
|
|
4de4902200 | ||
|
|
2ea1f09dcb | ||
|
|
68b4b894de | ||
|
|
defd83e5a6 | ||
|
|
452e31a05f | ||
|
|
1798262d7a | ||
|
|
cd633b9ac9 | ||
|
|
5c5f7d315a | ||
|
|
0f7493ce50 | ||
|
|
40f61f828a | ||
|
|
108277f4b7 |
No files matched your search
@@ -30,6 +30,11 @@ Version 3.5.0 is not yet released.
|
|||||||
|
|
||||||
- `subscribe`, `unsubscribe`: Added an optional `mpt_issuances` request field, an array of MPT issuance IDs (hex strings). Subscribers receive a message with `type` `mptTransaction` for each validated transaction whose metadata affects a subscribed issuance; the message has the same fields as the `transactions` stream. MPT issuance subscriptions count toward the per-connection subscription limit. An empty array, a non-array value, or an invalid ID returns `invalidParams`. ([#5671](https://github.com/XRPLF/rippled/pull/5671))
|
- `subscribe`, `unsubscribe`: Added an optional `mpt_issuances` request field, an array of MPT issuance IDs (hex strings). Subscribers receive a message with `type` `mptTransaction` for each validated transaction whose metadata affects a subscribed issuance; the message has the same fields as the `transactions` stream. MPT issuance subscriptions count toward the per-connection subscription limit. An empty array, a non-array value, or an invalid ID returns `invalidParams`. ([#5671](https://github.com/XRPLF/rippled/pull/5671))
|
||||||
|
|
||||||
|
### Bugfixes in 3.5.0
|
||||||
|
|
||||||
|
- `channel_authorize`: The `channel_id` field now returns an `invalidParams` error if the value is not a string. [#7582](https://github.com/XRPLF/rippled/pull/7582)
|
||||||
|
- `channel_verify`: The `channel_id` and `signature` fields now return an `invalidParams` error if the value is not a string. [#7582](https://github.com/XRPLF/rippled/pull/7582)
|
||||||
|
|
||||||
## XRP Ledger server version 3.4.0
|
## XRP Ledger server version 3.4.0
|
||||||
|
|
||||||
Version 3.4.0 is not yet released. These changes are available in the 3.4.0 beta releases.
|
Version 3.4.0 is not yet released. These changes are available in the 3.4.0 beta releases.
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ namespace xrpl::ledger_entries {
|
|||||||
// builder's STObject and the wrapper's SLE.
|
// builder's STObject and the wrapper's SLE.
|
||||||
TEST(${name}Tests, BuilderSettersRoundTrip)
|
TEST(${name}Tests, BuilderSettersRoundTrip)
|
||||||
{
|
{
|
||||||
UInt256 const index{1u};
|
constexpr UInt256 index{1};
|
||||||
|
|
||||||
% for field in fields:
|
% for field in fields:
|
||||||
auto const ${field["paramName"]}Value = ${canonical_expr(field)};
|
auto const ${field["paramName"]}Value = ${canonical_expr(field)};
|
||||||
@@ -85,7 +85,7 @@ TEST(${name}Tests, BuilderSettersRoundTrip)
|
|||||||
// from that SLE, build a new wrapper, and verify all fields (and validate()).
|
// from that SLE, build a new wrapper, and verify all fields (and validate()).
|
||||||
TEST(${name}Tests, BuilderFromSleRoundTrip)
|
TEST(${name}Tests, BuilderFromSleRoundTrip)
|
||||||
{
|
{
|
||||||
UInt256 const index{2u};
|
constexpr UInt256 index{2};
|
||||||
|
|
||||||
% for field in fields:
|
% for field in fields:
|
||||||
auto const ${field["paramName"]}Value = ${canonical_expr(field)};
|
auto const ${field["paramName"]}Value = ${canonical_expr(field)};
|
||||||
@@ -146,7 +146,7 @@ TEST(${name}Tests, BuilderFromSleRoundTrip)
|
|||||||
// 3) Verify wrapper throws when constructed from wrong ledger entry type.
|
// 3) Verify wrapper throws when constructed from wrong ledger entry type.
|
||||||
TEST(${name}Tests, WrapperThrowsOnWrongEntryType)
|
TEST(${name}Tests, WrapperThrowsOnWrongEntryType)
|
||||||
{
|
{
|
||||||
UInt256 const index{3u};
|
constexpr UInt256 index{3};
|
||||||
|
|
||||||
// Build a valid ledger entry of a different type
|
// Build a valid ledger entry of a different type
|
||||||
// Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq
|
// Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq
|
||||||
@@ -177,7 +177,7 @@ TEST(${name}Tests, WrapperThrowsOnWrongEntryType)
|
|||||||
// 4) Verify builder throws when constructed from wrong ledger entry type.
|
// 4) Verify builder throws when constructed from wrong ledger entry type.
|
||||||
TEST(${name}Tests, BuilderThrowsOnWrongEntryType)
|
TEST(${name}Tests, BuilderThrowsOnWrongEntryType)
|
||||||
{
|
{
|
||||||
UInt256 const index{4u};
|
constexpr UInt256 index{4};
|
||||||
|
|
||||||
// Build a valid ledger entry of a different type
|
// Build a valid ledger entry of a different type
|
||||||
% if wrong_le_include == "Ticket":
|
% if wrong_le_include == "Ticket":
|
||||||
@@ -207,7 +207,7 @@ TEST(${name}Tests, BuilderThrowsOnWrongEntryType)
|
|||||||
// 5) Build with only required fields and verify optional fields return nullopt.
|
// 5) Build with only required fields and verify optional fields return nullopt.
|
||||||
TEST(${name}Tests, OptionalFieldsReturnNullopt)
|
TEST(${name}Tests, OptionalFieldsReturnNullopt)
|
||||||
{
|
{
|
||||||
UInt256 const index{3u};
|
constexpr UInt256 index{3};
|
||||||
|
|
||||||
% for field in required_fields:
|
% for field in required_fields:
|
||||||
auto const ${field["paramName"]}Value = ${canonical_expr(field)};
|
auto const ${field["paramName"]}Value = ${canonical_expr(field)};
|
||||||
|
|||||||
Generated
+6
-6
@@ -2,11 +2,11 @@
|
|||||||
"nodes": {
|
"nodes": {
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1781173989,
|
"lastModified": 1791130267,
|
||||||
"narHash": "sha256-fnzKKPvS+oieI/pTzotA5tkoM47EB1NpaBcgk4R97hE=",
|
"narHash": "sha256-1sjwcQMcgAbmiip/VivBSiK8p2bTkQGOvCuO7vnwfx4=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "8c91a71d13451abc40eb9dae8910f972f979852f",
|
"rev": "9013764fcc0ea99fa16cf7aa7decf8a5c3889dd2",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -47,11 +47,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1784611586,
|
"lastModified": 1791189933,
|
||||||
"narHash": "sha256-OfqgY+0hp/zseZB7uyH0U8kIDPS4scZZCyAurEplvG0=",
|
"narHash": "sha256-Y8eN7ki0Urx4Ojf4w1xSpWg8uk/3lJqK+E0Oth77rH0=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "14f58845249f3552a89b07772626b8d3c632fa86",
|
"rev": "e60029353d0c48d216bc4b065168ccd4079c166f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -1,29 +1,31 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <xrpl/beast/type_name.h>
|
#include <xrpl/beast/utility/instrumentation.h>
|
||||||
|
|
||||||
|
#include <boost/core/type_name.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iterator>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages all counted object types.
|
* Manages all counted object types.
|
||||||
|
*
|
||||||
|
* Counters register themselves on a lock-free intrusive list maintained
|
||||||
|
* by this object when constructed. Because counters are never destroyed
|
||||||
|
* or removed, the ABA problem does not apply.
|
||||||
|
*
|
||||||
|
* The registry is iterable as a forward range.
|
||||||
*/
|
*/
|
||||||
class CountedObjects
|
class CountedObjects
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
static CountedObjects&
|
|
||||||
getInstance() noexcept;
|
|
||||||
|
|
||||||
using Entry = std::pair<std::string, int>;
|
|
||||||
using List = std::vector<Entry>;
|
|
||||||
|
|
||||||
[[nodiscard]] List
|
|
||||||
getCounts(int minimumThreshold) const;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Implementation for @ref CountedObject.
|
* Implementation for @ref CountedObject.
|
||||||
@@ -33,68 +35,147 @@ public:
|
|||||||
class Counter
|
class Counter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Counter(std::string name) noexcept : name_(std::move(name)), count_(0)
|
Counter(std::string name) noexcept;
|
||||||
{
|
|
||||||
// Insert ourselves at the front of the lock-free linked list
|
|
||||||
CountedObjects& instance = CountedObjects::getInstance();
|
|
||||||
Counter* head = nullptr;
|
|
||||||
|
|
||||||
do
|
// Counters are intrusive list nodes whose addresses are published
|
||||||
{
|
// in the registry; they must never be copied or moved. The atomic
|
||||||
head = instance.head_.load();
|
// members already force this, but we make it explicit.
|
||||||
next_ = head;
|
Counter(Counter const&) = delete;
|
||||||
} while (instance.head_.exchange(this) != head);
|
Counter&
|
||||||
|
operator=(Counter const&) = delete;
|
||||||
|
Counter(Counter&&) = delete;
|
||||||
|
Counter&
|
||||||
|
operator=(Counter&&) = delete;
|
||||||
|
|
||||||
++instance.count_;
|
std::uint32_t
|
||||||
}
|
|
||||||
|
|
||||||
~Counter() noexcept = default;
|
|
||||||
|
|
||||||
int
|
|
||||||
increment() noexcept
|
increment() noexcept
|
||||||
{
|
{
|
||||||
return ++count_;
|
auto const newCount = count_.fetch_add(1, std::memory_order::relaxed) + 1;
|
||||||
|
XRPL_ASSERT(newCount != 0, "xrpl::CountedObjects::Counter::increment : no overflow");
|
||||||
|
|
||||||
|
auto maxCount = maxCount_.load(std::memory_order::relaxed);
|
||||||
|
|
||||||
|
while (newCount > maxCount &&
|
||||||
|
!maxCount_.compare_exchange_weak(maxCount, newCount, std::memory_order::relaxed))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
return newCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
std::uint32_t
|
||||||
decrement() noexcept
|
decrement() noexcept
|
||||||
{
|
{
|
||||||
return --count_;
|
auto const prev = count_.fetch_sub(1, std::memory_order::relaxed);
|
||||||
|
XRPL_ASSERT(prev != 0, "xrpl::CountedObjects::Counter::decrement : no underflow");
|
||||||
|
return prev - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] int
|
[[nodiscard]] std::uint32_t
|
||||||
getCount() const noexcept
|
count() const noexcept
|
||||||
{
|
{
|
||||||
return count_.load();
|
return count_.load(std::memory_order::relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] Counter*
|
[[nodiscard]] std::uint32_t
|
||||||
getNext() const noexcept
|
max() const noexcept
|
||||||
{
|
{
|
||||||
return next_;
|
return std::max(
|
||||||
|
count_.load(std::memory_order::relaxed),
|
||||||
|
maxCount_.load(std::memory_order::relaxed));
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::string const&
|
[[nodiscard]] std::string const&
|
||||||
getName() const noexcept
|
name() const noexcept
|
||||||
{
|
{
|
||||||
return name_;
|
return name_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class CountedObjects;
|
||||||
|
|
||||||
|
Counter* next_ = nullptr;
|
||||||
|
std::atomic<std::uint32_t> count_ = 0;
|
||||||
|
std::atomic<std::uint32_t> maxCount_ = 0;
|
||||||
std::string const name_;
|
std::string const name_;
|
||||||
std::atomic<int> count_;
|
|
||||||
Counter* next_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
class Iterator
|
||||||
CountedObjects() noexcept;
|
{
|
||||||
~CountedObjects() noexcept = default;
|
public:
|
||||||
|
using value_type = Counter const;
|
||||||
|
using reference = value_type&;
|
||||||
|
using pointer = value_type*;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using iterator_category = std::forward_iterator_tag;
|
||||||
|
|
||||||
|
explicit Iterator(Counter* c = nullptr) noexcept : current_(c)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
reference
|
||||||
|
operator*() const noexcept
|
||||||
|
{
|
||||||
|
return *current_;
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer
|
||||||
|
operator->() const noexcept
|
||||||
|
{
|
||||||
|
return current_;
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator&
|
||||||
|
operator++() noexcept
|
||||||
|
{
|
||||||
|
current_ = current_->next_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator
|
||||||
|
operator++(int) noexcept
|
||||||
|
{
|
||||||
|
auto tmp = *this;
|
||||||
|
++*this;
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator==(Iterator const&) const noexcept = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Counter* current_;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr CountedObjects() noexcept = default;
|
||||||
|
|
||||||
|
[[nodiscard]] auto
|
||||||
|
begin() const noexcept
|
||||||
|
{
|
||||||
|
return Iterator{head_.load(std::memory_order::acquire)};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto
|
||||||
|
end() const noexcept
|
||||||
|
{
|
||||||
|
return Iterator{};
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::atomic<int> count_;
|
std::atomic<Counter*> head_ = nullptr;
|
||||||
std::atomic<Counter*> head_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** The global counted object registry. */
|
||||||
|
inline constinit CountedObjects gCountedObjects;
|
||||||
|
|
||||||
|
inline CountedObjects::Counter::Counter(std::string name) noexcept
|
||||||
|
: next_(gCountedObjects.head_.load(std::memory_order::relaxed)), name_(std::move(name))
|
||||||
|
{
|
||||||
|
while (!gCountedObjects.head_.compare_exchange_weak(
|
||||||
|
next_, this, std::memory_order::release, std::memory_order::relaxed))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -103,27 +184,37 @@ private:
|
|||||||
* Derived classes have their instances counted automatically. This is used
|
* Derived classes have their instances counted automatically. This is used
|
||||||
* for reporting purposes.
|
* for reporting purposes.
|
||||||
*
|
*
|
||||||
|
* The constructors are private and `Object` is befriended so that the
|
||||||
|
* CRTP parameter must be the deriving class itself: a copy-paste error
|
||||||
|
* like `class B : public CountedObject<A>` fails to compile instead of
|
||||||
|
* silently polluting A's count.
|
||||||
|
*
|
||||||
|
* @note This class has no move operations by design: a derived class's
|
||||||
|
* move constructor falls back to the copy constructor for this
|
||||||
|
* base, so the newly created instance is counted. This keeps the
|
||||||
|
* invariant that count is the number of outstanding subobjects.
|
||||||
|
*
|
||||||
|
* @warning Counted objects constructed during dynamic initialization of
|
||||||
|
* other translation units may have their increments discarded when
|
||||||
|
* counter itself is dynamically initialized. Do not create counted
|
||||||
|
* objects before main() begins.
|
||||||
|
*
|
||||||
* @ingroup basics
|
* @ingroup basics
|
||||||
*/
|
*/
|
||||||
template <class Object>
|
template <class Object>
|
||||||
|
requires std::is_class_v<Object>
|
||||||
class CountedObject
|
class CountedObject
|
||||||
{
|
{
|
||||||
private:
|
static inline CountedObjects::Counter counter{boost::core::type_name<Object>()};
|
||||||
static auto&
|
|
||||||
getCounter() noexcept
|
|
||||||
{
|
|
||||||
static CountedObjects::Counter kC{beast::typeName<Object>()};
|
|
||||||
return kC;
|
|
||||||
}
|
|
||||||
|
|
||||||
CountedObject() noexcept
|
CountedObject() noexcept
|
||||||
{
|
{
|
||||||
getCounter().increment();
|
counter.increment();
|
||||||
}
|
}
|
||||||
|
|
||||||
CountedObject(CountedObject const&) noexcept
|
CountedObject(CountedObject const&) noexcept
|
||||||
{
|
{
|
||||||
getCounter().increment();
|
counter.increment();
|
||||||
}
|
}
|
||||||
|
|
||||||
CountedObject&
|
CountedObject&
|
||||||
@@ -132,7 +223,7 @@ private:
|
|||||||
public:
|
public:
|
||||||
~CountedObject() noexcept
|
~CountedObject() noexcept
|
||||||
{
|
{
|
||||||
getCounter().decrement();
|
counter.decrement();
|
||||||
}
|
}
|
||||||
|
|
||||||
friend Object;
|
friend Object;
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <xrpl/basics/ByteUtilities.h>
|
#include <xrpl/basics/ByteUtilities.h>
|
||||||
#include <xrpl/beast/type_name.h>
|
|
||||||
#include <xrpl/beast/utility/instrumentation.h>
|
#include <xrpl/beast/utility/instrumentation.h>
|
||||||
|
|
||||||
#include <boost/align.hpp>
|
#include <boost/align.hpp>
|
||||||
#include <boost/container/static_vector.hpp>
|
#include <boost/container/static_vector.hpp>
|
||||||
|
#include <boost/core/type_name.hpp>
|
||||||
#include <boost/predef.h>
|
#include <boost/predef.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <typeinfo>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#if BOOST_OS_LINUX
|
#if BOOST_OS_LINUX
|
||||||
@@ -335,7 +336,7 @@ public:
|
|||||||
}) != cfg.end())
|
}) != cfg.end())
|
||||||
{
|
{
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"SlabAllocatorSet<" + beast::typeName<Type>() + ">: duplicate slab size");
|
"SlabAllocatorSet<" + boost::core::type_name<Type>() + ">: duplicate slab size");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto const& c : cfg)
|
for (auto const& c : cfg)
|
||||||
|
|||||||
+441
-409
File diff suppressed because it is too large.
Load diff
@@ -1,7 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <xrpl/basics/sanitizers.h>
|
#include <xrpl/basics/sanitizers.h>
|
||||||
#include <xrpl/beast/type_name.h>
|
|
||||||
|
#include <boost/core/type_name.hpp>
|
||||||
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -55,7 +56,7 @@ Throw(Args&&... args)
|
|||||||
std::is_convertible_v<E*, std::exception*>, "Exception must derive from std::exception.");
|
std::is_convertible_v<E*, std::exception*>, "Exception must derive from std::exception.");
|
||||||
|
|
||||||
E e(std::forward<Args>(args)...);
|
E e(std::forward<Args>(args)...);
|
||||||
logThrow(std::string("Throwing exception of type " + beast::typeName<E>() + ": ") + e.what());
|
logThrow("Throwing exception of type " + boost::core::type_name<E>() + ": " + e.what());
|
||||||
throw std::move(e);
|
throw std::move(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,200 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <concepts>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace xrpl {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opt-in bitwise operators for scoped enumerations.
|
||||||
|
*
|
||||||
|
* Provides `&`, `|`, `^`, `~` and the corresponding compound assignment forms
|
||||||
|
* for any scoped enumeration with an unsigned underlying type that opts in by
|
||||||
|
* specializing @ref OptIn:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* namespace xrpl {
|
||||||
|
*
|
||||||
|
* enum class MyFlags : std::uint32_t { a = 1, b = 2, c = 4 };
|
||||||
|
*
|
||||||
|
* template <>
|
||||||
|
* struct enum_bitops::OptIn<MyFlags> : std::true_type {};
|
||||||
|
*
|
||||||
|
* } // namespace xrpl
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* The operators are constexpr and noexcept, and return the enumeration type.
|
||||||
|
*
|
||||||
|
* @par Where the specialization may be declared
|
||||||
|
* In namespace xrpl, or at global scope with full qualification, and after
|
||||||
|
* the enumeration is defined but before the operators are first used. This
|
||||||
|
* cannot be done in a nested namespace or at class scope; for enumerations
|
||||||
|
* nested in a class this means after the class definition.
|
||||||
|
*
|
||||||
|
* @par Where the operators are found
|
||||||
|
* The operators are declared in namespace xrpl, so argument-dependent lookup
|
||||||
|
* will find them only for enumerations declared in xrpl or nested in a class
|
||||||
|
* declared directly in xrpl. Enumerations in a nested namespace will only be
|
||||||
|
* found from code inside xrpl, and only if no enclosing scope declares an
|
||||||
|
* operator of the same name; elsewhere they require using-declarations.
|
||||||
|
*/
|
||||||
|
namespace enum_bitops {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Types that may be opted in to the bitwise operators.
|
||||||
|
*
|
||||||
|
* Satisfied by a scoped enumeration whose underlying type is an unsigned
|
||||||
|
* integer type.
|
||||||
|
*
|
||||||
|
* @tparam T The type to test.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
concept Eligible = std::is_scoped_enum_v<T> && std::unsigned_integral<std::underlying_type_t<T>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opt-in switch for the bitwise operators.
|
||||||
|
*
|
||||||
|
* The primary template derives from std::false_type. Specialize it to derive
|
||||||
|
* from std::true_type to enable the operators for an @ref Eligible enumeration.
|
||||||
|
*
|
||||||
|
* @tparam T The scoped enumeration to opt in.
|
||||||
|
*/
|
||||||
|
template <Eligible T>
|
||||||
|
struct OptIn : std::false_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enumerations for which the bitwise operators are enabled.
|
||||||
|
*
|
||||||
|
* Satisfied when @p T satisfies @ref Eligible and @ref OptIn has been
|
||||||
|
* specialized for it to derive from std::true_type.
|
||||||
|
*
|
||||||
|
* @tparam T The type to test.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
concept Candidate = Eligible<T> && OptIn<T>::value;
|
||||||
|
|
||||||
|
} // namespace enum_bitops
|
||||||
|
|
||||||
|
// These are declared in xrpl, not in enum_bitops, so that argument-dependent
|
||||||
|
// lookup finds them for enumerations whose associated namespace is xrpl.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Bitwise operators for opted-in scoped enumerations
|
||||||
|
*
|
||||||
|
* Each operator applies the corresponding built-in operator to the
|
||||||
|
* underlying values and converts the result back to the enumeration type.
|
||||||
|
* Both operands must have the same enumeration type; there is no implicit
|
||||||
|
* conversion to or from the underlying type.
|
||||||
|
*/
|
||||||
|
/** @{ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bitwise AND.
|
||||||
|
*
|
||||||
|
* @param lhs The left operand.
|
||||||
|
* @param rhs The right operand.
|
||||||
|
* @return The bits set in both @p lhs and @p rhs.
|
||||||
|
*/
|
||||||
|
template <enum_bitops::Candidate T>
|
||||||
|
constexpr T
|
||||||
|
operator&(T lhs, T rhs) noexcept
|
||||||
|
{
|
||||||
|
return static_cast<T>(std::to_underlying(lhs) & std::to_underlying(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bitwise OR.
|
||||||
|
*
|
||||||
|
* @param lhs The left operand.
|
||||||
|
* @param rhs The right operand.
|
||||||
|
* @return The bits set in @p lhs, in @p rhs, or in both.
|
||||||
|
*/
|
||||||
|
template <enum_bitops::Candidate T>
|
||||||
|
constexpr T
|
||||||
|
operator|(T lhs, T rhs) noexcept
|
||||||
|
{
|
||||||
|
return static_cast<T>(std::to_underlying(lhs) | std::to_underlying(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bitwise exclusive OR.
|
||||||
|
*
|
||||||
|
* @param lhs The left operand.
|
||||||
|
* @param rhs The right operand.
|
||||||
|
* @return The bits set in exactly one of @p lhs and @p rhs.
|
||||||
|
*/
|
||||||
|
template <enum_bitops::Candidate T>
|
||||||
|
constexpr T
|
||||||
|
operator^(T lhs, T rhs) noexcept
|
||||||
|
{
|
||||||
|
return static_cast<T>(std::to_underlying(lhs) ^ std::to_underlying(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bitwise complement.
|
||||||
|
*
|
||||||
|
* The result has every bit of the underlying type that is clear in
|
||||||
|
* @p val, including bits that no enumerator names. It is intended for
|
||||||
|
* clearing flags, as in `flags & ~flag`.
|
||||||
|
*
|
||||||
|
* @param val The operand.
|
||||||
|
* @return The complement of @p val.
|
||||||
|
*/
|
||||||
|
template <enum_bitops::Candidate T>
|
||||||
|
constexpr T
|
||||||
|
operator~(T val) noexcept
|
||||||
|
{
|
||||||
|
return static_cast<T>(~std::to_underlying(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bitwise AND assignment.
|
||||||
|
*
|
||||||
|
* @param lhs The value to modify.
|
||||||
|
* @param rhs The right operand.
|
||||||
|
* @return A reference to @p lhs.
|
||||||
|
*/
|
||||||
|
template <enum_bitops::Candidate T>
|
||||||
|
constexpr T&
|
||||||
|
operator&=(T& lhs, T rhs) noexcept
|
||||||
|
{
|
||||||
|
lhs = lhs & rhs;
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bitwise OR assignment.
|
||||||
|
*
|
||||||
|
* @param lhs The value to modify.
|
||||||
|
* @param rhs The right operand.
|
||||||
|
* @return A reference to @p lhs.
|
||||||
|
*/
|
||||||
|
template <enum_bitops::Candidate T>
|
||||||
|
constexpr T&
|
||||||
|
operator|=(T& lhs, T rhs) noexcept
|
||||||
|
{
|
||||||
|
lhs = lhs | rhs;
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bitwise exclusive OR assignment.
|
||||||
|
*
|
||||||
|
* @param lhs The value to modify.
|
||||||
|
* @param rhs The right operand.
|
||||||
|
* @return A reference to @p lhs.
|
||||||
|
*/
|
||||||
|
template <enum_bitops::Candidate T>
|
||||||
|
constexpr T&
|
||||||
|
operator^=(T& lhs, T rhs) noexcept
|
||||||
|
{
|
||||||
|
lhs = lhs ^ rhs;
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
} // namespace xrpl
|
||||||
+152
-47
@@ -2,107 +2,212 @@
|
|||||||
|
|
||||||
#include <xrpl/beast/utility/instrumentation.h> // IWYU pragma: keep
|
#include <xrpl/beast/utility/instrumentation.h> // IWYU pragma: keep
|
||||||
|
|
||||||
|
#include <concepts>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
// safe_cast adds compile-time checks to a static_cast to ensure that
|
/** Every value of @p Src can be represented by @p Dest.
|
||||||
// the destination can hold all values of the source. This is particularly
|
|
||||||
// handy when the source or destination is an enumeration type.
|
|
||||||
|
|
||||||
template <class Src, class Dest>
|
Given two integral types, the cast is safe when the destination can
|
||||||
concept SafeToCast = (std::is_integral_v<Src> && std::is_integral_v<Dest>) &&
|
hold every possible value of the source.
|
||||||
(std::is_signed_v<Src> || std::is_unsigned_v<Dest>) &&
|
|
||||||
(std::is_signed_v<Src> != std::is_signed_v<Dest> ? sizeof(Dest) > sizeof(Src)
|
|
||||||
: sizeof(Dest) >= sizeof(Src));
|
|
||||||
|
|
||||||
template <class Dest, class Src>
|
Comparing the bounds requires care: we use @c std::cmp_less_equal and
|
||||||
|
@c std::cmp_greater_equal; the plain relational operators would apply
|
||||||
|
arithmetic conversions, resulting in incorrect results when comparing
|
||||||
|
across signedness.
|
||||||
|
|
||||||
|
Because @c std::cmp_* requires standard signed or unsigned integer
|
||||||
|
arguments, which excludes character types and bool, we first widen
|
||||||
|
all type bounds to the maximum-width integer type while preserving
|
||||||
|
signedness.
|
||||||
|
|
||||||
|
@note Extended integer types, like __int128 on gcc, cannot be safely
|
||||||
|
widened to a standard integer type, so the concept will reject
|
||||||
|
them.
|
||||||
|
*/
|
||||||
|
template <typename Src, typename Dest>
|
||||||
|
concept SafeToCast = std::is_integral_v<Src> && std::is_integral_v<Dest> && []() consteval {
|
||||||
|
using WideSrc = std::conditional_t<std::is_signed_v<Src>, std::intmax_t, std::uintmax_t>;
|
||||||
|
using WideDest = std::conditional_t<std::is_signed_v<Dest>, std::intmax_t, std::uintmax_t>;
|
||||||
|
|
||||||
|
// Note: this guard must be an evaluated branch, and not a
|
||||||
|
// static_assert. The lambda body is outside the immediate
|
||||||
|
// context, so a substitution-time failure here would be a
|
||||||
|
// hard error rather than leaving the concept unsatisfied.
|
||||||
|
if constexpr (sizeof(Src) > sizeof(WideSrc) || sizeof(Dest) > sizeof(WideDest))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return std::cmp_less_equal(
|
||||||
|
static_cast<WideDest>(std::numeric_limits<Dest>::min()),
|
||||||
|
static_cast<WideSrc>(std::numeric_limits<Src>::min())) &&
|
||||||
|
std::cmp_greater_equal(
|
||||||
|
static_cast<WideDest>(std::numeric_limits<Dest>::max()),
|
||||||
|
static_cast<WideSrc>(std::numeric_limits<Src>::max()));
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
/** Compile-time-checked static_cast that rejects non-value preserving casts.
|
||||||
|
|
||||||
|
@note There is deliberately no enum-to-enum overload, and no overload
|
||||||
|
returning the underlying type of an enum. For the latter, use
|
||||||
|
@c std::to_underlying.
|
||||||
|
*/
|
||||||
|
/** @{ */
|
||||||
|
template <typename Dest, typename Src>
|
||||||
|
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
|
||||||
constexpr Dest
|
constexpr Dest
|
||||||
safeCast(Src s) noexcept
|
safeCast(Src s) noexcept
|
||||||
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
|
|
||||||
{
|
{
|
||||||
static_assert(
|
static_assert(
|
||||||
std::is_signed_v<Dest> || std::is_unsigned_v<Src>, "Cannot cast signed to unsigned");
|
SafeToCast<Src, Dest>, "This cast is not value-preserving. Please use unsafeCast instead.");
|
||||||
constexpr unsigned kNotSame = std::is_signed_v<Dest> != std::is_signed_v<Src>;
|
|
||||||
static_assert(
|
|
||||||
sizeof(Dest) >= sizeof(Src) + kNotSame,
|
|
||||||
"Destination is too small to hold all values of source");
|
|
||||||
return static_cast<Dest>(s);
|
return static_cast<Dest>(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Dest, class Src>
|
template <typename Dest, typename Src>
|
||||||
|
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
|
||||||
constexpr Dest
|
constexpr Dest
|
||||||
safeCast(Src s) noexcept
|
safeCast(Src s) noexcept
|
||||||
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
|
|
||||||
{
|
{
|
||||||
return static_cast<Dest>(safeCast<std::underlying_type_t<Dest>>(s));
|
return static_cast<Dest>(safeCast<std::underlying_type_t<Dest>>(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Dest, class Src>
|
template <typename Dest, typename Src>
|
||||||
|
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
|
||||||
constexpr Dest
|
constexpr Dest
|
||||||
safeCast(Src s) noexcept
|
safeCast(Src s) noexcept
|
||||||
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
|
|
||||||
{
|
{
|
||||||
return safeCast<Dest>(static_cast<std::underlying_type_t<Src>>(s));
|
return safeCast<Dest>(std::to_underlying(s));
|
||||||
}
|
}
|
||||||
|
/** @} */
|
||||||
|
|
||||||
// unsafe_cast explicitly flags a static_cast as not necessarily able to hold
|
/** Integral-to-integral cast that is known to be narrowing or sign-erasing.
|
||||||
// all values of the source. It includes a compile-time check so that if
|
|
||||||
// underlying types become safe, it can be converted to a safe_cast.
|
|
||||||
|
|
||||||
template <class Dest, class Src>
|
This explicitly flags a conversion that can lose information for some
|
||||||
|
values of the source type, where the call site accepts that loss (or
|
||||||
|
truncation is the intended behavior).
|
||||||
|
|
||||||
|
The compile-time check ensures the cast remains "unsafe": if the types
|
||||||
|
involved later change such that the conversion becomes inherently
|
||||||
|
value-preserving, the static assertion fires with instructions to
|
||||||
|
migrate the call site to @ref safeCast.
|
||||||
|
|
||||||
|
If the conversion's safety depends on a runtime precondition rather
|
||||||
|
than on the types, or varies across instantiations of generic code,
|
||||||
|
use @ref checkedCast instead.
|
||||||
|
*/
|
||||||
|
/** @{ */
|
||||||
|
template <typename Dest, typename Src>
|
||||||
|
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
|
||||||
constexpr Dest
|
constexpr Dest
|
||||||
unsafeCast(Src s) noexcept
|
unsafeCast(Src s) noexcept
|
||||||
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
|
|
||||||
{
|
{
|
||||||
static_assert(
|
static_assert(
|
||||||
!SafeToCast<Src, Dest>,
|
!SafeToCast<Src, Dest>, "This cast is value-preserving. Please use safeCast instead.");
|
||||||
"Only unsafe if casting signed to unsigned or "
|
|
||||||
"destination is too small");
|
|
||||||
return static_cast<Dest>(s);
|
return static_cast<Dest>(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Dest, class Src>
|
template <typename Dest, typename Src>
|
||||||
|
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
|
||||||
constexpr Dest
|
constexpr Dest
|
||||||
unsafeCast(Src s) noexcept
|
unsafeCast(Src s) noexcept
|
||||||
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
|
|
||||||
{
|
{
|
||||||
return static_cast<Dest>(unsafeCast<std::underlying_type_t<Dest>>(s));
|
return static_cast<Dest>(unsafeCast<std::underlying_type_t<Dest>>(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Dest, class Src>
|
template <typename Dest, typename Src>
|
||||||
|
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
|
||||||
constexpr Dest
|
constexpr Dest
|
||||||
unsafeCast(Src s) noexcept
|
unsafeCast(Src s) noexcept
|
||||||
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
|
|
||||||
{
|
{
|
||||||
return unsafeCast<Dest>(static_cast<std::underlying_type_t<Src>>(s));
|
return unsafeCast<Dest>(std::to_underlying(s));
|
||||||
|
}
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/** Integral-to-integral cast when the caller has performed a bounds check.
|
||||||
|
|
||||||
|
This documents that a runtime precondition or external invariant, which
|
||||||
|
is not necessarily visible to the compiler, guarantees that the requested
|
||||||
|
conversion is value-preserving for the values that can actually occur.
|
||||||
|
|
||||||
|
This is primarily meant for generic code, where the same expression may
|
||||||
|
be value-preserving for one instantiation but not for another, making
|
||||||
|
both @ref safeCast and @ref unsafeCast unusable.
|
||||||
|
|
||||||
|
Unlike @ref safeCast and @ref unsafeCast, this imposes no static check
|
||||||
|
on the type relationship: the caller's claim is about runtime values,
|
||||||
|
not about types.
|
||||||
|
*/
|
||||||
|
/** @{ */
|
||||||
|
template <typename Dest, typename Src>
|
||||||
|
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
|
||||||
|
constexpr Dest
|
||||||
|
checkedCast(Src s) noexcept
|
||||||
|
{
|
||||||
|
return static_cast<Dest>(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Dest, typename Src>
|
||||||
|
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
|
||||||
|
constexpr Dest
|
||||||
|
checkedCast(Src s) noexcept
|
||||||
|
{
|
||||||
|
return static_cast<Dest>(checkedCast<std::underlying_type_t<Dest>>(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Dest, typename Src>
|
||||||
|
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
|
||||||
|
constexpr Dest
|
||||||
|
checkedCast(Src s) noexcept
|
||||||
|
{
|
||||||
|
return checkedCast<Dest>(std::to_underlying(s));
|
||||||
|
}
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/** Downcast within a class hierarchy, verified in debug builds.
|
||||||
|
|
||||||
|
Performs a static_cast down a hierarchy, but in debug builds verifies
|
||||||
|
via dynamic_cast that the object's dynamic type actually permits the
|
||||||
|
downcast. Both build modes execute the same conversion; debug builds
|
||||||
|
merely add the check.
|
||||||
|
|
||||||
|
The pointer form passes null through unchanged, as dynamic_cast does.
|
||||||
|
|
||||||
|
@note The check requires @p Src to be polymorphic; in release builds
|
||||||
|
an invalid downcast is undefined behavior on use, exactly as
|
||||||
|
with a bare static_cast.
|
||||||
|
*/
|
||||||
|
/** @{ */
|
||||||
template <class Dest, class Src>
|
template <class Dest, class Src>
|
||||||
requires std::is_pointer_v<Dest>
|
requires(
|
||||||
|
std::is_pointer_v<Dest> && std::is_polymorphic_v<Src> &&
|
||||||
|
std::derived_from<std::remove_pointer_t<Dest>, Src> &&
|
||||||
|
(std::is_const_v<std::remove_pointer_t<Dest>> || !std::is_const_v<Src>) &&
|
||||||
|
(std::is_volatile_v<std::remove_pointer_t<Dest>> || !std::is_volatile_v<Src>))
|
||||||
inline Dest
|
inline Dest
|
||||||
safeDowncast(Src* s) noexcept
|
safeDowncast(Src* s) noexcept
|
||||||
{
|
{
|
||||||
#ifdef NDEBUG
|
XRPL_ASSERT(
|
||||||
|
s == nullptr || dynamic_cast<Dest>(s) != nullptr, "xrpl::safeDowncast : valid downcast");
|
||||||
return static_cast<Dest>(s); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
|
return static_cast<Dest>(s); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
|
||||||
#else
|
|
||||||
auto* result = dynamic_cast<Dest>(s);
|
|
||||||
XRPL_ASSERT(result != nullptr, "xrpl::safeDowncast : pointer downcast is valid");
|
|
||||||
return result;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Dest, class Src>
|
template <class Dest, class Src>
|
||||||
requires std::is_lvalue_reference_v<Dest>
|
requires(
|
||||||
|
std::is_lvalue_reference_v<Dest> && std::is_polymorphic_v<Src> &&
|
||||||
|
std::derived_from<std::remove_reference_t<Dest>, Src>)
|
||||||
inline Dest
|
inline Dest
|
||||||
safeDowncast(Src& s) noexcept
|
safeDowncast(Src& s) noexcept
|
||||||
{
|
{
|
||||||
#ifndef NDEBUG
|
return *safeDowncast<std::add_pointer_t<std::remove_reference_t<Dest>>>(std::addressof(s));
|
||||||
XRPL_ASSERT(
|
|
||||||
dynamic_cast<std::add_pointer_t<std::remove_reference_t<Dest>>>(&s) != nullptr,
|
|
||||||
"xrpl::safeDowncast : reference downcast is valid");
|
|
||||||
#endif
|
|
||||||
return static_cast<Dest>(s); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
|
|
||||||
}
|
}
|
||||||
|
/** @} */
|
||||||
|
|
||||||
} // namespace xrpl
|
} // namespace xrpl
|
||||||
+209
-153
@@ -4,88 +4,215 @@
|
|||||||
|
|
||||||
#include <xrpl/beast/utility/instrumentation.h>
|
#include <xrpl/beast/utility/instrumentation.h>
|
||||||
|
|
||||||
|
#include <boost/predef/architecture.h>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <concepts>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#ifndef __aarch64__
|
#if BOOST_ARCH_X86
|
||||||
#include <immintrin.h>
|
#include <immintrin.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
|
/** An unsigned integral type suitable for use as a spinlock.
|
||||||
|
|
||||||
|
The type must be always lock-free when wrapped in std::atomic, so
|
||||||
|
that lock operations cannot themselves take a (library-level) lock.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
concept SpinlockValueType = std::is_unsigned_v<T> && std::atomic<T>::is_always_lock_free;
|
||||||
|
|
||||||
|
/** A spinlock value type that additionally supports the atomic bitwise
|
||||||
|
operations required to pack multiple locks into a single integer.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
concept PackedSpinlockValueType = SpinlockValueType<T> && requires(std::atomic<T>& a, T v) {
|
||||||
|
{ a.fetch_or(v) } -> std::same_as<T>;
|
||||||
|
{ a.fetch_and(v) } -> std::same_as<T>;
|
||||||
|
};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
/**
|
|
||||||
* Inform the processor that we are in a tight spin-wait loop.
|
/** Inform the processor that we are in a tight spin-wait loop.
|
||||||
*
|
|
||||||
* Spinlocks caught in tight loops can result in the processor's pipeline
|
Spinlocks caught in tight loops can result in the processor's pipeline
|
||||||
* filling up with comparison operations, resulting in a misprediction at
|
filling up with comparison operations, resulting in a misprediction at
|
||||||
* the time the lock is finally acquired, necessitating pipeline flushing
|
the time the lock is finally acquired, necessitating pipeline flushing
|
||||||
* which is ridiculously expensive and results in very high latency.
|
which is ridiculously expensive and results in very high latency.
|
||||||
*
|
|
||||||
* This function instructs the processor to "pause" for some architecture
|
This function instructs the processor to "pause" for some architecture
|
||||||
* specific amount of time, to prevent this.
|
specific amount of time, to prevent this.
|
||||||
*/
|
*/
|
||||||
inline void
|
inline void
|
||||||
spinPause() noexcept
|
spinPause() noexcept
|
||||||
{
|
{
|
||||||
#ifdef __aarch64__
|
#if BOOST_ARCH_X86
|
||||||
asm volatile("yield");
|
|
||||||
#else
|
|
||||||
_mm_pause();
|
_mm_pause();
|
||||||
|
#elif BOOST_ARCH_ARM
|
||||||
|
asm volatile("yield" ::: "memory");
|
||||||
|
#else
|
||||||
|
#error No implementation available for spinPause to use
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
/** @{ */
|
//------------------------------------------------------------------------------
|
||||||
/**
|
|
||||||
* Classes to handle arrays of spinlocks packed into a single atomic integer:
|
|
||||||
*
|
|
||||||
* Packed spinlocks allow for tremendously space-efficient lock-sharding
|
|
||||||
* but they come at a cost.
|
|
||||||
*
|
|
||||||
* First, the implementation is necessarily low-level and uses advanced
|
|
||||||
* features like memory ordering and highly platform-specific tricks to
|
|
||||||
* maximize performance. This imposes a significant and ongoing cost to
|
|
||||||
* developers.
|
|
||||||
*
|
|
||||||
* Second, and perhaps most important, is that the packing of multiple
|
|
||||||
* locks into a single integer which, albeit space-efficient, also has
|
|
||||||
* performance implications stemming from data dependencies, increased
|
|
||||||
* cache-coherency traffic between processors and heavier loads on the
|
|
||||||
* processor's load/store units.
|
|
||||||
*
|
|
||||||
* To be sure, these locks can have advantages but they are definitely
|
|
||||||
* not general purpose locks and should not be thought of or used that
|
|
||||||
* way. The use cases for them are likely few and far between; without
|
|
||||||
* a compelling reason to use them, backed by profiling data, it might
|
|
||||||
* be best to use one of the standard locking primitives instead. Note
|
|
||||||
* that in most common platforms, `std::mutex` is so heavily optimized
|
|
||||||
* that it can, usually, outperform spinlocks.
|
|
||||||
*
|
|
||||||
* @tparam T An unsigned integral type (e.g. std::uint16_t)
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/** Attempt to acquire a spinlock without blocking.
|
||||||
* A class that grabs a single packed spinlock from an atomic integer.
|
|
||||||
*
|
@note This interface is primarily intended for one-shot attempts to
|
||||||
* This class meets the requirements of Lockable:
|
acquire the lock. Avoid calling this function directly from a
|
||||||
* https://en.cppreference.com/w/cpp/named_req/Lockable
|
loop and use @ref spinLock instead.
|
||||||
|
|
||||||
|
@tparam T An unsigned integral type.
|
||||||
|
@param lock The atomic variable used as the lock.
|
||||||
|
@return true if the lock was acquired, false if it was already held.
|
||||||
*/
|
*/
|
||||||
template <class T>
|
template <SpinlockValueType T>
|
||||||
|
[[nodiscard]] bool
|
||||||
|
spinTryLock(std::atomic<T>& lock) noexcept
|
||||||
|
{
|
||||||
|
// A compare-exchange is required here, not an unconditional exchange:
|
||||||
|
// a failed attempt must not modify the lock word, in case the atomic
|
||||||
|
// is shared with PackedSpinlock).
|
||||||
|
T expected = 0;
|
||||||
|
|
||||||
|
return lock.compare_exchange_strong(
|
||||||
|
expected,
|
||||||
|
std::numeric_limits<T>::max(),
|
||||||
|
std::memory_order::acquire,
|
||||||
|
std::memory_order::relaxed);
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Acquire a spinlock, blocking until available.
|
||||||
|
|
||||||
|
Uses a TTAS (test-and-test-and-set) pattern, so waiters share the cache
|
||||||
|
line read-only, helping to avoid unnecessary coherency traffic.
|
||||||
|
|
||||||
|
@tparam T An unsigned integral type.
|
||||||
|
@param lock The atomic variable used as the lock.
|
||||||
|
*/
|
||||||
|
template <SpinlockValueType T>
|
||||||
|
void
|
||||||
|
spinLock(std::atomic<T>& lock) noexcept
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Relaxed ordering is sufficient for the spin: this load is only
|
||||||
|
// a filter. The acquire on the successful exchange in spinTryLock
|
||||||
|
// is what synchronizes the critical section.
|
||||||
|
while (lock.load(std::memory_order::relaxed) != 0)
|
||||||
|
detail::spinPause();
|
||||||
|
} while (!spinTryLock(lock));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Release a spinlock.
|
||||||
|
|
||||||
|
@tparam T An unsigned integral type.
|
||||||
|
@param lock The atomic variable used as the lock.
|
||||||
|
*/
|
||||||
|
template <SpinlockValueType T>
|
||||||
|
void
|
||||||
|
spinUnlock(std::atomic<T>& lock) noexcept
|
||||||
|
{
|
||||||
|
lock.store(0, std::memory_order::release);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** A Lockable interface to a spinlock implemented on top of an atomic.
|
||||||
|
|
||||||
|
@tparam T An unsigned integral type.
|
||||||
|
|
||||||
|
@note Using `PackedSpinlock` and `Spinlock` against the same underlying
|
||||||
|
atomic integer is possible but can result in `Spinlock` not being
|
||||||
|
able to acquire the lock during periods of high contention due to
|
||||||
|
the way the two locks operate: `Spinlock` spins and tries to grab
|
||||||
|
all the bits at once, whereas any given `PackedSpinlock` instance
|
||||||
|
only tries to grab one bit at a time. Caveat emptor.
|
||||||
|
|
||||||
|
This class meets the requirements of Lockable:
|
||||||
|
https://en.cppreference.com/w/cpp/named_req/Lockable
|
||||||
|
*/
|
||||||
|
template <SpinlockValueType T>
|
||||||
|
class Spinlock
|
||||||
|
{
|
||||||
|
std::atomic<T>& lock_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Spinlock(Spinlock const&) = delete;
|
||||||
|
Spinlock&
|
||||||
|
operator=(Spinlock const&) = delete;
|
||||||
|
|
||||||
|
/** Construct a spinlock handle.
|
||||||
|
|
||||||
|
@param lock The atomic integer to spin against.
|
||||||
|
|
||||||
|
@note For performance reasons, you should strive to have `lock` be
|
||||||
|
on a cacheline by itself.
|
||||||
|
*/
|
||||||
|
explicit Spinlock(std::atomic<T>& lock) noexcept : lock_(lock)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool
|
||||||
|
try_lock() noexcept // NOLINT(readability-identifier-naming)
|
||||||
|
{
|
||||||
|
return spinTryLock(lock_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
lock() noexcept
|
||||||
|
{
|
||||||
|
spinLock(lock_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
unlock() noexcept
|
||||||
|
{
|
||||||
|
spinUnlock(lock_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** A Lockable interface to a packed spinlock implemented on top of an atomic.
|
||||||
|
|
||||||
|
Packed spinlocks offer tremendous space-efficient lock-sharding but
|
||||||
|
they come at a cost.
|
||||||
|
|
||||||
|
First, the implementation is necessarily low-level and uses advanced
|
||||||
|
features like memory ordering and highly platform-specific tricks to
|
||||||
|
maximize performance. This imposes a significant and ongoing cost to
|
||||||
|
developers.
|
||||||
|
|
||||||
|
Second, and perhaps most important, is that the packing of multiple
|
||||||
|
locks into a single integer which, albeit space-efficient, also has
|
||||||
|
performance implications stemming from data dependencies, increased
|
||||||
|
cache-coherency traffic between processors and heavier loads on the
|
||||||
|
processor's load/store units.
|
||||||
|
|
||||||
|
To be sure, these locks can have advantages but they are definitely
|
||||||
|
not general purpose locks and should not be thought of or used that
|
||||||
|
way. The use cases for them are likely few and far between; without
|
||||||
|
a compelling reason to use them, backed by profiling data, it might
|
||||||
|
be best to use one of the standard locking primitives instead. Note
|
||||||
|
that in most common platforms, `std::mutex` is so heavily optimized
|
||||||
|
that it can, usually, outperform spinlocks.
|
||||||
|
|
||||||
|
@tparam T An unsigned integral type (e.g. std::uint16_t)
|
||||||
|
|
||||||
|
This class meets the requirements of Lockable:
|
||||||
|
https://en.cppreference.com/w/cpp/named_req/Lockable
|
||||||
|
*/
|
||||||
|
template <PackedSpinlockValueType T>
|
||||||
class PackedSpinlock
|
class PackedSpinlock
|
||||||
{
|
{
|
||||||
// clang-format off
|
|
||||||
static_assert(std::is_unsigned_v<T>);
|
|
||||||
static_assert(std::atomic<T>::is_always_lock_free);
|
|
||||||
static_assert(
|
|
||||||
std::is_same_v<decltype(std::declval<std::atomic<T>&>().fetch_or(0)), T> &&
|
|
||||||
std::is_same_v<decltype(std::declval<std::atomic<T>&>().fetch_and(0)), T>,
|
|
||||||
"std::atomic<T>::fetch_and(T) and std::atomic<T>::fetch_and(T) are required by packed_spinlock");
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::atomic<T>& bits_;
|
std::atomic<T>& bits_;
|
||||||
T const mask_;
|
T const mask_;
|
||||||
|
|
||||||
@@ -94,120 +221,49 @@ public:
|
|||||||
PackedSpinlock&
|
PackedSpinlock&
|
||||||
operator=(PackedSpinlock const&) = delete;
|
operator=(PackedSpinlock const&) = delete;
|
||||||
|
|
||||||
/**
|
/** Construct a packed spinlock handle for a single bit.
|
||||||
* A single spinlock packed inside the specified atomic
|
|
||||||
*
|
@param lock The atomic integer inside which the spinlock is packed.
|
||||||
* @param lock The atomic integer inside which the spinlock is packed.
|
@param index The index of the spinlock this object acquires.
|
||||||
* @param index The index of the spinlock this object acquires.
|
|
||||||
*
|
@note For performance reasons, you should strive to have `lock` be
|
||||||
* @note For performance reasons, you should strive to have `lock` be
|
on a cacheline by itself.
|
||||||
* on a cacheline by itself.
|
|
||||||
*/
|
*/
|
||||||
PackedSpinlock(std::atomic<T>& lock, int index) : bits_(lock), mask_(static_cast<T>(1) << index)
|
PackedSpinlock(std::atomic<T>& lock, int index) noexcept
|
||||||
{
|
: bits_(lock), mask_([index]() {
|
||||||
XRPL_ASSERT(
|
XRPL_ASSERT(
|
||||||
index >= 0 && (mask_ != 0),
|
index >= 0 && index < std::numeric_limits<T>::digits,
|
||||||
"xrpl::PackedSpinlock::PackedSpinlock : valid index and mask");
|
"xrpl::PackedSpinlock::PackedSpinlock : valid index");
|
||||||
}
|
return static_cast<T>(T{1} << index);
|
||||||
|
}())
|
||||||
[[nodiscard]] bool
|
|
||||||
try_lock() // NOLINT(readability-identifier-naming)
|
|
||||||
{
|
|
||||||
return (bits_.fetch_or(mask_, std::memory_order_acquire) & mask_) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
lock()
|
|
||||||
{
|
|
||||||
while (!try_lock())
|
|
||||||
{
|
|
||||||
// The use of relaxed memory ordering here is intentional and
|
|
||||||
// serves to help reduce cache coherency traffic during times
|
|
||||||
// of contention by avoiding writes that would definitely not
|
|
||||||
// result in the lock being acquired.
|
|
||||||
while ((bits_.load(std::memory_order_relaxed) & mask_) != 0)
|
|
||||||
detail::spinPause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
unlock()
|
|
||||||
{
|
|
||||||
bits_.fetch_and(~mask_, std::memory_order_release);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A spinlock implemented on top of an atomic integer.
|
|
||||||
*
|
|
||||||
* @note Using `packed_spinlock` and `spinlock` against the same underlying
|
|
||||||
* atomic integer can result in `spinlock` not being able to actually
|
|
||||||
* acquire the lock during periods of high contention, because of how
|
|
||||||
* the two locks operate: `spinlock` will spin trying to grab all the
|
|
||||||
* bits at once, whereas any given `packed_spinlock` will only try to
|
|
||||||
* grab one bit at a time. Caveat emptor.
|
|
||||||
*
|
|
||||||
* This class meets the requirements of Lockable:
|
|
||||||
* https://en.cppreference.com/w/cpp/named_req/Lockable
|
|
||||||
*/
|
|
||||||
template <class T>
|
|
||||||
class Spinlock
|
|
||||||
{
|
|
||||||
static_assert(std::is_unsigned_v<T>);
|
|
||||||
static_assert(std::atomic<T>::is_always_lock_free);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::atomic<T>& lock_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Spinlock(Spinlock const&) = delete;
|
|
||||||
Spinlock&
|
|
||||||
operator=(Spinlock const&) = delete;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Grabs the
|
|
||||||
*
|
|
||||||
* @param lock The atomic integer to spin against.
|
|
||||||
*
|
|
||||||
* @note For performance reasons, you should strive to have `lock` be
|
|
||||||
* on a cacheline by itself.
|
|
||||||
*/
|
|
||||||
Spinlock(std::atomic<T>& lock) : lock_(lock)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
try_lock() // NOLINT(readability-identifier-naming)
|
try_lock() noexcept // NOLINT(readability-identifier-naming)
|
||||||
{
|
{
|
||||||
T expected = 0;
|
return (bits_.fetch_or(mask_, std::memory_order::acquire) & mask_) == 0;
|
||||||
|
|
||||||
return lock_.compare_exchange_weak(
|
|
||||||
expected,
|
|
||||||
std::numeric_limits<T>::max(),
|
|
||||||
std::memory_order_acquire,
|
|
||||||
std::memory_order_relaxed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
lock()
|
lock() noexcept
|
||||||
{
|
{
|
||||||
while (!try_lock())
|
do
|
||||||
{
|
{
|
||||||
// The use of relaxed memory ordering here is intentional and
|
// The use of relaxed memory ordering here is intentional and
|
||||||
// serves to help reduce cache coherency traffic during times
|
// serves to help reduce cache coherency traffic during times
|
||||||
// of contention by avoiding writes that would definitely not
|
// of contention by avoiding writes that are unlikely to grab
|
||||||
// result in the lock being acquired.
|
// the requested lock.
|
||||||
while (lock_.load(std::memory_order_relaxed) != 0)
|
while ((bits_.load(std::memory_order::relaxed) & mask_) != 0)
|
||||||
detail::spinPause();
|
detail::spinPause();
|
||||||
}
|
} while (!try_lock());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
unlock()
|
unlock() noexcept
|
||||||
{
|
{
|
||||||
lock_.store(0, std::memory_order_release);
|
bits_.fetch_and(~mask_, std::memory_order::release);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/** @} */
|
|
||||||
|
|
||||||
} // namespace xrpl
|
} // namespace xrpl
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <string>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <typeinfo>
|
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
|
||||||
#include <cxxabi.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
std::string
|
|
||||||
typeName()
|
|
||||||
{
|
|
||||||
using TR = std::remove_reference_t<T>;
|
|
||||||
|
|
||||||
std::string name = typeid(TR).name();
|
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
|
||||||
if (auto s = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, nullptr))
|
|
||||||
{
|
|
||||||
name = s;
|
|
||||||
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
|
|
||||||
std::free(s);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (std::is_const_v<TR>)
|
|
||||||
name += " const";
|
|
||||||
if (std::is_volatile_v<TR>)
|
|
||||||
name += " volatile";
|
|
||||||
if (std::is_lvalue_reference_v<T>)
|
|
||||||
{
|
|
||||||
name += "&";
|
|
||||||
}
|
|
||||||
else if (std::is_rvalue_reference_v<T>)
|
|
||||||
{
|
|
||||||
name += "&&";
|
|
||||||
}
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace beast
|
|
||||||
@@ -2,26 +2,24 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <compare>
|
||||||
|
#include <concepts>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zero allows classes to offer efficient comparisons to zero.
|
* Zero allows classes to offer efficient comparisons to zero.
|
||||||
*
|
*
|
||||||
* Zero is a struct to allow classes to efficiently compare with zero without
|
|
||||||
* requiring an rvalue construction.
|
|
||||||
*
|
|
||||||
* It's often the case that we have classes which combine a number and a unit.
|
* It's often the case that we have classes which combine a number and a unit.
|
||||||
* In such cases, comparisons like t > 0 or t != 0 make sense, but comparisons
|
* In such cases, comparisons like t > 0 or t != 0 make sense, but comparisons
|
||||||
* like t > 1 or t != 1 do not.
|
* like t > 1 or t != 1 do not. Comparing against kZero expresses exactly that,
|
||||||
|
* without constructing a T.
|
||||||
*
|
*
|
||||||
* The class Zero allows such comparisons to be easily made.
|
* A type T participates if either `t.signum()` or an unqualified `signum(t)`
|
||||||
*
|
* found by argument-dependent lookup returns an integer that is negative,
|
||||||
* The comparing class T either needs to have a method called signum() which
|
* zero, or positive according to the sign of t. Both `t == kZero` and
|
||||||
* returns a positive number, 0, or a negative; or there needs to be a signum
|
* `kZero == t` work, as do all six relational operators in either order.
|
||||||
* function which resolves in the namespace which takes an instance of T and
|
|
||||||
* returns a positive, zero or negative number.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct Zero
|
struct Zero
|
||||||
{
|
{
|
||||||
explicit Zero() = default;
|
explicit Zero() = default;
|
||||||
@@ -30,115 +28,44 @@ struct Zero
|
|||||||
inline constexpr Zero kZero{};
|
inline constexpr Zero kZero{};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default implementation of signum calls the method on the class.
|
* Default implementation of signum: call the member function.
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <class T>
|
||||||
auto
|
requires requires(T const& t) {
|
||||||
signum(T const& t)
|
{ t.signum() } -> std::integral;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr auto
|
||||||
|
signum(T const& t) noexcept(noexcept(t.signum()))
|
||||||
{
|
{
|
||||||
return t.signum();
|
return t.signum();
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace detail::zero_helper {
|
namespace detail {
|
||||||
|
|
||||||
// For argument dependent lookup to function properly, calls to signum must
|
/**
|
||||||
// be made from a namespace that does not include overloads of the function..
|
* A type with a usable signum: either the member-based default above, or a
|
||||||
|
* `signum(t)` overload in T's own namespace, found by ADL. A user overload
|
||||||
|
* that is a better match than the template wins, as usual.
|
||||||
|
*/
|
||||||
template <class T>
|
template <class T>
|
||||||
auto
|
concept HasSignum = requires(T const& t) {
|
||||||
callSignum(T const& t)
|
{ signum(t) } -> std::integral;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <detail::HasSignum T>
|
||||||
|
[[nodiscard]] constexpr bool
|
||||||
|
operator==(T const& t, Zero) noexcept(noexcept(signum(t)))
|
||||||
{
|
{
|
||||||
return signum(t);
|
return signum(t) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace detail::zero_helper
|
template <detail::HasSignum T>
|
||||||
|
[[nodiscard]] constexpr std::strong_ordering
|
||||||
// Handle operators where T is on the left side using signum.
|
operator<=>(T const& t, Zero) noexcept(noexcept(signum(t)))
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool
|
|
||||||
operator==(T const& t, Zero)
|
|
||||||
{
|
{
|
||||||
return detail::zero_helper::callSignum(t) == 0;
|
return signum(t) <=> 0;
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool
|
|
||||||
operator!=(T const& t, Zero)
|
|
||||||
{
|
|
||||||
return detail::zero_helper::callSignum(t) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool
|
|
||||||
operator<(T const& t, Zero)
|
|
||||||
{
|
|
||||||
return detail::zero_helper::callSignum(t) < 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool
|
|
||||||
operator>(T const& t, Zero)
|
|
||||||
{
|
|
||||||
return detail::zero_helper::callSignum(t) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool
|
|
||||||
operator>=(T const& t, Zero)
|
|
||||||
{
|
|
||||||
return detail::zero_helper::callSignum(t) >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool
|
|
||||||
operator<=(T const& t, Zero)
|
|
||||||
{
|
|
||||||
return detail::zero_helper::callSignum(t) <= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle operators where T is on the right side by
|
|
||||||
// reversing the operation, so that T is on the left side.
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool
|
|
||||||
operator==(Zero, T const& t)
|
|
||||||
{
|
|
||||||
return t == kZero;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool
|
|
||||||
operator!=(Zero, T const& t)
|
|
||||||
{
|
|
||||||
return t != kZero;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool
|
|
||||||
operator<(Zero, T const& t)
|
|
||||||
{
|
|
||||||
return t > kZero;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool
|
|
||||||
operator>(Zero, T const& t)
|
|
||||||
{
|
|
||||||
return t < kZero;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool
|
|
||||||
operator>=(Zero, T const& t)
|
|
||||||
{
|
|
||||||
return t <= kZero;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool
|
|
||||||
operator<=(Zero, T const& t)
|
|
||||||
{
|
|
||||||
return t >= kZero;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace beast
|
} // namespace beast
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <xrpl/basics/base_uint.h>
|
#include <xrpl/basics/base_uint.h>
|
||||||
#include <xrpl/basics/safe_cast.h>
|
#include <xrpl/basics/enum_bitops.h>
|
||||||
#include <xrpl/beast/utility/instrumentation.h>
|
#include <xrpl/beast/utility/instrumentation.h>
|
||||||
#include <xrpl/ledger/OwnerCounts.h>
|
#include <xrpl/ledger/OwnerCounts.h>
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
@@ -22,80 +22,39 @@
|
|||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
// Bitwise flag enum with existing operator overloads
|
enum class ApplyFlags : std::uint32_t {
|
||||||
// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class)
|
None = 0x00,
|
||||||
enum ApplyFlags : std::uint32_t {
|
|
||||||
TapNone = 0x00,
|
|
||||||
|
|
||||||
// This is a local transaction with the
|
// This is a local transaction with the
|
||||||
// fail_hard flag set.
|
// fail_hard flag set.
|
||||||
TapFailHard = 0x10,
|
FailHard = 0x10,
|
||||||
|
|
||||||
// This is not the transaction's last pass
|
// This is not the transaction's last pass
|
||||||
// Transaction can be retried, soft failures allowed
|
// Transaction can be retried, soft failures allowed
|
||||||
TapRetry = 0x20,
|
Retry = 0x20,
|
||||||
|
|
||||||
// Transaction came from a privileged source
|
// Transaction came from a privileged source
|
||||||
TapUnlimited = 0x400,
|
Unlimited = 0x400,
|
||||||
|
|
||||||
// Transaction is executing as part of a batch
|
// Transaction is executing as part of a batch
|
||||||
TapBatch = 0x800,
|
Batch = 0x800,
|
||||||
|
|
||||||
// Transaction shouldn't be applied
|
// Transaction shouldn't be applied
|
||||||
// Signatures shouldn't be checked
|
// Signatures shouldn't be checked
|
||||||
TapDryRun = 0x1000,
|
DryRun = 0x1000,
|
||||||
|
|
||||||
// Transaction is being preflighted as the payload of a
|
// Transaction is being preflighted as the payload of a
|
||||||
// TransactionProposalCreate. Its signatures are collected on-ledger
|
// TransactionProposalCreate. Its signatures are collected on-ledger
|
||||||
// afterward, so signature-presence checks (e.g. Batch signer matching)
|
// afterward, so signature-presence checks (e.g. Batch signer matching)
|
||||||
// are skipped at proposal-creation time (On-Chain Cosigner spec
|
// are skipped at proposal-creation time (On-Chain Cosigner spec
|
||||||
// §5.3.1.2).
|
// §5.3.1.2).
|
||||||
TapProposal = 0x2000
|
Proposal = 0x2000
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr ApplyFlags
|
template <>
|
||||||
operator|(ApplyFlags const& lhs, ApplyFlags const& rhs)
|
struct enum_bitops::OptIn<ApplyFlags> : std::true_type
|
||||||
{
|
{
|
||||||
return safeCast<ApplyFlags>(
|
};
|
||||||
safeCast<std::underlying_type_t<ApplyFlags>>(lhs) |
|
|
||||||
safeCast<std::underlying_type_t<ApplyFlags>>(rhs));
|
|
||||||
}
|
|
||||||
|
|
||||||
static_assert((TapFailHard | TapRetry) == safeCast<ApplyFlags>(0x30u), "ApplyFlags operator |");
|
|
||||||
static_assert((TapRetry | TapFailHard) == safeCast<ApplyFlags>(0x30u), "ApplyFlags operator |");
|
|
||||||
|
|
||||||
constexpr ApplyFlags
|
|
||||||
operator&(ApplyFlags const& lhs, ApplyFlags const& rhs)
|
|
||||||
{
|
|
||||||
return safeCast<ApplyFlags>(
|
|
||||||
safeCast<std::underlying_type_t<ApplyFlags>>(lhs) &
|
|
||||||
safeCast<std::underlying_type_t<ApplyFlags>>(rhs));
|
|
||||||
}
|
|
||||||
|
|
||||||
static_assert((TapFailHard & TapRetry) == TapNone, "ApplyFlags operator &");
|
|
||||||
static_assert((TapRetry & TapFailHard) == TapNone, "ApplyFlags operator &");
|
|
||||||
|
|
||||||
constexpr ApplyFlags
|
|
||||||
operator~(ApplyFlags const& flags)
|
|
||||||
{
|
|
||||||
return safeCast<ApplyFlags>(~safeCast<std::underlying_type_t<ApplyFlags>>(flags));
|
|
||||||
}
|
|
||||||
|
|
||||||
static_assert(~TapRetry == safeCast<ApplyFlags>(0xFFFFFFDFu), "ApplyFlags operator ~");
|
|
||||||
|
|
||||||
inline ApplyFlags
|
|
||||||
operator|=(ApplyFlags& lhs, ApplyFlags const& rhs)
|
|
||||||
{
|
|
||||||
lhs = lhs | rhs;
|
|
||||||
return lhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline ApplyFlags
|
|
||||||
operator&=(ApplyFlags& lhs, ApplyFlags const& rhs)
|
|
||||||
{
|
|
||||||
lhs = lhs & rhs;
|
|
||||||
return lhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <xrpl/basics/base_uint.h>
|
||||||
#include <xrpl/beast/utility/Journal.h>
|
#include <xrpl/beast/utility/Journal.h>
|
||||||
#include <xrpl/ledger/ApplyView.h>
|
#include <xrpl/ledger/ApplyView.h>
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
#include <xrpl/ledger/entries/SLEBase.h>
|
#include <xrpl/ledger/entries/SLEBase.h>
|
||||||
#include <xrpl/protocol/Indexes.h>
|
#include <xrpl/protocol/Indexes.h>
|
||||||
#include <xrpl/protocol/LedgerFormats.h>
|
#include <xrpl/protocol/LedgerFormats.h>
|
||||||
|
#include <xrpl/protocol/SField.h>
|
||||||
|
#include <xrpl/protocol/STVector256.h>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
@@ -25,6 +31,31 @@ public:
|
|||||||
: Base(keylet::skip(), view, j)
|
: Base(keylet::skip(), view, j)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks up a hash `diff` slots back from the most recent entry in the
|
||||||
|
* sfHashes vector (diff == 0 is the most recent entry).
|
||||||
|
*
|
||||||
|
* Callers decide which skip list entry to read and how to translate a
|
||||||
|
* target ledger sequence into `diff`; this only does the bounds check
|
||||||
|
* and vector indexing shared by both the recent (stride 1) and distant
|
||||||
|
* (stride 256) skip lists.
|
||||||
|
*
|
||||||
|
* @param diff how many slots back from the most recent hash to look up;
|
||||||
|
* 0 is the most recent hash.
|
||||||
|
* @return the hash at that slot, or std::nullopt if the entry does not
|
||||||
|
* exist or `diff` is out of range.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] std::optional<UInt256>
|
||||||
|
hashAt(std::size_t diff) const
|
||||||
|
{
|
||||||
|
if (!this->exists())
|
||||||
|
return std::nullopt;
|
||||||
|
STVector256 const vec = (*this)->getFieldV256(sfHashes);
|
||||||
|
if (vec.size() > diff)
|
||||||
|
return vec[vec.size() - diff - 1];
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
using LedgerHashesEntryR = LedgerHashesEntry<ReadView>;
|
using LedgerHashesEntryR = LedgerHashesEntry<ReadView>;
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <xrpl/basics/base_uint.h>
|
||||||
#include <xrpl/nodestore/NodeObject.h>
|
#include <xrpl/nodestore/NodeObject.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace xrpl::node_store {
|
namespace xrpl::node_store {
|
||||||
|
|
||||||
@@ -23,7 +25,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Construct the decoded blob from raw data.
|
* Construct the decoded blob from raw data.
|
||||||
*/
|
*/
|
||||||
DecodedBlob(void const* key, void const* value, int valueBytes);
|
DecodedBlob(std::optional<uint256> key, void const* value, int valueBytes);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the decoding was successful.
|
* Determine if the decoding was successful.
|
||||||
@@ -43,10 +45,10 @@ public:
|
|||||||
private:
|
private:
|
||||||
bool success_{false};
|
bool success_{false};
|
||||||
|
|
||||||
void const* key_;
|
uint256 key_;
|
||||||
NodeObjectType objectType_{NodeObjectType::Unknown};
|
NodeObjectType objectType_{NodeObjectType::Unknown};
|
||||||
unsigned char const* objectData_{nullptr};
|
unsigned char const* objectData_{nullptr};
|
||||||
int dataBytes_;
|
int dataBytes_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace xrpl::node_store
|
} // namespace xrpl::node_store
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <xrpl/basics/base_uint.h>
|
||||||
#include <xrpl/beast/utility/instrumentation.h>
|
#include <xrpl/beast/utility/instrumentation.h>
|
||||||
#include <xrpl/nodestore/NodeObject.h>
|
#include <xrpl/nodestore/NodeObject.h>
|
||||||
|
|
||||||
@@ -37,11 +38,6 @@ namespace xrpl::node_store {
|
|||||||
|
|
||||||
class EncodedBlob
|
class EncodedBlob
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* The 32-byte key of the serialized object.
|
|
||||||
*/
|
|
||||||
std::array<std::uint8_t, 32> key_{};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A pre-allocated buffer for the serialized object.
|
* A pre-allocated buffer for the serialized object.
|
||||||
*
|
*
|
||||||
@@ -65,6 +61,11 @@ class EncodedBlob
|
|||||||
*/
|
*/
|
||||||
std::uint8_t* const ptr_;
|
std::uint8_t* const ptr_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The 32-byte key of the serialized object.
|
||||||
|
*/
|
||||||
|
uint256 key_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit EncodedBlob(std::shared_ptr<NodeObject> const& obj)
|
explicit EncodedBlob(std::shared_ptr<NodeObject> const& obj)
|
||||||
: size_([&obj]() {
|
: size_([&obj]() {
|
||||||
@@ -76,11 +77,12 @@ public:
|
|||||||
return obj->getData().size() + 9;
|
return obj->getData().size() + 9;
|
||||||
}())
|
}())
|
||||||
, ptr_((size_ <= payload_.size()) ? payload_.data() : new std::uint8_t[size_])
|
, ptr_((size_ <= payload_.size()) ? payload_.data() : new std::uint8_t[size_])
|
||||||
|
, key_(obj->getHash())
|
||||||
{
|
{
|
||||||
std::fill_n(ptr_, 8, std::uint8_t{0});
|
std::fill_n(ptr_, 8, std::uint8_t{0});
|
||||||
ptr_[8] = static_cast<std::uint8_t>(obj->getType());
|
ptr_[8] = static_cast<std::uint8_t>(obj->getType());
|
||||||
|
|
||||||
std::copy_n(obj->getData().data(), obj->getData().size(), ptr_ + 9);
|
std::copy_n(obj->getData().data(), obj->getData().size(), ptr_ + 9);
|
||||||
std::copy_n(obj->getHash().data(), obj->getHash().size(), key_.data());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~EncodedBlob()
|
~EncodedBlob()
|
||||||
@@ -95,10 +97,10 @@ public:
|
|||||||
delete[] ptr_;
|
delete[] ptr_;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] void const*
|
[[nodiscard]] uint256 const&
|
||||||
getKey() const noexcept
|
getKey() const noexcept
|
||||||
{
|
{
|
||||||
return static_cast<void const*>(key_.data());
|
return key_;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::size_t
|
[[nodiscard]] std::size_t
|
||||||
|
|||||||
@@ -61,14 +61,22 @@ parseBase58(std::string const& s);
|
|||||||
/**
|
/**
|
||||||
* A special account that's used as the "issuer" for XRP.
|
* A special account that's used as the "issuer" for XRP.
|
||||||
*/
|
*/
|
||||||
AccountID const&
|
constexpr inline AccountID const&
|
||||||
xrpAccount();
|
xrpAccount() noexcept
|
||||||
|
{
|
||||||
|
static constexpr AccountID kAccount(beast::kZero);
|
||||||
|
return kAccount;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A placeholder for empty accounts.
|
* A placeholder for empty accounts.
|
||||||
*/
|
*/
|
||||||
AccountID const&
|
constexpr inline AccountID const&
|
||||||
noAccount();
|
noAccount() noexcept
|
||||||
|
{
|
||||||
|
static constexpr AccountID kAccount = xrpAccount().next();
|
||||||
|
return kAccount;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert hex or base58 string to AccountID.
|
* Convert hex or base58 string to AccountID.
|
||||||
@@ -80,10 +88,10 @@ bool
|
|||||||
toIssuer(AccountID&, std::string const&);
|
toIssuer(AccountID&, std::string const&);
|
||||||
|
|
||||||
// DEPRECATED Should be checking the currency or native flag
|
// DEPRECATED Should be checking the currency or native flag
|
||||||
inline bool
|
constexpr inline bool
|
||||||
isXRP(AccountID const& c)
|
isXRP(AccountID const& c) noexcept
|
||||||
{
|
{
|
||||||
return c == beast::kZero;
|
return c == xrpAccount();
|
||||||
}
|
}
|
||||||
|
|
||||||
// DEPRECATED
|
// DEPRECATED
|
||||||
@@ -101,22 +109,6 @@ operator<<(std::ostream& os, AccountID const& x)
|
|||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the global cache used to map AccountID to base58 conversions.
|
|
||||||
*
|
|
||||||
* The cache is optional and need not be initialized. But because conversion
|
|
||||||
* is expensive (it requires a SHA-256 operation) in most cases the overhead
|
|
||||||
* of the cache is worth the benefit.
|
|
||||||
*
|
|
||||||
* @param count The number of entries the cache should accommodate. Zero will
|
|
||||||
* disable the cache, releasing any memory associated with it.
|
|
||||||
*
|
|
||||||
* @note The function will only initialize the cache the first time it is
|
|
||||||
* invoked. Subsequent invocations do nothing.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
initAccountIdCache(std::size_t count);
|
|
||||||
|
|
||||||
} // namespace xrpl
|
} // namespace xrpl
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <xrpl/basics/contract.h>
|
#include <xrpl/basics/contract.h>
|
||||||
#include <xrpl/beast/type_name.h>
|
|
||||||
#include <xrpl/protocol/SOTemplate.h>
|
#include <xrpl/protocol/SOTemplate.h>
|
||||||
|
|
||||||
#include <boost/container/flat_map.hpp>
|
#include <boost/container/flat_map.hpp>
|
||||||
|
#include <boost/core/type_name.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
@@ -84,7 +84,7 @@ public:
|
|||||||
* Derived classes will load the object with all the known formats.
|
* Derived classes will load the object with all the known formats.
|
||||||
*/
|
*/
|
||||||
private:
|
private:
|
||||||
KnownFormats() : name_(beast::typeName<Derived>())
|
KnownFormats() : name_(boost::core::type_name<Derived>())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,20 @@ public:
|
|||||||
|
|
||||||
STBitString(SField const& n);
|
STBitString(SField const& n);
|
||||||
STBitString(value_type const& v);
|
STBitString(value_type const& v);
|
||||||
|
|
||||||
|
template <typename Tag>
|
||||||
|
requires(!std::is_void_v<Tag>)
|
||||||
|
STBitString(BaseUInt<Bits, Tag> const& v) : value_(v)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
STBitString(SField const& n, value_type const& v);
|
STBitString(SField const& n, value_type const& v);
|
||||||
|
template <typename Tag>
|
||||||
|
requires(!std::is_void_v<Tag>)
|
||||||
|
STBitString(SField const& n, BaseUInt<Bits, Tag> const& v) : STBase(n), value_(v)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
STBitString(SerialIter& sit, SField const& name);
|
STBitString(SerialIter& sit, SField const& name);
|
||||||
|
|
||||||
[[nodiscard]] SerializedTypeID
|
[[nodiscard]] SerializedTypeID
|
||||||
@@ -166,7 +179,7 @@ template <typename Tag>
|
|||||||
void
|
void
|
||||||
STBitString<Bits>::setValue(BaseUInt<Bits, Tag> const& v)
|
STBitString<Bits>::setValue(BaseUInt<Bits, Tag> const& v)
|
||||||
{
|
{
|
||||||
value_ = v;
|
value_ = value_type{v};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int Bits>
|
template <int Bits>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <xrpl/basics/CountedObject.h>
|
#include <xrpl/basics/CountedObject.h>
|
||||||
#include <xrpl/basics/UnorderedContainers.h>
|
#include <xrpl/basics/UnorderedContainers.h>
|
||||||
|
#include <xrpl/basics/enum_bitops.h>
|
||||||
#include <xrpl/beast/utility/instrumentation.h>
|
#include <xrpl/beast/utility/instrumentation.h>
|
||||||
#include <xrpl/json/json_value.h>
|
#include <xrpl/json/json_value.h>
|
||||||
#include <xrpl/protocol/AccountID.h>
|
#include <xrpl/protocol/AccountID.h>
|
||||||
@@ -15,6 +16,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -22,18 +24,8 @@ namespace xrpl {
|
|||||||
|
|
||||||
class STPathElement final : public CountedObject<STPathElement>
|
class STPathElement final : public CountedObject<STPathElement>
|
||||||
{
|
{
|
||||||
unsigned int type_;
|
|
||||||
AccountID accountID_;
|
|
||||||
PathAsset assetID_;
|
|
||||||
AccountID issuerID_;
|
|
||||||
|
|
||||||
bool isOffer_;
|
|
||||||
std::size_t hashValue_;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Bitwise values (typeCurrency | typeMPT)
|
enum class Type : std::uint8_t {
|
||||||
// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class)
|
|
||||||
enum Type {
|
|
||||||
TypeNone = 0x00,
|
TypeNone = 0x00,
|
||||||
TypeAccount = 0x01, // Rippling through an account (vs taking an offer).
|
TypeAccount = 0x01, // Rippling through an account (vs taking an offer).
|
||||||
TypeCurrency = 0x10, // Currency follows.
|
TypeCurrency = 0x10, // Currency follows.
|
||||||
@@ -45,6 +37,8 @@ public:
|
|||||||
// Combination of all types.
|
// Combination of all types.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using enum Type;
|
||||||
|
|
||||||
STPathElement();
|
STPathElement();
|
||||||
STPathElement(STPathElement const&) = default;
|
STPathElement(STPathElement const&) = default;
|
||||||
STPathElement&
|
STPathElement&
|
||||||
@@ -62,42 +56,42 @@ public:
|
|||||||
bool forceAsset = false);
|
bool forceAsset = false);
|
||||||
|
|
||||||
STPathElement(
|
STPathElement(
|
||||||
unsigned int uType,
|
Type type,
|
||||||
AccountID const& account,
|
AccountID const& account,
|
||||||
PathAsset const& asset,
|
PathAsset const& asset,
|
||||||
AccountID const& issuer);
|
AccountID const& issuer);
|
||||||
|
|
||||||
[[nodiscard]] std::uint32_t
|
[[nodiscard]] Type
|
||||||
getNodeType() const;
|
getNodeType() const noexcept;
|
||||||
|
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
isOffer() const;
|
isOffer() const noexcept;
|
||||||
|
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
isAccount() const;
|
isAccount() const noexcept;
|
||||||
|
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
hasIssuer() const;
|
hasIssuer() const noexcept;
|
||||||
|
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
hasCurrency() const;
|
hasCurrency() const noexcept;
|
||||||
|
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
hasMPT() const;
|
hasMPT() const noexcept;
|
||||||
|
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
hasAsset() const;
|
hasAsset() const noexcept;
|
||||||
|
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
isNone() const;
|
isNone() const noexcept;
|
||||||
|
|
||||||
// Nodes are either an account ID or a offer prefix. Offer prefixs denote a
|
// Nodes are either an account ID or a offer prefix. Offer prefixs denote a
|
||||||
// class of offers.
|
// class of offers.
|
||||||
[[nodiscard]] AccountID const&
|
[[nodiscard]] AccountID const&
|
||||||
getAccountID() const;
|
getAccountID() const noexcept;
|
||||||
|
|
||||||
[[nodiscard]] PathAsset const&
|
[[nodiscard]] PathAsset const&
|
||||||
getPathAsset() const;
|
getPathAsset() const noexcept;
|
||||||
|
|
||||||
[[nodiscard]] Currency const&
|
[[nodiscard]] Currency const&
|
||||||
getCurrency() const;
|
getCurrency() const;
|
||||||
@@ -106,17 +100,35 @@ public:
|
|||||||
getMPTID() const;
|
getMPTID() const;
|
||||||
|
|
||||||
[[nodiscard]] AccountID const&
|
[[nodiscard]] AccountID const&
|
||||||
getIssuerID() const;
|
getIssuerID() const noexcept;
|
||||||
|
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
isType(Type const& pe) const;
|
isType(Type pe) const noexcept;
|
||||||
|
|
||||||
bool
|
friend bool
|
||||||
operator==(STPathElement const& t) const;
|
operator==(STPathElement const& lhs, STPathElement const& rhs) noexcept
|
||||||
|
{
|
||||||
|
return lhs.isType(TypeAccount) == rhs.isType(TypeAccount) &&
|
||||||
|
lhs.hashValue_ == rhs.hashValue_ && lhs.accountID_ == rhs.accountID_ &&
|
||||||
|
lhs.assetID_ == rhs.assetID_ && lhs.issuerID_ == rhs.issuerID_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::size_t
|
static std::size_t
|
||||||
getHash(STPathElement const& element);
|
getHash(STPathElement const& element);
|
||||||
|
|
||||||
|
Type type_;
|
||||||
|
AccountID accountID_;
|
||||||
|
PathAsset assetID_;
|
||||||
|
AccountID issuerID_;
|
||||||
|
|
||||||
|
bool isOffer_;
|
||||||
|
std::size_t hashValue_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct enum_bitops::OptIn<STPathElement::Type> : std::true_type
|
||||||
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class Hasher>
|
template <class Hasher>
|
||||||
@@ -124,7 +136,7 @@ void
|
|||||||
hash_append(Hasher& h, STPathElement const& e) noexcept
|
hash_append(Hasher& h, STPathElement const& e) noexcept
|
||||||
{
|
{
|
||||||
using beast::hash_append;
|
using beast::hash_append;
|
||||||
hash_append(h, (e.getNodeType() & STPathElement::TypeAccount) != 0u);
|
hash_append(h, e.isType(STPathElement::TypeAccount));
|
||||||
hash_append(h, e.getAccountID());
|
hash_append(h, e.getAccountID());
|
||||||
hash_append(h, e.getPathAsset());
|
hash_append(h, e.getPathAsset());
|
||||||
hash_append(h, e.getIssuerID());
|
hash_append(h, e.getIssuerID());
|
||||||
@@ -404,11 +416,11 @@ inline STPathElement::STPathElement(
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline STPathElement::STPathElement(
|
inline STPathElement::STPathElement(
|
||||||
unsigned int uType,
|
Type type,
|
||||||
AccountID const& account,
|
AccountID const& account,
|
||||||
PathAsset const& asset,
|
PathAsset const& asset,
|
||||||
AccountID const& issuer)
|
AccountID const& issuer)
|
||||||
: type_(uType)
|
: type_(type)
|
||||||
, accountID_(account)
|
, accountID_(account)
|
||||||
, assetID_(asset)
|
, assetID_(asset)
|
||||||
, issuerID_(issuer)
|
, issuerID_(issuer)
|
||||||
@@ -423,56 +435,56 @@ inline STPathElement::STPathElement(
|
|||||||
hashValue_ = getHash(*this);
|
hashValue_ = getHash(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::uint32_t
|
inline STPathElement::Type
|
||||||
STPathElement::getNodeType() const
|
STPathElement::getNodeType() const noexcept
|
||||||
{
|
{
|
||||||
return type_;
|
return type_;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
STPathElement::isOffer() const
|
STPathElement::isOffer() const noexcept
|
||||||
{
|
{
|
||||||
return isOffer_;
|
return isOffer_;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
STPathElement::isAccount() const
|
STPathElement::isAccount() const noexcept
|
||||||
{
|
{
|
||||||
return !isOffer();
|
return !isOffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
STPathElement::isType(Type const& pe) const
|
STPathElement::isType(Type pe) const noexcept
|
||||||
{
|
{
|
||||||
return (type_ & pe) != 0u;
|
return (type_ & pe) != STPathElement::TypeNone;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
STPathElement::hasIssuer() const
|
STPathElement::hasIssuer() const noexcept
|
||||||
{
|
{
|
||||||
return isType(STPathElement::TypeIssuer);
|
return isType(STPathElement::TypeIssuer);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
STPathElement::hasCurrency() const
|
STPathElement::hasCurrency() const noexcept
|
||||||
{
|
{
|
||||||
return isType(STPathElement::TypeCurrency);
|
return isType(STPathElement::TypeCurrency);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
STPathElement::hasMPT() const
|
STPathElement::hasMPT() const noexcept
|
||||||
{
|
{
|
||||||
return isType(STPathElement::TypeMpt);
|
return isType(STPathElement::TypeMpt);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
STPathElement::hasAsset() const
|
STPathElement::hasAsset() const noexcept
|
||||||
{
|
{
|
||||||
return isType(STPathElement::TypeAsset);
|
return isType(STPathElement::TypeAsset);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
STPathElement::isNone() const
|
STPathElement::isNone() const noexcept
|
||||||
{
|
{
|
||||||
return getNodeType() == STPathElement::TypeNone;
|
return getNodeType() == STPathElement::TypeNone;
|
||||||
}
|
}
|
||||||
@@ -480,13 +492,13 @@ STPathElement::isNone() const
|
|||||||
// Nodes are either an account ID or a offer prefix. Offer prefixs denote a
|
// Nodes are either an account ID or a offer prefix. Offer prefixs denote a
|
||||||
// class of offers.
|
// class of offers.
|
||||||
inline AccountID const&
|
inline AccountID const&
|
||||||
STPathElement::getAccountID() const
|
STPathElement::getAccountID() const noexcept
|
||||||
{
|
{
|
||||||
return accountID_;
|
return accountID_;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline PathAsset const&
|
inline PathAsset const&
|
||||||
STPathElement::getPathAsset() const
|
STPathElement::getPathAsset() const noexcept
|
||||||
{
|
{
|
||||||
return assetID_;
|
return assetID_;
|
||||||
}
|
}
|
||||||
@@ -504,18 +516,11 @@ STPathElement::getMPTID() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline AccountID const&
|
inline AccountID const&
|
||||||
STPathElement::getIssuerID() const
|
STPathElement::getIssuerID() const noexcept
|
||||||
{
|
{
|
||||||
return issuerID_;
|
return issuerID_;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
|
||||||
STPathElement::operator==(STPathElement const& t) const
|
|
||||||
{
|
|
||||||
return (type_ & TypeAccount) == (t.type_ & TypeAccount) && hashValue_ == t.hashValue_ &&
|
|
||||||
accountID_ == t.accountID_ && assetID_ == t.assetID_ && issuerID_ == t.issuerID_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------ STPath ------------
|
// ------------ STPath ------------
|
||||||
|
|
||||||
inline STPath::STPath(std::vector<STPathElement> p) : path_(std::move(p))
|
inline STPath::STPath(std::vector<STPathElement> p) : path_(std::move(p))
|
||||||
|
|||||||
@@ -596,7 +596,7 @@ template <std::size_t Bits, class Tag>
|
|||||||
BaseUInt<Bits, Tag>
|
BaseUInt<Bits, Tag>
|
||||||
SerialIter::getBitString()
|
SerialIter::getBitString()
|
||||||
{
|
{
|
||||||
auto const n = Bits / 8;
|
constexpr auto n = BaseUInt<Bits, Tag>::size();
|
||||||
|
|
||||||
if (remain_ < n)
|
if (remain_ < n)
|
||||||
Throw<std::runtime_error>("invalid SerialIter getBitString");
|
Throw<std::runtime_error>("invalid SerialIter getBitString");
|
||||||
@@ -607,7 +607,7 @@ SerialIter::getBitString()
|
|||||||
used_ += n;
|
used_ += n;
|
||||||
remain_ -= n;
|
remain_ -= n;
|
||||||
|
|
||||||
return BaseUInt<Bits, Tag>::fromVoid(x);
|
return BaseUInt<Bits, Tag>{std::span<std::uint8_t const, n>{x, n}};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace xrpl
|
} // namespace xrpl
|
||||||
@@ -14,7 +14,7 @@ namespace xrpl {
|
|||||||
// Various protocol and system specific constant globals.
|
// Various protocol and system specific constant globals.
|
||||||
|
|
||||||
/* The name of the system. */
|
/* The name of the system. */
|
||||||
static inline std::string const&
|
inline std::string const&
|
||||||
systemName()
|
systemName()
|
||||||
{
|
{
|
||||||
static std::string const kName = "xrpld";
|
static std::string const kName = "xrpld";
|
||||||
@@ -52,11 +52,10 @@ isLegalAmountSigned(XRPAmount const& amount)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* The currency code for the native currency. */
|
/* The currency code for the native currency. */
|
||||||
static inline std::string const&
|
inline std::string
|
||||||
systemCurrencyCode()
|
systemCurrencyCode()
|
||||||
{
|
{
|
||||||
static std::string const kCode = "XRP";
|
return "XRP";
|
||||||
return kCode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -61,26 +61,40 @@ using Domain = BaseUInt<256>;
|
|||||||
/**
|
/**
|
||||||
* XRP currency.
|
* XRP currency.
|
||||||
*/
|
*/
|
||||||
Currency const&
|
constexpr inline Currency const&
|
||||||
xrpCurrency();
|
xrpCurrency() noexcept
|
||||||
|
{
|
||||||
|
static constexpr Currency const kCurrency(beast::kZero);
|
||||||
|
return kCurrency;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A placeholder for empty currencies.
|
* A placeholder for empty currencies.
|
||||||
*/
|
*/
|
||||||
Currency const&
|
constexpr inline Currency const&
|
||||||
noCurrency();
|
noCurrency() noexcept
|
||||||
|
{
|
||||||
|
static constexpr Currency const kCurrency(xrpCurrency().next());
|
||||||
|
return kCurrency;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We deliberately disallow the currency that looks like "XRP" because too
|
* We deliberately disallow the currency that looks like "XRP" because too
|
||||||
* many people were using it instead of the correct XRP currency.
|
* many people were using it instead of the correct XRP currency.
|
||||||
|
*
|
||||||
|
* Note that this doesn't catch "xRP" or "xrp" or other case variations.
|
||||||
*/
|
*/
|
||||||
Currency const&
|
constexpr inline Currency const&
|
||||||
badCurrency();
|
badCurrency() noexcept
|
||||||
|
|
||||||
inline bool
|
|
||||||
isXRP(Currency const& c)
|
|
||||||
{
|
{
|
||||||
return c == beast::kZero;
|
static constexpr Currency kCurrency{"0000000000000000000000005852500000000000"};
|
||||||
|
return kCurrency;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline bool
|
||||||
|
isXRP(Currency const& c) noexcept
|
||||||
|
{
|
||||||
|
return c == xrpCurrency();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -98,7 +112,7 @@ to_string(Currency const& c);
|
|||||||
* to rewrite some unit test code.
|
* to rewrite some unit test code.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
toCurrency(Currency&, std::string const&);
|
toCurrency(Currency&, std::string_view);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to convert a string to a Currency, returns noCurrency() on failure.
|
* Tries to convert a string to a Currency, returns noCurrency() on failure.
|
||||||
@@ -107,8 +121,7 @@ toCurrency(Currency&, std::string const&);
|
|||||||
* unfortunate; changing this will require very careful checking
|
* unfortunate; changing this will require very careful checking
|
||||||
* everywhere and may mean having to rewrite some unit test code.
|
* everywhere and may mean having to rewrite some unit test code.
|
||||||
*/
|
*/
|
||||||
Currency
|
Currency toCurrency(std::string_view);
|
||||||
toCurrency(std::string const&);
|
|
||||||
|
|
||||||
inline std::ostream&
|
inline std::ostream&
|
||||||
operator<<(std::ostream& os, Currency const& x)
|
operator<<(std::ostream& os, Currency const& x)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <span>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
@@ -182,7 +183,7 @@ public:
|
|||||||
operator result_type() noexcept
|
operator result_type() noexcept
|
||||||
{
|
{
|
||||||
auto const digest = Sha512Hasher::result_type(h_);
|
auto const digest = Sha512Hasher::result_type(h_);
|
||||||
return result_type::fromVoid(digest.data());
|
return result_type{std::span{digest}.first<result_type::size()>()};
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -392,6 +392,7 @@ JSS(max_ledger); // in/out: LedgerCleaner
|
|||||||
JSS(max_queue_size); // out: TxQ
|
JSS(max_queue_size); // out: TxQ
|
||||||
JSS(max_spend_drops); // out: AccountInfo
|
JSS(max_spend_drops); // out: AccountInfo
|
||||||
JSS(max_spend_drops_total); // out: AccountInfo
|
JSS(max_spend_drops_total); // out: AccountInfo
|
||||||
|
JSS(maximum);
|
||||||
JSS(mean); // out: get_aggregate_price
|
JSS(mean); // out: get_aggregate_price
|
||||||
JSS(median); // out: get_aggregate_price
|
JSS(median); // out: get_aggregate_price
|
||||||
JSS(median_fee); // out: TxQ
|
JSS(median_fee); // out: TxQ
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
namespace xrpl::nft {
|
namespace xrpl::nft {
|
||||||
|
|
||||||
@@ -98,7 +99,8 @@ getTaxon(UInt256 const& id)
|
|||||||
inline AccountID
|
inline AccountID
|
||||||
getIssuer(UInt256 const& id)
|
getIssuer(UInt256 const& id)
|
||||||
{
|
{
|
||||||
return AccountID::fromVoid(id.data() + 4);
|
return AccountID{
|
||||||
|
std::span<unsigned char const, uint256::size()>{id}.subspan<4, AccountID::size()>()};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace xrpl::nft
|
} // namespace xrpl::nft
|
||||||
@@ -2,13 +2,10 @@
|
|||||||
|
|
||||||
#include <xrpl/basics/base_uint.h>
|
#include <xrpl/basics/base_uint.h>
|
||||||
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
namespace xrpl::nft {
|
namespace xrpl::nft {
|
||||||
|
|
||||||
// NFT directory pages order their contents based only on the low 96 bits of
|
// NFT directory pages order their contents based only on the low 96 bits of
|
||||||
// the NFToken value. This mask provides easy access to the necessary mask.
|
// the NFToken value. This mask provides easy access to the necessary mask.
|
||||||
constexpr UInt256 kPageMask(
|
constexpr UInt256 kPageMask{"0000000000000000000000000000000000000000ffffffffffffffffffffffff"};
|
||||||
std::string_view("0000000000000000000000000000000000000000ffffffffffffffffffffffff"));
|
|
||||||
|
|
||||||
} // namespace xrpl::nft
|
} // namespace xrpl::nft
|
||||||
@@ -51,7 +51,8 @@ public:
|
|||||||
beast::Journal journal = beast::Journal{beast::Journal::getNullSink()})
|
beast::Journal journal = beast::Journal{beast::Journal::getNullSink()})
|
||||||
: ApplyContext(registry, base, std::nullopt, tx, preclaimResult, baseFee, flags, journal)
|
: ApplyContext(registry, base, std::nullopt, tx, preclaimResult, baseFee, flags, journal)
|
||||||
{
|
{
|
||||||
XRPL_ASSERT((flags & TapBatch) == 0, "Batch apply flag should not be set");
|
XRPL_ASSERT(
|
||||||
|
(flags & ApplyFlags::Batch) == ApplyFlags::None, "Batch apply flag should not be set");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::reference_wrapper<ServiceRegistry> registry;
|
std::reference_wrapper<ServiceRegistry> registry;
|
||||||
|
|||||||
@@ -59,10 +59,11 @@ public:
|
|||||||
, parentBatchId(parentBatchId)
|
, parentBatchId(parentBatchId)
|
||||||
, j(j)
|
, j(j)
|
||||||
{
|
{
|
||||||
XRPL_ASSERT((flags & TapBatch) == TapBatch, "Batch apply flag should be set");
|
XRPL_ASSERT(
|
||||||
|
(flags & ApplyFlags::Batch) == ApplyFlags::Batch, "Batch apply flag should be set");
|
||||||
XRPL_ASSERT_IF(
|
XRPL_ASSERT_IF(
|
||||||
(flags & TapProposal) != TapNone,
|
(flags & ApplyFlags::Proposal) != ApplyFlags::None,
|
||||||
(flags & TapDryRun) != TapNone,
|
(flags & ApplyFlags::DryRun) != ApplyFlags::None,
|
||||||
"xrpl::PreflightContext : proposal preflight implies dry run");
|
"xrpl::PreflightContext : proposal preflight implies dry run");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,10 +75,11 @@ public:
|
|||||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||||
: registry(registry), tx(tx), rules(std::move(rules)), flags(flags), j(j)
|
: registry(registry), tx(tx), rules(std::move(rules)), flags(flags), j(j)
|
||||||
{
|
{
|
||||||
XRPL_ASSERT((flags & TapBatch) == 0, "Batch apply flag should not be set");
|
XRPL_ASSERT(
|
||||||
|
(flags & ApplyFlags::Batch) == ApplyFlags::None, "Batch apply flag should not be set");
|
||||||
XRPL_ASSERT_IF(
|
XRPL_ASSERT_IF(
|
||||||
(flags & TapProposal) != TapNone,
|
(flags & ApplyFlags::Proposal) != ApplyFlags::None,
|
||||||
(flags & TapDryRun) != TapNone,
|
(flags & ApplyFlags::DryRun) != ApplyFlags::None,
|
||||||
"xrpl::PreflightContext : proposal preflight implies dry run");
|
"xrpl::PreflightContext : proposal preflight implies dry run");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +118,7 @@ public:
|
|||||||
, j(j)
|
, j(j)
|
||||||
{
|
{
|
||||||
XRPL_ASSERT(
|
XRPL_ASSERT(
|
||||||
parentBatchId.has_value() == ((flags & TapBatch) == TapBatch),
|
parentBatchId.has_value() == ((flags & ApplyFlags::Batch) == ApplyFlags::Batch),
|
||||||
"Parent Batch ID should be set if batch apply flag is set");
|
"Parent Batch ID should be set if batch apply flag is set");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +131,8 @@ public:
|
|||||||
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
beast::Journal j = beast::Journal{beast::Journal::getNullSink()})
|
||||||
: PreclaimContext(registry, view, preflightResult, tx, flags, std::nullopt, j)
|
: PreclaimContext(registry, view, preflightResult, tx, flags, std::nullopt, j)
|
||||||
{
|
{
|
||||||
XRPL_ASSERT((flags & TapBatch) == 0, "Batch apply flag should not be set");
|
XRPL_ASSERT(
|
||||||
|
(flags & ApplyFlags::Batch) == ApplyFlags::None, "Batch apply flag should not be set");
|
||||||
}
|
}
|
||||||
|
|
||||||
PreclaimContext&
|
PreclaimContext&
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ struct ApplyResult
|
|||||||
inline bool
|
inline bool
|
||||||
isTecClaimHardFail(TER ter, ApplyFlags flags)
|
isTecClaimHardFail(TER ter, ApplyFlags flags)
|
||||||
{
|
{
|
||||||
return isTecClaim(ter) && ((flags & TapRetry) == 0u);
|
return isTecClaim(ter) && ((flags & ApplyFlags::Retry) == ApplyFlags::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+73
-73
@@ -2,144 +2,144 @@ Detected OS: macos (Darwin arm64)
|
|||||||
|
|
||||||
Core build tools:
|
Core build tools:
|
||||||
✅ cmake
|
✅ cmake
|
||||||
cmake version 4.1.2
|
cmake version 4.4.3
|
||||||
/nix/store/gvabsb4yqb5xsqzqph54rijnn4zpihnp-cmake-4.1.2/bin/cmake
|
/nix/store/q85csxf4s4shx89zif097h1ql4ax1rrz-cmake-4.4.3/bin/cmake
|
||||||
✅ conan
|
✅ conan
|
||||||
Conan version 2.28.1
|
Conan version 2.32.0
|
||||||
/nix/store/9jiyxmkpwmn6dcqs0765s83riw3l5ail-conan-2.28.1/bin/conan
|
/nix/store/921jqsgbilixmr3xchj95si0jl8pi13z-conan-2.32.0/bin/conan
|
||||||
✅ git
|
✅ git
|
||||||
git version 2.54.0
|
git version 2.55.0
|
||||||
/nix/store/a14yxcqvv9x2l9mllgpirzhvz93pgprg-git-2.54.0/bin/git
|
/nix/store/gw7c7m0dwca5lg4152x9lpmxpj171bzw-git-2.55.0/bin/git
|
||||||
✅ python3
|
✅ python3
|
||||||
Python 3.13.13
|
Python 3.14.7
|
||||||
/nix/store/ygxqin6ydzjfawywqpp5pal8wv6sf5bh-python3-3.13.13/bin/python3.13
|
/nix/store/0kcws5blwbdyxqxjd1w8hjipz61xny3k-python3-3.14.7/bin/python3.14
|
||||||
|
|
||||||
Development tooling:
|
Development tooling:
|
||||||
✅ ccache
|
✅ ccache
|
||||||
ccache version 4.13.6
|
ccache version 4.13.6
|
||||||
/nix/store/57davyvs6p6dkrl3svzwg1ph18wsy4cz-ccache-4.13.6/bin/ccache
|
/nix/store/ydr5nlzxp1djb5256y0zzqa87vz55gsk-ccache-4.13.6/bin/ccache
|
||||||
✅ clang
|
✅ clang
|
||||||
clang version 22.1.7
|
clang version 22.1.8
|
||||||
/nix/store/192glrb2cldvziyf3378mzjqbzx3ih4g-clang-wrapper-22.1.7/bin/clang
|
/nix/store/dakcxgwz4ixkk6ps50b7vn3dvblyzsnf-clang-wrapper-22.1.8/bin/clang
|
||||||
✅ clang-22
|
✅ clang-22
|
||||||
clang version 22.1.7
|
clang version 22.1.8
|
||||||
/nix/store/rbap7zqq7mw00fyqa02p5rj7gqjp4w5i-clang-22/bin/clang-22
|
/nix/store/w826sqb98nnym6kzackg829664iqdlxj-clang-22/bin/clang-22
|
||||||
✅ clang++
|
✅ clang++
|
||||||
clang version 22.1.7
|
clang version 22.1.8
|
||||||
/nix/store/192glrb2cldvziyf3378mzjqbzx3ih4g-clang-wrapper-22.1.7/bin/clang++
|
/nix/store/dakcxgwz4ixkk6ps50b7vn3dvblyzsnf-clang-wrapper-22.1.8/bin/clang++
|
||||||
✅ clang++-22
|
✅ clang++-22
|
||||||
clang version 22.1.7
|
clang version 22.1.8
|
||||||
/nix/store/v9haf787f7bcz0mq1sad4bpyx21pj6li-clang++-22/bin/clang++-22
|
/nix/store/wnwyw3yfqrhpxij65bl3pa1x65q2zg3m-clang++-22/bin/clang++-22
|
||||||
✅ ClangBuildAnalyzer
|
✅ ClangBuildAnalyzer
|
||||||
ClangBuildAnalyzer 1.6.0
|
ClangBuildAnalyzer 1.6.0
|
||||||
/nix/store/4l50ds9fa2mkvh7wg8qzrlbmjs12sb8l-clangbuildanalyzer-1.6.0/bin/ClangBuildAnalyzer
|
/nix/store/c7jjnw29ra78jdlzjww2m374izhy05n8-clangbuildanalyzer-1.6.0/bin/ClangBuildAnalyzer
|
||||||
✅ curl
|
✅ curl
|
||||||
curl 8.20.0 (aarch64-apple-darwin25.3.0) libcurl/8.20.0 OpenSSL/3.6.2 zlib/1.3.2 libssh2/1.11.1 nghttp2/1.69.0 mit-krb5/1.22.1
|
curl 8.22.0 (aarch64-apple-darwin25.6.0) libcurl/8.22.0 OpenSSL/3.5.8 zlib/1.3.2 libssh2/1.11.1 nghttp2/1.70.0 mit-krb5/1.22.2
|
||||||
/nix/store/kclq0czaxvsgh4ym9ld7b6iwy50l1snk-curl-8.20.0-bin/bin/curl
|
/nix/store/pq1h17abqy6q5y6k5dn9qj0fwb89flba-curl-8.22.0-bin/bin/curl
|
||||||
✅ file
|
✅ file
|
||||||
file-5.47
|
file-5.48
|
||||||
/nix/store/dax63li7wwcbqxxkkgzc4g2rx7d4w86x-file-5.47/bin/file
|
/nix/store/y44vpziwbzfm8020dzlqqbybyyy7lb6g-file-5.48/bin/file
|
||||||
✅ less
|
✅ less
|
||||||
less 692 (PCRE2 regular expressions)
|
less 710 (PCRE2 regular expressions)
|
||||||
/nix/store/lvr16y75r1pdxpdv0aph5ak2yd0hkvqm-less-692/bin/less
|
/nix/store/3yn3dx5fwg5i0inqqzwnxq7f6lwhpjqa-less-710/bin/less
|
||||||
✅ make
|
✅ make
|
||||||
GNU Make 4.4.1
|
GNU Make 4.4.1
|
||||||
/nix/store/8wwiw8pwyhrkzyq28hqzxfl4z84lks81-gnumake-4.4.1/bin/make
|
/nix/store/8l4wgxmhdxfvc9g18y76bnxl1ghgcp7n-gnumake-4.4.1/bin/make
|
||||||
✅ netstat
|
✅ netstat
|
||||||
present
|
present
|
||||||
/nix/store/qsd1kzqb0ahrk433vmyl245gp623j19s-network_cmds-730.80.3/bin/netstat
|
/nix/store/57fqvgdav8lrmzndkwm7qrgksl01yj23-network_cmds-741.100.2/bin/netstat
|
||||||
✅ ninja
|
✅ ninja
|
||||||
1.13.2
|
1.13.2
|
||||||
/nix/store/bqykhrblarkj4fl0hz2mf8ngwfv6x6bz-ninja-1.13.2/bin/ninja
|
/nix/store/infm4vlx71q83bqz0k559w1gn4b0rq4i-ninja-1.13.2/bin/ninja
|
||||||
✅ perl
|
✅ perl
|
||||||
v5.42.0
|
v5.42.3
|
||||||
/nix/store/js13ri9fvm0ajk1fpd3acigys2a9whdv-perl-5.42.0/bin/perl
|
/nix/store/7rvr6qjfzk2ybzn1imjw6c6qq1n1a9cr-perl-5.42.3/bin/perl
|
||||||
✅ pkg-config
|
✅ pkg-config
|
||||||
0.29.2
|
0.29.2
|
||||||
/nix/store/lzrwr375jqhhbca116kja96xf1md83l8-pkg-config-wrapper-0.29.2/bin/pkg-config
|
/nix/store/nhgylvj98klqggy54h7gbc3b2ia4a00y-pkg-config-wrapper-0.29.2/bin/pkg-config
|
||||||
✅ vim
|
✅ vim
|
||||||
VIM - Vi IMproved 9.2 (2026 Feb 14, compiled Jan 01 1980 00:00:00)
|
VIM - Vi IMproved 9.2 (2026 Feb 14, compiled Jan 01 1980 00:00:00)
|
||||||
/nix/store/6vbkykg92w603c0sw3mkk7p7mfaawbns-vim-9.2.0389/bin/vim
|
/nix/store/akaa9kqm3b8rlkv61kvsqr2l2j70rvxb-vim-9.2.1001/bin/vim
|
||||||
✅ zip
|
✅ zip
|
||||||
Zip 3.0
|
Zip 3.0
|
||||||
/nix/store/z6ph729vcakbvz3wh8ln1wk6mi06w487-zip-3.0/bin/zip
|
/nix/store/dvawr36npnad4xa6dxi2ss0fw44aa82r-zip-3.0/bin/zip
|
||||||
✅ clang-apply-replacements
|
✅ clang-apply-replacements
|
||||||
clang-apply-replacements version 22.1.7
|
clang-apply-replacements version 22.1.8
|
||||||
/nix/store/vzyyjf3cm1hbj9wcr2qcb66x6j98zpy7-clang-tools-22.1.7/bin/clang-apply-replacements
|
/nix/store/5bij4zn161lagwyndmrr21vmxjg054nv-clang-tools-22.1.8/bin/clang-apply-replacements
|
||||||
✅ clang-apply-replacements-22
|
✅ clang-apply-replacements-22
|
||||||
clang-apply-replacements version 22.1.7
|
clang-apply-replacements version 22.1.8
|
||||||
/nix/store/m3ii69rca4077lf4wlk7m3jcag1fs577-clang-apply-replacements-22/bin/clang-apply-replacements-22
|
/nix/store/w4bz034k4l0w719rrmbwnqcgyjbswy81-clang-apply-replacements-22/bin/clang-apply-replacements-22
|
||||||
✅ clang-format
|
✅ clang-format
|
||||||
clang-format version 22.1.7
|
clang-format version 22.1.8
|
||||||
/nix/store/vzyyjf3cm1hbj9wcr2qcb66x6j98zpy7-clang-tools-22.1.7/bin/clang-format
|
/nix/store/5bij4zn161lagwyndmrr21vmxjg054nv-clang-tools-22.1.8/bin/clang-format
|
||||||
✅ clang-format-22
|
✅ clang-format-22
|
||||||
clang-format version 22.1.7
|
clang-format version 22.1.8
|
||||||
/nix/store/4fawqy6ngqcsqd2ygyyzm93q0xy3f5gs-clang-format-22/bin/clang-format-22
|
/nix/store/w7z346l0r8y36b6lc5i7jzj3w0581l2y-clang-format-22/bin/clang-format-22
|
||||||
✅ clang-tidy
|
✅ clang-tidy
|
||||||
LLVM version 22.1.7
|
LLVM version 22.1.8
|
||||||
/nix/store/vzyyjf3cm1hbj9wcr2qcb66x6j98zpy7-clang-tools-22.1.7/bin/clang-tidy
|
/nix/store/5bij4zn161lagwyndmrr21vmxjg054nv-clang-tools-22.1.8/bin/clang-tidy
|
||||||
✅ clang-tidy-22
|
✅ clang-tidy-22
|
||||||
LLVM version 22.1.7
|
LLVM version 22.1.8
|
||||||
/nix/store/jqw4280saixaxxihwdba9ldm2fsm6dr3-clang-tidy-22/bin/clang-tidy-22
|
/nix/store/0lv25qd3ddgjm3ndn70q61jfr6krlkl7-clang-tidy-22/bin/clang-tidy-22
|
||||||
✅ dot
|
✅ dot
|
||||||
dot - graphviz version 12.2.1 (0)
|
dot - graphviz version 15.1.1 (0)
|
||||||
/nix/store/ijb4fbnqa6wzlpqnhb6q9knqpf7qqn5z-graphviz-12.2.1/bin/dot
|
/nix/store/mc99a6bpbk2waym2cnw6ifmxli4ndb4x-graphviz-15.1.1/bin/dot
|
||||||
✅ doxygen
|
✅ doxygen
|
||||||
1.16.1
|
1.17.0
|
||||||
/nix/store/kbryjdpq9jizjb0ws0nzbf2h2ymbdiwm-doxygen-1.16.1/bin/doxygen
|
/nix/store/kz43yaj0wg6fx6cbrcmi2442z1ramy0c-doxygen-1.17.0/bin/doxygen
|
||||||
✅ gcovr
|
✅ gcovr
|
||||||
gcovr 8.4
|
gcovr 8.4
|
||||||
/nix/store/wn8jiyh9p0bybs96s4163qp3k8vfmczx-python3.13-gcovr-8.4/bin/gcovr
|
/nix/store/czj1vxsizrbba280gg22cys40qxkqjgr-python3.14-gcovr-8.4/bin/gcovr
|
||||||
✅ gh
|
✅ gh
|
||||||
gh version 2.94.0 (nixpkgs)
|
gh version 2.102.0 (2026-09-30)
|
||||||
/nix/store/fhnpw0hs0gjms1ha6ap02jq7rx13gkbp-gh-2.94.0/bin/gh
|
/nix/store/s8bahl6zssvry93ihivfmk1l6c58byrr-gh-2.102.0/bin/gh
|
||||||
✅ git-cliff
|
✅ git-cliff
|
||||||
git-cliff 2.13.1
|
git-cliff 2.14.2
|
||||||
/nix/store/cy0wwhgxa7yvrz97zydbq6sqmixc90fq-git-cliff-2.13.1/bin/git-cliff
|
/nix/store/k4m5yk0qb7hfslwsq1zjajz1d94j12bj-git-cliff-2.14.2/bin/git-cliff
|
||||||
✅ git-lfs
|
✅ git-lfs
|
||||||
git-lfs/3.7.1 (3.7.1; darwin arm64; go 1.26.3)
|
git-lfs/3.8.0 (3.8.0; darwin arm64; go 1.26.8)
|
||||||
/nix/store/k9r7zjfjplqa4d5s71cqvf2iv73jd9mc-git-lfs-3.7.1/bin/git-lfs
|
/nix/store/wzixjr3yb40a13m2q654hm3bdjn6sal3-git-lfs-3.8.0/bin/git-lfs
|
||||||
✅ gpg
|
✅ gpg
|
||||||
gpg (GnuPG) 2.4.9
|
gpg (GnuPG) 2.4.9
|
||||||
/nix/store/cgh6iwzz5jgx9z5whka4vgj210i6npc6-gnupg-2.4.9/bin/gpg
|
/nix/store/0ldfwnwx385i5kh7cyvxx4mkvgjhs5rh-gnupg-2.4.9/bin/gpg
|
||||||
✅ pre-commit
|
✅ pre-commit
|
||||||
pre-commit 4.5.1
|
pre-commit 4.6.2
|
||||||
/nix/store/z3cca68620w0w10f090szgzdnmh1waf2-pre-commit-4.5.1/bin/pre-commit
|
/nix/store/ivh54xypg9qvd3ha9lis0i8p4nkf8n7a-pre-commit-4.6.2/bin/pre-commit
|
||||||
✅ run-clang-tidy
|
✅ run-clang-tidy
|
||||||
usage: run-clang-tidy [-h] [-allow-enabling-alpha-checkers]
|
usage: run-clang-tidy [-h] [-allow-enabling-alpha-checkers]
|
||||||
/nix/store/4x28x911z2f9y7adqlh3qspp4a16dig7-run-clang-tidy/bin/run-clang-tidy
|
/nix/store/3xss1mm3mh1hzcmg182na6ki1p5rnxf1-run-clang-tidy/bin/run-clang-tidy
|
||||||
✅ run-clang-tidy-22
|
✅ run-clang-tidy-22
|
||||||
usage: run-clang-tidy [-h] [-allow-enabling-alpha-checkers]
|
usage: run-clang-tidy [-h] [-allow-enabling-alpha-checkers]
|
||||||
/nix/store/x8iymrh76sk5q91ryg5pa7i32s6gfh34-run-clang-tidy-22/bin/run-clang-tidy-22
|
/nix/store/w68r9z07hcq4fwbfyq3yvc7qxm8aqfbl-run-clang-tidy-22/bin/run-clang-tidy-22
|
||||||
|
|
||||||
Rust toolchain:
|
Rust toolchain:
|
||||||
✅ cargo
|
✅ cargo
|
||||||
cargo 1.97.1 (c980f4866 2026-06-30)
|
cargo 1.97.1 (c980f4866 2026-06-30)
|
||||||
/nix/store/bnfk1sl4s9angb0vj1cj9a5y5zvqinwy-rust-minimal-1.97.1/bin/cargo
|
/nix/store/gklji8yq8yrx1k039c10kwz60vqf2dxj-rust-minimal-1.97.1/bin/cargo
|
||||||
✅ cargo-audit
|
✅ cargo-audit
|
||||||
cargo-audit-audit 0.22.1
|
cargo-audit-audit 0.22.2
|
||||||
/nix/store/snwkga2f5gyf404h7mmp9wriwxb8v65f-cargo-audit-0.22.1/bin/cargo-audit
|
/nix/store/7jgrwj557l91940aj0ag4i924ima1cqn-cargo-audit-0.22.2/bin/cargo-audit
|
||||||
✅ cargo-llvm-cov
|
✅ cargo-llvm-cov
|
||||||
cargo-llvm-cov 0.8.5
|
cargo-llvm-cov 0.9.0
|
||||||
/nix/store/fpiqdh91gwyxalqp409ynm0s0g086w7w-cargo-llvm-cov-0.8.5/bin/cargo-llvm-cov
|
/nix/store/5q1h285z8zzkw2qxig9a0b1avcsk1cf0-cargo-llvm-cov-0.9.0/bin/cargo-llvm-cov
|
||||||
✅ cargo-nextest
|
✅ cargo-nextest
|
||||||
cargo-nextest 0.9.137
|
cargo-nextest 0.9.146
|
||||||
/nix/store/ylz7m947mhkgsp6i7611id3s3gcd58nq-cargo-nextest-0.9.137/bin/cargo-nextest
|
/nix/store/69v3hx3k597hcxd2zy7yad7fg0aw5yxd-cargo-nextest-0.9.146/bin/cargo-nextest
|
||||||
✅ clippy-driver
|
✅ clippy-driver
|
||||||
clippy 0.1.97 (8bab26f4f6 2026-07-14)
|
clippy 0.1.97 (8bab26f4f6 2026-07-14)
|
||||||
/nix/store/bnfk1sl4s9angb0vj1cj9a5y5zvqinwy-rust-minimal-1.97.1/bin/clippy-driver
|
/nix/store/gklji8yq8yrx1k039c10kwz60vqf2dxj-rust-minimal-1.97.1/bin/clippy-driver
|
||||||
✅ rust-analyzer
|
✅ rust-analyzer
|
||||||
rust-analyzer 1.97.1 (8bab26f4 2026-07-14)
|
rust-analyzer 1.97.1 (8bab26f4 2026-07-14)
|
||||||
/nix/store/j6apc5pmd0giy15da9p650r8zklslmvi-rust-analyzer-preview-1.97.1-aarch64-apple-darwin/bin/rust-analyzer
|
/nix/store/30rbyk8gx7v53fxqhs4pp0fvxfjycspr-rust-analyzer-preview-1.97.1-aarch64-apple-darwin/bin/rust-analyzer
|
||||||
✅ rust-nightly
|
✅ rust-nightly
|
||||||
rustc 1.99.0-nightly (87e5904f5 2026-07-20)
|
rustc 1.101.0-nightly (282215592 2026-10-04)
|
||||||
/nix/store/fqpjz4l0nsnji8b2pz57mnj0akbp6hcl-rust-nightly/bin/rust-nightly
|
/nix/store/a9552rf3pylcnkijc1z0hf1lcnv50djq-rust-nightly/bin/rust-nightly
|
||||||
✅ rustc
|
✅ rustc
|
||||||
rustc 1.97.1 (8bab26f4f 2026-07-14)
|
rustc 1.97.1 (8bab26f4f 2026-07-14)
|
||||||
/nix/store/bnfk1sl4s9angb0vj1cj9a5y5zvqinwy-rust-minimal-1.97.1/bin/rustc
|
/nix/store/gklji8yq8yrx1k039c10kwz60vqf2dxj-rust-minimal-1.97.1/bin/rustc
|
||||||
✅ rustfmt
|
✅ rustfmt
|
||||||
rustfmt 1.9.0-stable (8bab26f4f6 2026-07-14)
|
rustfmt 1.9.0-stable (8bab26f4f6 2026-07-14)
|
||||||
/nix/store/5ymwgr9jqjz7zzbmj0j5vqbwcd3kp0vm-rustfmt-preview-1.97.1-aarch64-apple-darwin/bin/rustfmt
|
/nix/store/0jmdipyn8266j9kqx2xxm2qkxlrlrjfc-rustfmt-preview-1.97.1-aarch64-apple-darwin/bin/rustfmt
|
||||||
|
|
||||||
Skipping git-over-HTTPS check (CHECK_TOOLS_SKIP_CLONE is set).
|
Skipping git-over-HTTPS check (CHECK_TOOLS_SKIP_CLONE is set).
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -21,7 +21,8 @@ let
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
toolchain = if pkgs.stdenv.isLinux then linux.toolchain else (darwin.toolchain ++ [ darwinEnv ]);
|
toolchain =
|
||||||
|
if pkgs.stdenv.hostPlatform.isLinux then linux.toolchain else (darwin.toolchain ++ [ darwinEnv ]);
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
default = pkgs.buildEnv {
|
default = pkgs.buildEnv {
|
||||||
|
|||||||
+7
-6
@@ -20,15 +20,16 @@ let
|
|||||||
|
|
||||||
# Custom-glibc stdenvs, matching the CI environment. darwin has no custom
|
# Custom-glibc stdenvs, matching the CI environment. darwin has no custom
|
||||||
# glibc, so there they fall back to the plain nixpkgs stdenvs.
|
# glibc, so there they fall back to the plain nixpkgs stdenvs.
|
||||||
customGccStdenv = if pkgs.stdenv.isLinux then linux.gccStdenv else plainGccStdenv;
|
customGccStdenv = if pkgs.stdenv.hostPlatform.isLinux then linux.gccStdenv else plainGccStdenv;
|
||||||
customClangStdenv = if pkgs.stdenv.isLinux then linux.clangStdenv else plainClangStdenv;
|
customClangStdenv =
|
||||||
|
if pkgs.stdenv.hostPlatform.isLinux then linux.clangStdenv else plainClangStdenv;
|
||||||
|
|
||||||
# gcov matching each gcc shell, so `-Dcoverage=ON` builds work in the shell.
|
# gcov matching each gcc shell, so `-Dcoverage=ON` builds work in the shell.
|
||||||
plainGcov = mkGcov {
|
plainGcov = mkGcov {
|
||||||
name = "plain";
|
name = "plain";
|
||||||
cc = gccPackage.cc;
|
cc = gccPackage.cc;
|
||||||
};
|
};
|
||||||
customGccGcov = if pkgs.stdenv.isLinux then linux.gcov else plainGcov;
|
customGccGcov = if pkgs.stdenv.hostPlatform.isLinux then linux.gcov else plainGcov;
|
||||||
|
|
||||||
# Whole directory: init.sh locates the profiles relative to itself.
|
# Whole directory: init.sh locates the profiles relative to itself.
|
||||||
conanDir = ../conan;
|
conanDir = ../conan;
|
||||||
@@ -51,7 +52,7 @@ let
|
|||||||
|
|
||||||
# Not sdkEnv: a shell's stdenv already sets that up. Prepended so the stub
|
# Not sdkEnv: a shell's stdenv already sets that up. Prepended so the stub
|
||||||
# beats the nixpkgs libresolv this shell's tooling drags in.
|
# beats the nixpkgs libresolv this shell's tooling drags in.
|
||||||
darwinLibresolvHook = pkgs.lib.optionalString pkgs.stdenv.isDarwin (
|
darwinLibresolvHook = pkgs.lib.optionalString pkgs.stdenv.hostPlatform.isDarwin (
|
||||||
pkgs.lib.concatLines (
|
pkgs.lib.concatLines (
|
||||||
pkgs.lib.mapAttrsToList (
|
pkgs.lib.mapAttrsToList (
|
||||||
name: value: ''export ${name}="${value} ''${${name}:-}"''
|
name: value: ''export ${name}="${value} ''${${name}:-}"''
|
||||||
@@ -126,7 +127,7 @@ let
|
|||||||
in
|
in
|
||||||
rec {
|
rec {
|
||||||
# macOS: Nix Clang. Linux: Nix GCC.
|
# macOS: Nix Clang. Linux: Nix GCC.
|
||||||
default = if pkgs.stdenv.isDarwin then clang else gcc;
|
default = if pkgs.stdenv.hostPlatform.isDarwin then clang else gcc;
|
||||||
|
|
||||||
# gcc/clang use the custom-glibc toolchain, matching CI. On darwin there is no
|
# gcc/clang use the custom-glibc toolchain, matching CI. On darwin there is no
|
||||||
# custom glibc, so they fall back to the plain nixpkgs toolchain.
|
# custom glibc, so they fall back to the plain nixpkgs toolchain.
|
||||||
@@ -171,7 +172,7 @@ rec {
|
|||||||
# The *-plain shells (stock nixpkgs toolchain) exist only on Linux: on darwin
|
# The *-plain shells (stock nixpkgs toolchain) exist only on Linux: on darwin
|
||||||
# gcc/clang are already plain, so these would be redundant and are omitted, which
|
# gcc/clang are already plain, so these would be redundant and are omitted, which
|
||||||
# makes `nix develop .#gcc-plain` fail there rather than silently aliasing gcc.
|
# makes `nix develop .#gcc-plain` fail there rather than silently aliasing gcc.
|
||||||
// pkgs.lib.optionalAttrs pkgs.stdenv.isLinux {
|
// pkgs.lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux {
|
||||||
gcc-plain = makeShell {
|
gcc-plain = makeShell {
|
||||||
shellName = "gcc-plain";
|
shellName = "gcc-plain";
|
||||||
stdenv = plainGccStdenv;
|
stdenv = plainGccStdenv;
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
#include <xrpl/basics/CountedObject.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace xrpl {
|
|
||||||
|
|
||||||
CountedObjects&
|
|
||||||
CountedObjects::getInstance() noexcept
|
|
||||||
{
|
|
||||||
static CountedObjects kInstance;
|
|
||||||
|
|
||||||
return kInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
CountedObjects::CountedObjects() noexcept : count_(0), head_(nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CountedObjects::List
|
|
||||||
CountedObjects::getCounts(int minimumThreshold) const
|
|
||||||
{
|
|
||||||
List counts;
|
|
||||||
|
|
||||||
// When other operations are concurrent, the count
|
|
||||||
// might be temporarily less than the actual count.
|
|
||||||
counts.reserve(count_.load());
|
|
||||||
|
|
||||||
for (auto* ctr = head_.load(); ctr != nullptr; ctr = ctr->getNext())
|
|
||||||
{
|
|
||||||
if (ctr->getCount() >= minimumThreshold)
|
|
||||||
counts.emplace_back(ctr->getName(), ctr->getCount());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ranges::sort(counts);
|
|
||||||
|
|
||||||
return counts;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace xrpl
|
|
||||||
@@ -80,10 +80,10 @@ BookDirs::const_iterator::operator++()
|
|||||||
XRPL_ASSERT(index_ != kZero, "xrpl::BookDirs::const_iterator::operator++ : nonzero index");
|
XRPL_ASSERT(index_ != kZero, "xrpl::BookDirs::const_iterator::operator++ : nonzero index");
|
||||||
if (!cdirNext(*view_, curKey_, sle_, entry_, index_))
|
if (!cdirNext(*view_, curKey_, sle_, entry_, index_))
|
||||||
{
|
{
|
||||||
if (index_ == 0)
|
if (index_ == kZero)
|
||||||
curKey_ = view_->succ(++curKey_, nextQuality_).value_or(kZero);
|
curKey_ = view_->succ(++curKey_, nextQuality_).value_or(kZero);
|
||||||
|
|
||||||
if (index_ != 0 || curKey_ == kZero)
|
if (index_ != kZero || curKey_ == kZero)
|
||||||
{
|
{
|
||||||
curKey_ = key_;
|
curKey_ = key_;
|
||||||
entry_ = 0;
|
entry_ = 0;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <xrpl/beast/utility/instrumentation.h>
|
#include <xrpl/beast/utility/instrumentation.h>
|
||||||
#include <xrpl/ledger/LedgerTiming.h>
|
#include <xrpl/ledger/LedgerTiming.h>
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
|
#include <xrpl/ledger/entries/FeeSettingsEntry.h>
|
||||||
#include <xrpl/nodestore/NodeObject.h>
|
#include <xrpl/nodestore/NodeObject.h>
|
||||||
#include <xrpl/protocol/Feature.h>
|
#include <xrpl/protocol/Feature.h>
|
||||||
#include <xrpl/protocol/Fees.h>
|
#include <xrpl/protocol/Fees.h>
|
||||||
@@ -261,7 +262,7 @@ Ledger::Ledger(Ledger const& prevLedger, NetClock::time_point closeTime)
|
|||||||
{
|
{
|
||||||
header_.seq = prevLedger.header_.seq + 1;
|
header_.seq = prevLedger.header_.seq + 1;
|
||||||
header_.parentCloseTime = prevLedger.header_.closeTime;
|
header_.parentCloseTime = prevLedger.header_.closeTime;
|
||||||
header_.hash = prevLedger.header().hash + UInt256(1);
|
header_.hash = prevLedger.header().hash.next();
|
||||||
header_.drops = prevLedger.header().drops;
|
header_.drops = prevLedger.header().drops;
|
||||||
header_.closeTimeResolution = prevLedger.header_.closeTimeResolution;
|
header_.closeTimeResolution = prevLedger.header_.closeTimeResolution;
|
||||||
header_.parentHash = prevLedger.header().hash;
|
header_.parentHash = prevLedger.header().hash;
|
||||||
@@ -560,7 +561,7 @@ Ledger::setup()
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (auto const sle = read(keylet::feeSettings()))
|
if (auto const sle = FeeSettingsEntryR(*this))
|
||||||
{
|
{
|
||||||
bool oldFees = false;
|
bool oldFees = false;
|
||||||
bool newFees = false;
|
bool newFees = false;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <xrpl/beast/utility/instrumentation.h>
|
#include <xrpl/beast/utility/instrumentation.h>
|
||||||
#include <xrpl/ledger/ApplyView.h>
|
#include <xrpl/ledger/ApplyView.h>
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
|
#include <xrpl/ledger/entries/LedgerHashesEntry.h>
|
||||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||||
@@ -361,17 +362,16 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal)
|
|||||||
if (int const diff = ledger.seq() - seq; diff <= 256)
|
if (int const diff = ledger.seq() - seq; diff <= 256)
|
||||||
{
|
{
|
||||||
// Within 256...
|
// Within 256...
|
||||||
auto const hashIndex = ledger.read(keylet::skip());
|
LedgerHashesEntryR const hashIndex(ledger, journal);
|
||||||
if (hashIndex)
|
if (hashIndex)
|
||||||
{
|
{
|
||||||
XRPL_ASSERT(
|
XRPL_ASSERT(
|
||||||
hashIndex->getFieldU32(sfLastLedgerSequence) == (ledger.seq() - 1),
|
hashIndex->getFieldU32(sfLastLedgerSequence) == (ledger.seq() - 1),
|
||||||
"xrpl::hashOfSeq : matching ledger sequence");
|
"xrpl::hashOfSeq : matching ledger sequence");
|
||||||
STVector256 vec = hashIndex->getFieldV256(sfHashes);
|
if (auto const hash = hashIndex.hashAt(diff - 1))
|
||||||
if (vec.size() >= diff)
|
return hash;
|
||||||
return vec[vec.size() - diff];
|
|
||||||
JLOG(journal.warn()) << "Ledger " << ledger.seq() << " missing hash for " << seq << " ("
|
JLOG(journal.warn()) << "Ledger " << ledger.seq() << " missing hash for " << seq << " ("
|
||||||
<< vec.size() << "," << diff << ")";
|
<< hashIndex->getFieldV256(sfHashes).size() << "," << diff << ")";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -387,16 +387,14 @@ hashOfSeq(ReadView const& ledger, LedgerIndex seq, beast::Journal journal)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// in skiplist
|
// in skiplist
|
||||||
auto const hashIndex = ledger.read(keylet::skip(seq));
|
LedgerHashesEntryR const hashIndex(keylet::skip(seq), ledger, journal);
|
||||||
if (hashIndex)
|
if (hashIndex)
|
||||||
{
|
{
|
||||||
auto const lastSeq = hashIndex->getFieldU32(sfLastLedgerSequence);
|
auto const lastSeq = hashIndex->getFieldU32(sfLastLedgerSequence);
|
||||||
XRPL_ASSERT(lastSeq >= seq, "xrpl::hashOfSeq : minimum last ledger");
|
XRPL_ASSERT(lastSeq >= seq, "xrpl::hashOfSeq : minimum last ledger");
|
||||||
XRPL_ASSERT((lastSeq & 0xff) == 0, "xrpl::hashOfSeq : valid last ledger");
|
XRPL_ASSERT((lastSeq & 0xff) == 0, "xrpl::hashOfSeq : valid last ledger");
|
||||||
auto const diff = (lastSeq - seq) >> 8;
|
if (auto const hash = hashIndex.hashAt((lastSeq - seq) >> 8))
|
||||||
STVector256 vec = hashIndex->getFieldV256(sfHashes);
|
return hash;
|
||||||
if (vec.size() > diff)
|
|
||||||
return vec[vec.size() - diff - 1];
|
|
||||||
}
|
}
|
||||||
JLOG(journal.warn()) << "Can't get seq " << seq << " from " << ledger.seq() << " error";
|
JLOG(journal.warn()) << "Can't get seq " << seq << " from " << ledger.seq() << " error";
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|||||||
@@ -506,7 +506,7 @@ pseudoAccountAddress(ReadView const& view, UInt256 const& pseudoOwnerKey)
|
|||||||
RipeshaHasher rsh;
|
RipeshaHasher rsh;
|
||||||
auto const hash = sha512Half(i, view.header().parentHash, pseudoOwnerKey);
|
auto const hash = sha512Half(i, view.header().parentHash, pseudoOwnerKey);
|
||||||
rsh(hash.data(), hash.size());
|
rsh(hash.data(), hash.size());
|
||||||
AccountID const ret = AccountID::fromRaw(static_cast<RipeshaHasher::result_type>(rsh));
|
AccountID const ret{static_cast<RipeshaHasher::result_type>(rsh)};
|
||||||
if (!view.read(keylet::account(ret)))
|
if (!view.read(keylet::account(ret)))
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include <xrpl/nodestore/detail/DecodedBlob.h>
|
#include <xrpl/nodestore/detail/DecodedBlob.h>
|
||||||
|
|
||||||
#include <xrpl/basics/Blob.h>
|
#include <xrpl/basics/Blob.h>
|
||||||
#include <xrpl/basics/base_uint.h>
|
|
||||||
#include <xrpl/basics/safe_cast.h>
|
#include <xrpl/basics/safe_cast.h>
|
||||||
#include <xrpl/beast/utility/instrumentation.h>
|
#include <xrpl/beast/utility/instrumentation.h>
|
||||||
#include <xrpl/nodestore/NodeObject.h>
|
#include <xrpl/nodestore/NodeObject.h>
|
||||||
@@ -12,8 +11,13 @@
|
|||||||
|
|
||||||
namespace xrpl::node_store {
|
namespace xrpl::node_store {
|
||||||
|
|
||||||
DecodedBlob::DecodedBlob(void const* key, void const* value, int valueBytes) : key_(key)
|
DecodedBlob::DecodedBlob(std::optional<UInt256> key, void const* value, int valueBytes)
|
||||||
{
|
{
|
||||||
|
if (!key)
|
||||||
|
return;
|
||||||
|
|
||||||
|
key_ = key.value();
|
||||||
|
|
||||||
/* Data format:
|
/* Data format:
|
||||||
|
|
||||||
Bytes
|
Bytes
|
||||||
@@ -63,7 +67,7 @@ DecodedBlob::createObject()
|
|||||||
{
|
{
|
||||||
Blob data(objectData_, objectData_ + dataBytes_);
|
Blob data(objectData_, objectData_ + dataBytes_);
|
||||||
|
|
||||||
object = NodeObject::createObject(objectType_, std::move(data), UInt256::fromVoid(key_));
|
object = NodeObject::createObject(objectType_, std::move(data), key_);
|
||||||
}
|
}
|
||||||
|
|
||||||
return object;
|
return object;
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <span>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -216,7 +217,7 @@ public:
|
|||||||
[&hash, pno, &status](void const* data, std::size_t size) {
|
[&hash, pno, &status](void const* data, std::size_t size) {
|
||||||
nudb::detail::buffer bf;
|
nudb::detail::buffer bf;
|
||||||
auto const result = nodeobjectDecompress(data, size, bf);
|
auto const result = nodeobjectDecompress(data, size, bf);
|
||||||
DecodedBlob decoded(hash.data(), result.first, result.second);
|
DecodedBlob decoded(hash, result.first, result.second);
|
||||||
if (!decoded.wasOk())
|
if (!decoded.wasOk())
|
||||||
{
|
{
|
||||||
status = Status::DataCorrupt;
|
status = Status::DataCorrupt;
|
||||||
@@ -240,7 +241,7 @@ public:
|
|||||||
nudb::error_code ec;
|
nudb::error_code ec;
|
||||||
nudb::detail::buffer bf;
|
nudb::detail::buffer bf;
|
||||||
auto const result = nodeobjectCompress(e.getData(), e.getSize(), bf);
|
auto const result = nodeobjectCompress(e.getData(), e.getSize(), bf);
|
||||||
db.insert(e.getKey(), result.first, result.second, ec);
|
db.insert(e.getKey().data(), result.first, result.second, ec);
|
||||||
if (ec && ec != nudb::error::key_exists)
|
if (ec && ec != nudb::error::key_exists)
|
||||||
Throw<nudb::system_error>(ec);
|
Throw<nudb::system_error>(ec);
|
||||||
}
|
}
|
||||||
@@ -288,14 +289,25 @@ public:
|
|||||||
Throw<nudb::system_error>(ec);
|
Throw<nudb::system_error>(ec);
|
||||||
nudb::visit(
|
nudb::visit(
|
||||||
dp,
|
dp,
|
||||||
[&](void const* key,
|
[&](void const* keyData,
|
||||||
std::size_t keyBytes,
|
std::size_t keyBytes,
|
||||||
void const* data,
|
void const* data,
|
||||||
std::size_t size,
|
std::size_t size,
|
||||||
nudb::error_code&) {
|
nudb::error_code&) {
|
||||||
|
auto key = uint256::fromRaw(
|
||||||
|
std::span{static_cast<unsigned char const*>(keyData), keyBytes});
|
||||||
|
|
||||||
|
if (!key) [[unlikely]]
|
||||||
|
{
|
||||||
|
ec = make_error_code(nudb::error::invalid_key_size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
nudb::detail::buffer bf;
|
nudb::detail::buffer bf;
|
||||||
auto const result = nodeobjectDecompress(data, size, bf);
|
auto const result = nodeobjectDecompress(data, size, bf);
|
||||||
|
|
||||||
DecodedBlob decoded(key, result.first, result.second);
|
DecodedBlob decoded(key, result.first, result.second);
|
||||||
|
|
||||||
if (!decoded.wasOk())
|
if (!decoded.wasOk())
|
||||||
{
|
{
|
||||||
ec = make_error_code(nudb::error::missing_value);
|
ec = make_error_code(nudb::error::missing_value);
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
#include <format>
|
#include <format>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <span>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -111,6 +112,9 @@ public:
|
|||||||
if (!getIfExists(keyValues, Keys::kPath, name))
|
if (!getIfExists(keyValues, Keys::kPath, name))
|
||||||
Throw<std::runtime_error>("Missing path in RocksDBFactory backend");
|
Throw<std::runtime_error>("Missing path in RocksDBFactory backend");
|
||||||
|
|
||||||
|
if (keyBytes != uint256::size())
|
||||||
|
Throw<std::runtime_error>("Incorrect key size: expected 32 bytes");
|
||||||
|
|
||||||
rocksdb::BlockBasedTableOptions tableOptions;
|
rocksdb::BlockBasedTableOptions tableOptions;
|
||||||
options.env = env;
|
options.env = env;
|
||||||
|
|
||||||
@@ -292,7 +296,7 @@ public:
|
|||||||
|
|
||||||
if (getStatus.ok())
|
if (getStatus.ok())
|
||||||
{
|
{
|
||||||
DecodedBlob decoded(hash.data(), string.data(), string.size());
|
DecodedBlob decoded(hash, string.data(), string.size());
|
||||||
|
|
||||||
if (decoded.wasOk())
|
if (decoded.wasOk())
|
||||||
{
|
{
|
||||||
@@ -317,8 +321,8 @@ public:
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
status = static_cast<Status>(
|
status = checkedCast<Status>(
|
||||||
static_cast<int>(Status::CustomCode) + unsafeCast<int>(getStatus.code()));
|
safeCast<int>(Status::CustomCode) + safeCast<int>(getStatus.code()));
|
||||||
|
|
||||||
JLOG(journal.error()) << getStatus.ToString();
|
JLOG(journal.error()) << getStatus.ToString();
|
||||||
}
|
}
|
||||||
@@ -347,7 +351,7 @@ public:
|
|||||||
EncodedBlob const encoded(e);
|
EncodedBlob const encoded(e);
|
||||||
|
|
||||||
wb.Put(
|
wb.Put(
|
||||||
rocksdb::Slice(reinterpret_cast<char const*>(encoded.getKey()), keyBytes),
|
rocksdb::Slice(reinterpret_cast<char const*>(encoded.getKey().data()), keyBytes),
|
||||||
rocksdb::Slice(
|
rocksdb::Slice(
|
||||||
reinterpret_cast<char const*>(encoded.getData()), encoded.getSize()));
|
reinterpret_cast<char const*>(encoded.getData()), encoded.getSize()));
|
||||||
}
|
}
|
||||||
@@ -375,9 +379,12 @@ public:
|
|||||||
|
|
||||||
for (it->SeekToFirst(); it->Valid(); it->Next())
|
for (it->SeekToFirst(); it->Valid(); it->Next())
|
||||||
{
|
{
|
||||||
if (it->key().size() == keyBytes)
|
if (it->key().size() == keyBytes && it->key().size() == uint256::size())
|
||||||
{
|
{
|
||||||
DecodedBlob decoded(it->key().data(), it->value().data(), it->value().size());
|
DecodedBlob decoded(
|
||||||
|
uint256::fromRaw(std::span{it->key().data(), it->key().size()}),
|
||||||
|
it->value().data(),
|
||||||
|
it->value().size());
|
||||||
|
|
||||||
if (decoded.wasOk())
|
if (decoded.wasOk())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
#include <xrpl/protocol/digest.h>
|
#include <xrpl/protocol/digest.h>
|
||||||
#include <xrpl/protocol/tokens.h>
|
#include <xrpl/protocol/tokens.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -19,95 +21,143 @@
|
|||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
namespace detail {
|
namespace {
|
||||||
|
|
||||||
/**
|
/** If true, we implement a cache for account ID lookups.
|
||||||
* Caches the base58 representations of AccountIDs
|
|
||||||
|
This is generally a good idea and can improve performance, especially
|
||||||
|
when serving client requests.
|
||||||
|
|
||||||
|
The cache may generate warnings under some sanitizers; if you want to
|
||||||
|
disable caching, set this variable to false.
|
||||||
*/
|
*/
|
||||||
class AccountIdCache
|
constexpr bool cacheAccountIDs = true;
|
||||||
|
|
||||||
|
/** Represents a cached entry for the given AccountID.
|
||||||
|
|
||||||
|
We keep the size and alignment of the structure at 64 bytes to
|
||||||
|
make sure it fits within a cache line and avoid false sharing.
|
||||||
|
*/
|
||||||
|
struct alignas(64) CachedAccountID
|
||||||
{
|
{
|
||||||
private:
|
/** The sequence lock for the entry.
|
||||||
struct CachedAccountID
|
|
||||||
{
|
|
||||||
AccountID id;
|
|
||||||
char encoding[40] = {0};
|
|
||||||
};
|
|
||||||
|
|
||||||
// The actual cache
|
An even value means that the data is stable, whereas an odd
|
||||||
std::vector<CachedAccountID> cache_;
|
value indicates the data is being actively modified.
|
||||||
|
|
||||||
// We use a hash function designed to resist algorithmic complexity attacks
|
We use a 32-bit value to ensure that the counter will never
|
||||||
HardenedHash<> hasher_;
|
return to an observed value during a read window.
|
||||||
|
*/
|
||||||
|
std::atomic<std::uint32_t> seq{0};
|
||||||
|
|
||||||
// 64 spinlocks, packed into a single 64-bit value
|
/** The account id. */
|
||||||
std::atomic<std::uint64_t> locks_ = 0;
|
AccountID id{};
|
||||||
|
|
||||||
public:
|
/** The length of the cached encoding.
|
||||||
AccountIdCache(std::size_t count) : cache_(count)
|
|
||||||
{
|
|
||||||
// This is non-binding, but we try to avoid wasting memory that
|
|
||||||
// is caused by overallocation.
|
|
||||||
cache_.shrink_to_fit();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
This field disambiguates collisions between ACCOUNT_ZERO and
|
||||||
toBase58(AccountID const& id)
|
never-written-to slots, since they would otherwise share the
|
||||||
{
|
same encoded bits.
|
||||||
auto const index = hasher_(id) % cache_.size();
|
*/
|
||||||
|
std::uint8_t len = 0;
|
||||||
|
|
||||||
PackedSpinlock sl(locks_, index % 64);
|
/** The actual string encoding. */
|
||||||
|
char encoding[39] = {0};
|
||||||
{
|
|
||||||
std::scoped_lock const lock(sl);
|
|
||||||
|
|
||||||
// The check against the first character of the encoding ensures
|
|
||||||
// that we don't mishandle the case of the all-zero account:
|
|
||||||
if (cache_[index].encoding[0] != 0 && cache_[index].id == id)
|
|
||||||
return cache_[index].encoding;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ret = encodeBase58Token(TokenType::AccountID, id.data(), id.size());
|
|
||||||
|
|
||||||
XRPL_ASSERT(ret.size() <= 38, "xrpl::detail::AccountIdCache : maximum result size");
|
|
||||||
|
|
||||||
{
|
|
||||||
std::scoped_lock const lock(sl);
|
|
||||||
cache_[index].id = id;
|
|
||||||
std::strcpy(cache_[index].encoding, ret.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace detail
|
static_assert(sizeof(CachedAccountID) == 64 && alignof(CachedAccountID) == 64);
|
||||||
|
|
||||||
static std::unique_ptr<detail::AccountIdCache> gAccountIdCache;
|
// The actual cache to use: a simple, direct-mapped, best-effort construct.
|
||||||
|
//
|
||||||
|
// We do not use a hardened hash for this because an AccountID is already
|
||||||
|
// just cryptographically generated (i.e. uniformly distributed) bits and
|
||||||
|
// there is no amplification, since collisions do not carry an additional
|
||||||
|
// cost.
|
||||||
|
//
|
||||||
|
// The size is a "nice" power of 2 because it makes the calculation of an
|
||||||
|
// index easier.
|
||||||
|
constinit std::array<CachedAccountID, 65536> gAccountIdCache{};
|
||||||
|
|
||||||
void
|
} // namespace
|
||||||
initAccountIdCache(std::size_t count)
|
|
||||||
{
|
|
||||||
if (!gAccountIdCache && count != 0)
|
|
||||||
gAccountIdCache = std::make_unique<detail::AccountIdCache>(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
toBase58(AccountID const& v)
|
toBase58(AccountID const& v)
|
||||||
{
|
{
|
||||||
if (gAccountIdCache)
|
// Since the input bits are generated by a cryptographically secure
|
||||||
return gAccountIdCache->toBase58(v);
|
// hash construct, we can just pick any subset of them, no muss, no
|
||||||
|
// fuss.
|
||||||
|
auto const index = [&v]() -> std::uint16_t {
|
||||||
|
auto const data = v.data();
|
||||||
|
|
||||||
return encodeBase58Token(TokenType::AccountID, v.data(), v.size());
|
return static_cast<std::uint16_t>(data[0]) | (static_cast<std::uint16_t>(data[1]) << 8);
|
||||||
|
}();
|
||||||
|
|
||||||
|
if constexpr (cacheAccountIDs)
|
||||||
|
{
|
||||||
|
auto& slot = gAccountIdCache[index];
|
||||||
|
|
||||||
|
// This is the optimistic, store-free read path: a collision with a
|
||||||
|
// concurrent writer will be detected because the seq recheck fails
|
||||||
|
// and we fall through to the slow path.
|
||||||
|
//
|
||||||
|
// These unsynchronized reads of the slot fields are, formally, data
|
||||||
|
// races and, technically speaking, result in undefined behavior and
|
||||||
|
// a tool like TSAN might flag them. These should not have an impact
|
||||||
|
// on the correctness of the returned data: the sequence lock checks
|
||||||
|
// ensure we discard any data that was in flux.
|
||||||
|
if (auto const s0 = slot.seq.load(std::memory_order::acquire);
|
||||||
|
(s0 & 1) == 0 && slot.len != 0 && slot.id == v)
|
||||||
|
{
|
||||||
|
char buf[38];
|
||||||
|
auto const len = std::min<std::size_t>(slot.len, sizeof(buf));
|
||||||
|
std::memcpy(buf, slot.encoding, len);
|
||||||
|
|
||||||
|
// Prevents the loads above from sinking below the recheck; the
|
||||||
|
// acquire on s0 constrains later operations, not earlier ones.
|
||||||
|
std::atomic_thread_fence(std::memory_order::acquire);
|
||||||
|
|
||||||
|
// Everything read from the slot — the gates above and the bytes in
|
||||||
|
// buf — precedes this recheck and is validated by it. No slot access
|
||||||
|
// may follow it.
|
||||||
|
if (slot.seq.load(std::memory_order::relaxed) == s0)
|
||||||
|
return std::string(buf, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ret = encodeBase58Token(TokenType::AccountID, v.data(), v.size());
|
||||||
|
|
||||||
|
XRPL_ASSERT(!ret.empty() && ret.size() <= 38, "xrpl::toBase58 : maximum result size");
|
||||||
|
|
||||||
|
if constexpr (cacheAccountIDs)
|
||||||
|
{
|
||||||
|
auto& slot = gAccountIdCache[index];
|
||||||
|
|
||||||
|
// The CAS to an odd value excludes other writers; if it fails, another
|
||||||
|
// thread wrote or is writing into this slot. That is not a problem: we
|
||||||
|
// just don't store our result into the cache this time.
|
||||||
|
if (auto s0 = slot.seq.load(std::memory_order::relaxed); (s0 & 1) == 0 &&
|
||||||
|
slot.seq.compare_exchange_strong(s0, s0 + 1, std::memory_order::acquire))
|
||||||
|
{
|
||||||
|
slot.id = v;
|
||||||
|
slot.len = static_cast<std::uint8_t>(ret.size());
|
||||||
|
std::copy_n(ret.data(), ret.size(), slot.encoding);
|
||||||
|
|
||||||
|
// The acquire on the CAS above keeps the writes from becoming visible
|
||||||
|
// before the seqlock transitions to an odd value, which means readers
|
||||||
|
// can detect an in-progress write. This release publishes them before
|
||||||
|
// the seqlock transitions to an even value.
|
||||||
|
slot.seq.store(s0 + 2, std::memory_order::release);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
std::optional<AccountID>
|
std::optional<AccountID>
|
||||||
parseBase58(std::string const& s)
|
parseBase58(std::string const& s)
|
||||||
{
|
{
|
||||||
auto const result = decodeBase58Token(s, TokenType::AccountID);
|
return AccountID::fromRaw(decodeBase58Token(s, TokenType::AccountID));
|
||||||
if (result.size() != AccountID::kBytes)
|
|
||||||
return std::nullopt;
|
|
||||||
return AccountID::fromRaw(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -148,25 +198,9 @@ parseBase58(std::string const& s)
|
|||||||
AccountID
|
AccountID
|
||||||
calcAccountID(PublicKey const& pk)
|
calcAccountID(PublicKey const& pk)
|
||||||
{
|
{
|
||||||
static_assert(AccountID::kBytes == sizeof(RipeshaHasher::result_type));
|
|
||||||
|
|
||||||
RipeshaHasher rsh;
|
RipeshaHasher rsh;
|
||||||
rsh(pk.data(), pk.size());
|
rsh(pk.data(), pk.size());
|
||||||
return AccountID::fromRaw(static_cast<RipeshaHasher::result_type>(rsh));
|
return AccountID{static_cast<RipeshaHasher::result_type>(rsh)};
|
||||||
}
|
|
||||||
|
|
||||||
AccountID const&
|
|
||||||
xrpAccount()
|
|
||||||
{
|
|
||||||
static AccountID const kAccount(beast::kZero);
|
|
||||||
return kAccount;
|
|
||||||
}
|
|
||||||
|
|
||||||
AccountID const&
|
|
||||||
noAccount()
|
|
||||||
{
|
|
||||||
static AccountID const kAccount(1);
|
|
||||||
return kAccount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|||||||
@@ -185,8 +185,9 @@ getBookBase(Book const& book)
|
|||||||
UInt256
|
UInt256
|
||||||
getQualityNext(UInt256 const& uBase)
|
getQualityNext(UInt256 const& uBase)
|
||||||
{
|
{
|
||||||
static constexpr UInt256 kNextQuality(
|
static constexpr UInt256 kNextQuality{
|
||||||
"0000000000000000000000000000000000000000000000010000000000000000");
|
"0000000000000000000000000000000000000000000000010000000000000000"};
|
||||||
|
|
||||||
return uBase + kNextQuality;
|
return uBase + kNextQuality;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,9 +430,9 @@ payChannel(AccountID const& src, AccountID const& dst, SeqProxy const& seq) noex
|
|||||||
Keylet
|
Keylet
|
||||||
nftokenPageMin(AccountID const& owner)
|
nftokenPageMin(AccountID const& owner)
|
||||||
{
|
{
|
||||||
std::array<std::uint8_t, 32> buf{};
|
std::array<std::uint8_t, UInt256::size()> buf{};
|
||||||
std::memcpy(buf.data(), owner.data(), owner.size());
|
std::ranges::copy(owner, buf.begin());
|
||||||
return {ltNFTOKEN_PAGE, UInt256::fromRaw(buf)};
|
return {ltNFTOKEN_PAGE, UInt256{buf}};
|
||||||
}
|
}
|
||||||
|
|
||||||
Keylet
|
Keylet
|
||||||
|
|||||||
@@ -295,11 +295,9 @@ verify(PublicKey const& publicKey, Slice const& m, Slice const& sig) noexcept
|
|||||||
NodeID
|
NodeID
|
||||||
calcNodeID(PublicKey const& pk)
|
calcNodeID(PublicKey const& pk)
|
||||||
{
|
{
|
||||||
static_assert(NodeID::kBytes == sizeof(RipeshaHasher::result_type));
|
|
||||||
|
|
||||||
RipeshaHasher h;
|
RipeshaHasher h;
|
||||||
h(pk.data(), pk.size());
|
h(pk.data(), pk.size());
|
||||||
return NodeID::fromRaw(static_cast<RipeshaHasher::result_type>(h));
|
return NodeID{static_cast<RipeshaHasher::result_type>(h)};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace xrpl
|
} // namespace xrpl
|
||||||
@@ -148,12 +148,12 @@ STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Issue issue;
|
Issue issue;
|
||||||
issue.currency = sit.get160();
|
issue.currency = Currency{sit.get160()};
|
||||||
|
|
||||||
if (isXRP(issue.currency))
|
if (isXRP(issue.currency))
|
||||||
Throw<std::runtime_error>("invalid native currency");
|
Throw<std::runtime_error>("invalid native currency");
|
||||||
|
|
||||||
issue.account = sit.get160();
|
issue.account = AccountID{sit.get160()};
|
||||||
|
|
||||||
if (isXRP(issue.account))
|
if (isXRP(issue.account))
|
||||||
Throw<std::runtime_error>("invalid native account");
|
Throw<std::runtime_error>("invalid native account");
|
||||||
|
|||||||
@@ -19,16 +19,16 @@ STCurrency::STCurrency(SField const& name) : STBase{name}
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
STCurrency::STCurrency(SerialIter& sit, SField const& name) : STBase{name}
|
|
||||||
{
|
|
||||||
currency_ = sit.get160();
|
|
||||||
}
|
|
||||||
|
|
||||||
STCurrency::STCurrency(SField const& name, Currency const& currency)
|
STCurrency::STCurrency(SField const& name, Currency const& currency)
|
||||||
: STBase{name}, currency_{currency}
|
: STBase{name}, currency_{currency}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
STCurrency::STCurrency(SerialIter& sit, SField const& name)
|
||||||
|
: STCurrency{name, Currency{sit.get160()}}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
SerializedTypeID
|
SerializedTypeID
|
||||||
STCurrency::getSType() const
|
STCurrency::getSType() const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,48 +28,38 @@ STIssue::STIssue(SField const& name) : STBase{name}
|
|||||||
|
|
||||||
STIssue::STIssue(SerialIter& sit, SField const& name) : STBase{name}
|
STIssue::STIssue(SerialIter& sit, SField const& name) : STBase{name}
|
||||||
{
|
{
|
||||||
|
// Either the currency of an Issue or the issuer of an MPT; the
|
||||||
|
// following 160 bits disambiguate.
|
||||||
auto const currencyOrAccount = sit.get160();
|
auto const currencyOrAccount = sit.get160();
|
||||||
|
|
||||||
if (isXRP(Currency::fromRaw(currencyOrAccount)))
|
if (isXRP(Currency{currencyOrAccount}))
|
||||||
{
|
{
|
||||||
asset_ = xrpIssue();
|
asset_ = xrpIssue();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// Check if MPT
|
|
||||||
else
|
// MPT is serialized as:
|
||||||
|
// - 160 bits MPT issuer account
|
||||||
|
// - 160 bits black hole account
|
||||||
|
// - 32 bits sequence
|
||||||
|
AccountID const account{sit.get160()};
|
||||||
|
|
||||||
|
// MPT
|
||||||
|
if (noAccount() == account)
|
||||||
{
|
{
|
||||||
// MPT is serialized as:
|
// MPT: issuer (160), noAccount() (160), sequence (32). The sequence
|
||||||
// - 160 bits MPT issuer account
|
// is stored in little-endian and get32() reads big-endian, so
|
||||||
// - 160 bits black hole account
|
// reverse it before building the canonical ID.
|
||||||
// - 32 bits sequence
|
asset_ = MPTIssue{std::byteswap(sit.get32()), AccountID{currencyOrAccount}};
|
||||||
AccountID const account = AccountID::fromRaw(sit.get160());
|
return;
|
||||||
// MPT
|
|
||||||
if (noAccount() == account)
|
|
||||||
{
|
|
||||||
MPTID mptID;
|
|
||||||
std::uint32_t sequence = sit.get32();
|
|
||||||
// MPTID stores the sequence in canonical big-endian bytes. STIssue
|
|
||||||
// ledger bytes are the legacy LE-host encoding, so convert the
|
|
||||||
// native get32() value to LE bytes before copying into the MPTID.
|
|
||||||
sequence = boost::endian::native_to_little(sequence);
|
|
||||||
static_assert(MPTID::size() == sizeof(sequence) + sizeof(currencyOrAccount));
|
|
||||||
memcpy(mptID.data(), &sequence, sizeof(sequence));
|
|
||||||
memcpy(
|
|
||||||
mptID.data() + sizeof(sequence),
|
|
||||||
currencyOrAccount.data(),
|
|
||||||
sizeof(currencyOrAccount));
|
|
||||||
MPTIssue const issue{mptID};
|
|
||||||
asset_ = issue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Issue issue;
|
|
||||||
issue.currency = currencyOrAccount;
|
|
||||||
issue.account = account;
|
|
||||||
if (!isConsistent(issue))
|
|
||||||
Throw<std::runtime_error>("invalid issue: currency and account native mismatch");
|
|
||||||
asset_ = issue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Issue const issue{Currency{currencyOrAccount}, account};
|
||||||
|
|
||||||
|
if (!isConsistent(issue))
|
||||||
|
Throw<std::runtime_error>("invalid issue: currency and account native mismatch");
|
||||||
|
|
||||||
|
asset_ = issue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SerializedTypeID
|
SerializedTypeID
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <xrpl/basics/UnorderedContainers.h>
|
#include <xrpl/basics/UnorderedContainers.h>
|
||||||
#include <xrpl/basics/base_uint.h>
|
#include <xrpl/basics/base_uint.h>
|
||||||
#include <xrpl/basics/contract.h>
|
#include <xrpl/basics/contract.h>
|
||||||
|
#include <xrpl/basics/safe_cast.h>
|
||||||
#include <xrpl/beast/hash/uhash.h>
|
#include <xrpl/beast/hash/uhash.h>
|
||||||
#include <xrpl/beast/utility/instrumentation.h>
|
#include <xrpl/beast/utility/instrumentation.h>
|
||||||
#include <xrpl/json/json_value.h>
|
#include <xrpl/json/json_value.h>
|
||||||
@@ -17,9 +18,13 @@
|
|||||||
#include <xrpl/protocol/jss.h>
|
#include <xrpl/protocol/jss.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <concepts>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -36,10 +41,10 @@ STPathElement::getHash(STPathElement const& element)
|
|||||||
// important. We don't even really need to fully hash the whole
|
// important. We don't even really need to fully hash the whole
|
||||||
// base_uint here, as a few bytes would do for our use.
|
// base_uint here, as a few bytes would do for our use.
|
||||||
//
|
//
|
||||||
// The note above is only true because the result of this function reaches
|
// The note above is only true because the result of this function is only
|
||||||
// nothing but STPathElement::operator==, where it is a fast-reject
|
// used by the STPathElement equality operator as a fast-reject prefilter,
|
||||||
// prefilter ahead of the field comparisons that decide the answer. Do not
|
// ahead of the field comparisons that then decide the answer. Do not use
|
||||||
// use it to key a container.
|
// it to key a container.
|
||||||
|
|
||||||
for (auto const x : element.getAccountID())
|
for (auto const x : element.getAccountID())
|
||||||
hashAccount += (hashAccount * 257) ^ x;
|
hashAccount += (hashAccount * 257) ^ x;
|
||||||
@@ -106,12 +111,38 @@ STPathSet::operator=(STPathSet const& other)
|
|||||||
|
|
||||||
STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name)
|
STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name)
|
||||||
{
|
{
|
||||||
std::vector<STPathElement> path;
|
using enum STPathElement::Type;
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
int const iType = sit.get8();
|
|
||||||
|
|
||||||
if (iType == STPathElement::TypeNone || iType == STPathElement::TypeBoundary)
|
auto parsePathElementType = [](std::underlying_type_t<STPathElement::Type> byte) noexcept
|
||||||
|
-> std::optional<STPathElement::Type> {
|
||||||
|
if (byte == std::to_underlying(TypeBoundary))
|
||||||
|
return TypeBoundary;
|
||||||
|
|
||||||
|
constexpr auto knownBits = static_cast<std::underlying_type_t<STPathElement::Type>>(
|
||||||
|
std::to_underlying(TypeAccount) | std::to_underlying(TypeCurrency) |
|
||||||
|
std::to_underlying(TypeIssuer) | std::to_underlying(TypeMpt));
|
||||||
|
|
||||||
|
if ((byte & ~knownBits) != 0)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return static_cast<STPathElement::Type>(byte);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<STPathElement> path;
|
||||||
|
|
||||||
|
std::optional<STPathElement::Type> type;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
type = parsePathElementType(sit.get8());
|
||||||
|
|
||||||
|
if (!type)
|
||||||
|
{
|
||||||
|
JLOG(debugLog().error()) << "Bad path element in pathset";
|
||||||
|
Throw<std::runtime_error>("bad path element");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == TypeNone || type == TypeBoundary)
|
||||||
{
|
{
|
||||||
if (path.empty())
|
if (path.empty())
|
||||||
{
|
{
|
||||||
@@ -122,28 +153,11 @@ STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name)
|
|||||||
// Move rather than converting the vector to an STPath by copy.
|
// Move rather than converting the vector to an STPath by copy.
|
||||||
value_.emplace_back(std::move(path));
|
value_.emplace_back(std::move(path));
|
||||||
path.clear();
|
path.clear();
|
||||||
|
|
||||||
if (iType == STPathElement::TypeNone)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if ((iType & ~STPathElement::TypeAll) != 0)
|
|
||||||
{
|
|
||||||
JLOG(debugLog().error()) << "Bad path element " << iType << " in pathset";
|
|
||||||
Throw<std::runtime_error>("bad path element");
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto const hasAccount = (iType & STPathElement::TypeAccount) != 0u;
|
bool const hasCurrency = ((*type & TypeCurrency) == TypeCurrency);
|
||||||
auto const hasCurrency = (iType & STPathElement::TypeCurrency) != 0u;
|
bool const hasMPT = ((*type & TypeMpt) == TypeMpt);
|
||||||
auto const hasIssuer = (iType & STPathElement::TypeIssuer) != 0u;
|
|
||||||
auto const hasMPT = (iType & STPathElement::TypeMpt) != 0u;
|
|
||||||
|
|
||||||
AccountID account;
|
|
||||||
PathAsset asset;
|
|
||||||
AccountID issuer;
|
|
||||||
|
|
||||||
if (hasAccount)
|
|
||||||
account = sit.get160();
|
|
||||||
|
|
||||||
if (hasCurrency && hasMPT)
|
if (hasCurrency && hasMPT)
|
||||||
{
|
{
|
||||||
@@ -151,18 +165,26 @@ STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name)
|
|||||||
Throw<std::runtime_error>("bad path element: MPT and Currency");
|
Throw<std::runtime_error>("bad path element: MPT and Currency");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AccountID account;
|
||||||
|
AccountID issuer;
|
||||||
|
|
||||||
|
if ((*type & TypeAccount) == TypeAccount)
|
||||||
|
account = AccountID{sit.get160()};
|
||||||
|
|
||||||
|
PathAsset asset;
|
||||||
|
|
||||||
if (hasCurrency)
|
if (hasCurrency)
|
||||||
asset = Currency::fromRaw(sit.get160());
|
asset = Currency{sit.get160()};
|
||||||
|
|
||||||
if (hasMPT)
|
if (hasMPT)
|
||||||
asset = sit.get192();
|
asset = sit.get192();
|
||||||
|
|
||||||
if (hasIssuer)
|
if ((*type & TypeIssuer) == TypeIssuer)
|
||||||
issuer = sit.get160();
|
issuer = AccountID{sit.get160()};
|
||||||
|
|
||||||
path.emplace_back(account, asset, issuer, hasCurrency || hasMPT);
|
path.emplace_back(account, asset, issuer, hasCurrency || hasMPT);
|
||||||
}
|
}
|
||||||
}
|
} while (type != TypeNone);
|
||||||
}
|
}
|
||||||
|
|
||||||
STBase*
|
STBase*
|
||||||
@@ -216,24 +238,22 @@ STPath::getJson(JsonOptions) const
|
|||||||
for (auto const& it : path_)
|
for (auto const& it : path_)
|
||||||
{
|
{
|
||||||
json::Value elem(json::ValueType::Object);
|
json::Value elem(json::ValueType::Object);
|
||||||
auto const iType = it.getNodeType();
|
|
||||||
|
|
||||||
elem[jss::type] = iType;
|
elem[jss::type] = safeCast<std::uint32_t>(it.getNodeType());
|
||||||
|
|
||||||
if ((iType & STPathElement::TypeAccount) != 0u)
|
if (it.isType(STPathElement::TypeAccount))
|
||||||
elem[jss::account] = to_string(it.getAccountID());
|
elem[jss::account] = to_string(it.getAccountID());
|
||||||
|
|
||||||
XRPL_ASSERT(
|
XRPL_ASSERT(
|
||||||
((iType & STPathElement::TypeCurrency) == 0u) ||
|
!(it.hasCurrency() && it.hasMPT()),
|
||||||
((iType & STPathElement::TypeMpt) == 0u),
|
|
||||||
"xrpl::STPath::getJson : not type Currency and MPT");
|
"xrpl::STPath::getJson : not type Currency and MPT");
|
||||||
if ((iType & STPathElement::TypeCurrency) != 0u)
|
if (it.hasCurrency())
|
||||||
elem[jss::currency] = to_string(it.getCurrency());
|
elem[jss::currency] = to_string(it.getCurrency());
|
||||||
|
|
||||||
if ((iType & STPathElement::TypeMpt) != 0u)
|
if (it.hasMPT())
|
||||||
elem[jss::mpt_issuance_id] = to_string(it.getMPTID());
|
elem[jss::mpt_issuance_id] = to_string(it.getMPTID());
|
||||||
|
|
||||||
if ((iType & STPathElement::TypeIssuer) != 0u)
|
if (it.hasIssuer())
|
||||||
elem[jss::issuer] = to_string(it.getIssuerID());
|
elem[jss::issuer] = to_string(it.getIssuerID());
|
||||||
|
|
||||||
ret.append(elem);
|
ret.append(elem);
|
||||||
@@ -265,34 +285,42 @@ STPathSet::add(Serializer& s) const
|
|||||||
XRPL_ASSERT(getFName().fieldType == STI_PATHSET, "xrpl::STPathSet::add : valid field type");
|
XRPL_ASSERT(getFName().fieldType == STI_PATHSET, "xrpl::STPathSet::add : valid field type");
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
||||||
|
// Workaround, since `Serializer` does not accept strongly-typed enums
|
||||||
|
// yet. When support is added, the static_assert will trigger, and the
|
||||||
|
// lambda can be removed entirely.
|
||||||
|
auto const toByte = [](std::same_as<STPathElement::Type> auto t) noexcept {
|
||||||
|
static_assert(
|
||||||
|
!requires(Serializer& ser) { ser.add8(t); },
|
||||||
|
"Serializer::add8 now accepts STPathElement::Type directly; remove toByte.");
|
||||||
|
return std::to_underlying(t);
|
||||||
|
};
|
||||||
|
|
||||||
for (auto const& spPath : value_)
|
for (auto const& spPath : value_)
|
||||||
{
|
{
|
||||||
if (!first)
|
if (!first)
|
||||||
s.add8(STPathElement::TypeBoundary);
|
s.add8(toByte(STPathElement::TypeBoundary));
|
||||||
|
|
||||||
for (auto const& speElement : spPath)
|
for (auto const& speElement : spPath)
|
||||||
{
|
{
|
||||||
int const iType = speElement.getNodeType();
|
s.add8(toByte(speElement.getNodeType()));
|
||||||
|
|
||||||
s.add8(iType);
|
if (speElement.isType(STPathElement::TypeAccount))
|
||||||
|
|
||||||
if ((iType & STPathElement::TypeAccount) != 0u)
|
|
||||||
s.addBitString(speElement.getAccountID());
|
s.addBitString(speElement.getAccountID());
|
||||||
|
|
||||||
if ((iType & STPathElement::TypeMpt) != 0u)
|
if (speElement.hasMPT())
|
||||||
s.addBitString(speElement.getMPTID());
|
s.addBitString(speElement.getMPTID());
|
||||||
|
|
||||||
if ((iType & STPathElement::TypeCurrency) != 0u)
|
if (speElement.hasCurrency())
|
||||||
s.addBitString(speElement.getCurrency());
|
s.addBitString(speElement.getCurrency());
|
||||||
|
|
||||||
if ((iType & STPathElement::TypeIssuer) != 0u)
|
if (speElement.hasIssuer())
|
||||||
s.addBitString(speElement.getIssuerID());
|
s.addBitString(speElement.getIssuerID());
|
||||||
}
|
}
|
||||||
|
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
s.add8(STPathElement::TypeNone);
|
s.add8(toByte(STPathElement::TypeNone));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace xrpl
|
} // namespace xrpl
|
||||||
@@ -9,7 +9,9 @@
|
|||||||
#include <xrpl/protocol/Serializer.h>
|
#include <xrpl/protocol/Serializer.h>
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
#include <format>
|
#include <format>
|
||||||
|
#include <span>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
@@ -17,7 +19,7 @@ namespace xrpl {
|
|||||||
|
|
||||||
STVector256::STVector256(SerialIter& sit, SField const& name) : STBase(name)
|
STVector256::STVector256(SerialIter& sit, SField const& name) : STBase(name)
|
||||||
{
|
{
|
||||||
auto const slice = sit.getSlice(sit.getVLDataLength());
|
auto slice = sit.getSlice(sit.getVLDataLength());
|
||||||
|
|
||||||
if (slice.size() % UInt256::size() != 0)
|
if (slice.size() % UInt256::size() != 0)
|
||||||
{
|
{
|
||||||
@@ -25,12 +27,14 @@ STVector256::STVector256(SerialIter& sit, SField const& name) : STBase(name)
|
|||||||
std::format("Bad serialization for STVector256: {}", slice.size()));
|
std::format("Bad serialization for STVector256: {}", slice.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const cnt = slice.size() / UInt256::size();
|
value_.reserve(slice.size() / UInt256::size());
|
||||||
|
|
||||||
value_.reserve(cnt);
|
while (!slice.empty())
|
||||||
|
{
|
||||||
for (std::size_t i = 0; i != cnt; ++i)
|
value_.emplace_back(
|
||||||
value_.push_back(UInt256::fromRaw(slice.substr(i * UInt256::size(), UInt256::size())));
|
std::span<std::uint8_t const, UInt256::size()>{slice.data(), UInt256::size()});
|
||||||
|
slice += UInt256::size();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
STBase*
|
STBase*
|
||||||
|
|||||||
@@ -104,8 +104,9 @@ parseGenericSeed(std::string const& str, bool rfc1751)
|
|||||||
std::string key;
|
std::string key;
|
||||||
if (RFC1751::getKeyFromEnglish(key, str) == 1)
|
if (RFC1751::getKeyFromEnglish(key, str) == 1)
|
||||||
{
|
{
|
||||||
Blob const blob(key.rbegin(), key.rend());
|
std::array<std::uint8_t, UInt128::size()> bytes{};
|
||||||
return Seed{UInt128::fromRaw(blob)};
|
std::ranges::reverse_copy(key, bytes.begin());
|
||||||
|
return Seed{UInt128{bytes}};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ constexpr std::size_t kIsoCodeLength = 3;
|
|||||||
std::string
|
std::string
|
||||||
to_string(Currency const& currency)
|
to_string(Currency const& currency)
|
||||||
{
|
{
|
||||||
if (currency == beast::kZero)
|
if (currency == xrpCurrency())
|
||||||
return systemCurrencyCode();
|
return systemCurrencyCode();
|
||||||
|
|
||||||
if (currency == noCurrency())
|
if (currency == noCurrency())
|
||||||
@@ -62,7 +62,7 @@ to_string(Currency const& currency)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
toCurrency(Currency& currency, std::string const& code)
|
toCurrency(Currency& currency, std::string_view code)
|
||||||
{
|
{
|
||||||
if (code.empty() || code == systemCurrencyCode())
|
if (code.empty() || code == systemCurrencyCode())
|
||||||
{
|
{
|
||||||
@@ -87,7 +87,7 @@ toCurrency(Currency& currency, std::string const& code)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Currency
|
Currency
|
||||||
toCurrency(std::string const& code)
|
toCurrency(std::string_view code)
|
||||||
{
|
{
|
||||||
Currency currency;
|
Currency currency;
|
||||||
if (!toCurrency(currency, code))
|
if (!toCurrency(currency, code))
|
||||||
@@ -95,25 +95,4 @@ toCurrency(std::string const& code)
|
|||||||
return currency;
|
return currency;
|
||||||
}
|
}
|
||||||
|
|
||||||
Currency const&
|
|
||||||
xrpCurrency()
|
|
||||||
{
|
|
||||||
static Currency const kCurrency(beast::kZero);
|
|
||||||
return kCurrency;
|
|
||||||
}
|
|
||||||
|
|
||||||
Currency const&
|
|
||||||
noCurrency()
|
|
||||||
{
|
|
||||||
static Currency const kCurrency(1);
|
|
||||||
return kCurrency;
|
|
||||||
}
|
|
||||||
|
|
||||||
Currency const&
|
|
||||||
badCurrency()
|
|
||||||
{
|
|
||||||
static Currency const kCurrency(0x5852500000000000);
|
|
||||||
return kCurrency;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace xrpl
|
} // namespace xrpl
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <span>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -129,14 +130,15 @@ deserializeSHAMapNodeID(void const* data, std::size_t size)
|
|||||||
{
|
{
|
||||||
std::optional<SHAMapNodeID> ret;
|
std::optional<SHAMapNodeID> ret;
|
||||||
|
|
||||||
if (size == 33)
|
if (size == UInt256::size() + 1)
|
||||||
{
|
{
|
||||||
unsigned int const depth = *(static_cast<unsigned char const*>(data) + 32);
|
std::span const bytes{static_cast<unsigned char const*>(data), size};
|
||||||
if (depth <= SHAMap::kLeafDepth)
|
|
||||||
|
if (unsigned int const depth = bytes[uint256::size()]; depth <= SHAMap::kLeafDepth)
|
||||||
{
|
{
|
||||||
// Reject a serialized ID carrying bits below its own depth. Checked before
|
// Reject a serialized ID carrying bits below its own depth. Checked before
|
||||||
// constructing, since the constructor asserts that same property.
|
// constructing the node, since the constructor asserts that same property.
|
||||||
if (auto const id = UInt256::fromVoid(data); isPrefixOfAtDepth(id, depth, id))
|
if (uint256 const id{bytes.first<uint256::size()>()}; isPrefixOfAtDepth(id, depth, id))
|
||||||
ret.emplace(depth, id);
|
ret.emplace(depth, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ ApplyContext::ApplyContext(
|
|||||||
, parentBatchId_(parentBatchId)
|
, parentBatchId_(parentBatchId)
|
||||||
{
|
{
|
||||||
XRPL_ASSERT(
|
XRPL_ASSERT(
|
||||||
parentBatchId.has_value() == ((flags_ & TapBatch) == TapBatch),
|
parentBatchId.has_value() == ((flags_ & ApplyFlags::Batch) == ApplyFlags::Batch),
|
||||||
"Parent Batch ID should be set if batch apply flag is set");
|
"Parent Batch ID should be set if batch apply flag is set");
|
||||||
view_.emplace(&base_, flags_);
|
view_.emplace(&base_, flags_);
|
||||||
}
|
}
|
||||||
@@ -53,13 +53,14 @@ std::optional<TxMeta>
|
|||||||
ApplyContext::apply(TER ter)
|
ApplyContext::apply(TER ter)
|
||||||
{
|
{
|
||||||
// NOLINTNEXTLINE(bugprone-unchecked-optional-access) view_ emplaced in constructor
|
// NOLINTNEXTLINE(bugprone-unchecked-optional-access) view_ emplaced in constructor
|
||||||
return view_->apply(base_, tx, ter, parentBatchId_, (flags_ & TapDryRun) != 0u, journal);
|
return view_->apply(
|
||||||
|
base_, tx, ter, parentBatchId_, (flags_ & ApplyFlags::DryRun) != ApplyFlags::None, journal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ApplyContext::addOrderBook(Book const& book)
|
ApplyContext::addOrderBook(Book const& book)
|
||||||
{
|
{
|
||||||
if ((flags_ & TapDryRun) == TapNone)
|
if ((flags_ & ApplyFlags::DryRun) == ApplyFlags::None)
|
||||||
registry.get().getOrderBookDB().addOrderBook(book);
|
registry.get().getOrderBookDB().addOrderBook(book);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ preflightCheckSigningKey(STObject const& sigObject, beast::Journal j)
|
|||||||
std::optional<NotTEC>
|
std::optional<NotTEC>
|
||||||
preflightCheckSimulateKeys(ApplyFlags flags, STObject const& sigObject, beast::Journal j)
|
preflightCheckSimulateKeys(ApplyFlags flags, STObject const& sigObject, beast::Journal j)
|
||||||
{
|
{
|
||||||
if ((flags & TapDryRun) != 0u) // simulation
|
if ((flags & ApplyFlags::DryRun) != ApplyFlags::None) // simulation
|
||||||
{
|
{
|
||||||
std::optional<Slice> const signature = sigObject[~sfTxnSignature];
|
std::optional<Slice> const signature = sigObject[~sfTxnSignature];
|
||||||
if (signature && !signature->empty())
|
if (signature && !signature->empty())
|
||||||
@@ -510,7 +510,8 @@ Transactor::minimumFee(
|
|||||||
Fees const& fees,
|
Fees const& fees,
|
||||||
ApplyFlags flags)
|
ApplyFlags flags)
|
||||||
{
|
{
|
||||||
return scaleFeeLoad(baseFee, registry.getFeeTrack(), fees, (flags & TapUnlimited) != 0u);
|
return scaleFeeLoad(
|
||||||
|
baseFee, registry.getFeeTrack(), fees, (flags & ApplyFlags::Unlimited) != ApplyFlags::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
TER
|
TER
|
||||||
@@ -521,7 +522,7 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee)
|
|||||||
|
|
||||||
auto const feePaid = ctx.tx[sfFee].xrp();
|
auto const feePaid = ctx.tx[sfFee].xrp();
|
||||||
|
|
||||||
if ((ctx.flags & TapBatch) != 0u)
|
if ((ctx.flags & ApplyFlags::Batch) != ApplyFlags::None)
|
||||||
{
|
{
|
||||||
if (feePaid == beast::kZero)
|
if (feePaid == beast::kZero)
|
||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
@@ -951,7 +952,8 @@ Transactor::checkSign(
|
|||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (((flags & TapDryRun) != 0u) && pkSigner.empty() && !sigObject.isFieldPresent(sfSigners))
|
if (((flags & ApplyFlags::DryRun) != ApplyFlags::None) && pkSigner.empty() &&
|
||||||
|
!sigObject.isFieldPresent(sfSigners))
|
||||||
{
|
{
|
||||||
// simulate: skip signature validation when neither SigningPubKey nor
|
// simulate: skip signature validation when neither SigningPubKey nor
|
||||||
// Signers are provided
|
// Signers are provided
|
||||||
@@ -1122,7 +1124,7 @@ Transactor::checkMultiSign(
|
|||||||
}
|
}
|
||||||
|
|
||||||
XRPL_ASSERT(
|
XRPL_ASSERT(
|
||||||
(flags & TapDryRun) || !spk.empty(),
|
(flags & ApplyFlags::DryRun) != ApplyFlags::None || !spk.empty(),
|
||||||
"xrpl::Transactor::checkMultiSign : non-empty signer or "
|
"xrpl::Transactor::checkMultiSign : non-empty signer or "
|
||||||
"simulation");
|
"simulation");
|
||||||
AccountID const signingAcctIDFromPubKey =
|
AccountID const signingAcctIDFromPubKey =
|
||||||
@@ -1604,7 +1606,7 @@ Transactor::operator()()
|
|||||||
if (ctx_.size() > kOversizeMetaDataCap)
|
if (ctx_.size() > kOversizeMetaDataCap)
|
||||||
result = tecOVERSIZE;
|
result = tecOVERSIZE;
|
||||||
|
|
||||||
if (isTecClaim(result) && ((view().flags() & TapFailHard) != 0u))
|
if (isTecClaim(result) && ((view().flags() & ApplyFlags::FailHard) != ApplyFlags::None))
|
||||||
{
|
{
|
||||||
// If the TapFailHard flag is set, a tec result
|
// If the TapFailHard flag is set, a tec result
|
||||||
// must not do anything
|
// must not do anything
|
||||||
@@ -1684,7 +1686,7 @@ Transactor::operator()()
|
|||||||
// Once we call apply, we will no longer be able to look at view()
|
// Once we call apply, we will no longer be able to look at view()
|
||||||
metadata = ctx_.apply(result);
|
metadata = ctx_.apply(result);
|
||||||
|
|
||||||
if ((ctx_.flags() & TapDryRun) != 0u)
|
if ((ctx_.flags() & ApplyFlags::DryRun) != ApplyFlags::None)
|
||||||
return logger(result, false, std::move(metadata));
|
return logger(result, false, std::move(metadata));
|
||||||
|
|
||||||
return logger(result, canApply, std::move(metadata));
|
return logger(result, canApply, std::move(metadata));
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ applyBatchTransactions(
|
|||||||
auto applyOneTransaction = [®istry, &j, &parentBatchId, &batchView](STTx const& tx) {
|
auto applyOneTransaction = [®istry, &j, &parentBatchId, &batchView](STTx const& tx) {
|
||||||
OpenView perTxBatchView(kBatchView, batchView);
|
OpenView perTxBatchView(kBatchView, batchView);
|
||||||
|
|
||||||
auto const ret = apply(registry, perTxBatchView, parentBatchId, tx, TapBatch, j);
|
auto const ret = apply(registry, perTxBatchView, parentBatchId, tx, ApplyFlags::Batch, j);
|
||||||
XRPL_ASSERT(
|
XRPL_ASSERT(
|
||||||
ret.applied == (isTesSuccess(ret.ter) || isTecClaim(ret.ter)),
|
ret.applied == (isTesSuccess(ret.ter) || isTecClaim(ret.ter)),
|
||||||
"Inner transaction should not be applied");
|
"Inner transaction should not be applied");
|
||||||
@@ -274,7 +274,7 @@ applyTransaction(
|
|||||||
{
|
{
|
||||||
// Returns false if the transaction has need not be retried.
|
// Returns false if the transaction has need not be retried.
|
||||||
if (retryAssured)
|
if (retryAssured)
|
||||||
flags = flags | TapRetry;
|
flags = flags | ApplyFlags::Retry;
|
||||||
|
|
||||||
JLOG(j.debug()) << "TXN " << txn.getTransactionID() << (retryAssured ? "/retry" : "/final");
|
JLOG(j.debug()) << "TXN " << txn.getTransactionID() << (retryAssured ? "/retry" : "/final");
|
||||||
|
|
||||||
|
|||||||
@@ -346,7 +346,8 @@ preflight(
|
|||||||
// builds; re-check the same invariant here so a release build can't
|
// builds; re-check the same invariant here so a release build can't
|
||||||
// silently skip a proposed transaction's signature-presence checks
|
// silently skip a proposed transaction's signature-presence checks
|
||||||
// outside of a dry run.
|
// outside of a dry run.
|
||||||
if ((flags & TapProposal) != TapNone && (flags & TapDryRun) == TapNone)
|
if ((flags & ApplyFlags::Proposal) != ApplyFlags::None &&
|
||||||
|
(flags & ApplyFlags::DryRun) == ApplyFlags::None)
|
||||||
{
|
{
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
JLOG(j.fatal()) << "apply (preflight): TapProposal set without TapDryRun.";
|
JLOG(j.fatal()) << "apply (preflight): TapProposal set without TapDryRun.";
|
||||||
@@ -377,7 +378,8 @@ preflight(
|
|||||||
PreflightContext const pfCtx(registry, tx, parentBatchId, rules, flags, j);
|
PreflightContext const pfCtx(registry, tx, parentBatchId, rules, flags, j);
|
||||||
|
|
||||||
// See the comment in the other preflight() overload above.
|
// See the comment in the other preflight() overload above.
|
||||||
if ((flags & TapProposal) != TapNone && (flags & TapDryRun) == TapNone)
|
if ((flags & ApplyFlags::Proposal) != ApplyFlags::None &&
|
||||||
|
(flags & ApplyFlags::DryRun) == ApplyFlags::None)
|
||||||
{
|
{
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
JLOG(j.fatal()) << "apply (preflight): TapProposal set without TapDryRun.";
|
JLOG(j.fatal()) << "apply (preflight): TapProposal set without TapDryRun.";
|
||||||
|
|||||||
@@ -985,7 +985,7 @@ std::optional<std::variant<Quality, AMMOffer<TIn, TOut>>>
|
|||||||
BookStep<TIn, TOut, TDerived>::tip(ReadView const& view) const
|
BookStep<TIn, TOut, TDerived>::tip(ReadView const& view) const
|
||||||
{
|
{
|
||||||
// This can be simplified (and sped up) if directories are never empty.
|
// This can be simplified (and sped up) if directories are never empty.
|
||||||
Sandbox sb(&view, TapNone);
|
Sandbox sb(&view, ApplyFlags::None);
|
||||||
BookTip bt(sb, book_);
|
BookTip bt(sb, book_);
|
||||||
auto const lobQuality = bt.step(j_) ? std::optional<Quality>(bt.quality()) : std::nullopt;
|
auto const lobQuality = bt.step(j_) ? std::optional<Quality>(bt.quality()) : std::nullopt;
|
||||||
// Multi-path offer generates an offer with the quality
|
// Multi-path offer generates an offer with the quality
|
||||||
|
|||||||
@@ -57,10 +57,8 @@ checkNear(IOUAmount const& expected, IOUAmount const& actual)
|
|||||||
static bool
|
static bool
|
||||||
isXRPAccount(STPathElement const& pe)
|
isXRPAccount(STPathElement const& pe)
|
||||||
{
|
{
|
||||||
if (pe.getNodeType() != STPathElement::TypeAccount)
|
return pe.getNodeType() == STPathElement::TypeAccount && isXRP(pe.getAccountID());
|
||||||
return false;
|
}
|
||||||
return isXRP(pe.getAccountID());
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::pair<TER, std::unique_ptr<Step>>
|
static std::pair<TER, std::unique_ptr<Step>>
|
||||||
toStep(
|
toStep(
|
||||||
@@ -71,11 +69,8 @@ toStep(
|
|||||||
{
|
{
|
||||||
auto& j = ctx.j;
|
auto& j = ctx.j;
|
||||||
|
|
||||||
if (ctx.isFirst && e1->isAccount() &&
|
if (ctx.isFirst && e1->isAccount() && e1->hasCurrency() && e1->getPathAsset().isXRP())
|
||||||
((e1->getNodeType() & STPathElement::TypeCurrency) != 0u) && e1->getPathAsset().isXRP())
|
|
||||||
{
|
|
||||||
return makeXrpEndpointStep(ctx, e1->getAccountID());
|
return makeXrpEndpointStep(ctx, e1->getAccountID());
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx.isLast && isXRPAccount(*e1) && e2->isAccount())
|
if (ctx.isLast && isXRPAccount(*e1) && e2->isAccount())
|
||||||
return makeXrpEndpointStep(ctx, e2->getAccountID());
|
return makeXrpEndpointStep(ctx, e2->getAccountID());
|
||||||
@@ -117,15 +112,9 @@ toStep(
|
|||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
}
|
}
|
||||||
|
|
||||||
XRPL_ASSERT(
|
XRPL_ASSERT(e2->hasAsset() || e2->hasIssuer(), "xrpl::toStep : currency or issuer");
|
||||||
(e2->getNodeType() & STPathElement::TypeAsset) ||
|
PathAsset const outAsset = e2->hasAsset() ? e2->getPathAsset() : curAsset;
|
||||||
(e2->getNodeType() & STPathElement::TypeIssuer),
|
auto const outIssuer = e2->hasIssuer() ? e2->getIssuerID() : curAsset.getIssuer();
|
||||||
"xrpl::toStep : currency or issuer");
|
|
||||||
PathAsset const outAsset =
|
|
||||||
((e2->getNodeType() & STPathElement::TypeAsset) != 0u) ? e2->getPathAsset() : curAsset;
|
|
||||||
auto const outIssuer = ((e2->getNodeType() & STPathElement::TypeIssuer) != 0u)
|
|
||||||
? e2->getIssuerID()
|
|
||||||
: curAsset.getIssuer();
|
|
||||||
|
|
||||||
if (isXRP(curAsset) && outAsset.isXRP())
|
if (isXRP(curAsset) && outAsset.isXRP())
|
||||||
{
|
{
|
||||||
@@ -197,16 +186,15 @@ toStrand(
|
|||||||
for (std::size_t i = 0; i < path.size(); ++i)
|
for (std::size_t i = 0; i < path.size(); ++i)
|
||||||
{
|
{
|
||||||
auto const& pe = path[i];
|
auto const& pe = path[i];
|
||||||
auto const t = pe.getNodeType();
|
|
||||||
|
|
||||||
if (((t & ~STPathElement::TypeAll) != 0u) || (t == 0u))
|
if ((pe.getNodeType() & ~STPathElement::TypeAll) != STPathElement::TypeNone || pe.isNone())
|
||||||
return {temBAD_PATH, Strand{}};
|
return {temBAD_PATH, Strand{}};
|
||||||
|
|
||||||
bool const hasAccount = (t & STPathElement::TypeAccount) != 0u;
|
bool const hasAccount = pe.isType(STPathElement::TypeAccount);
|
||||||
bool const hasIssuer = (t & STPathElement::TypeIssuer) != 0u;
|
bool const hasIssuer = pe.hasIssuer();
|
||||||
bool const hasCurrency = (t & STPathElement::TypeCurrency) != 0u;
|
bool const hasCurrency = pe.hasCurrency();
|
||||||
bool const hasMPT = (t & STPathElement::TypeMpt) != 0u;
|
bool const hasMPT = pe.hasMPT();
|
||||||
bool const hasAsset = (t & STPathElement::TypeAsset) != 0u;
|
bool const hasAsset = pe.hasAsset();
|
||||||
|
|
||||||
if (hasAccount && (hasIssuer || hasCurrency))
|
if (hasAccount && (hasIssuer || hasCurrency))
|
||||||
return {temBAD_PATH, Strand{}};
|
return {temBAD_PATH, Strand{}};
|
||||||
@@ -250,9 +238,7 @@ toStrand(
|
|||||||
}();
|
}();
|
||||||
|
|
||||||
// Currency or MPT
|
// Currency or MPT
|
||||||
auto hasAsset = [](STPathElement const pe) {
|
auto hasAsset = [](STPathElement const& pe) { return pe.hasAsset(); };
|
||||||
return pe.getNodeType() & STPathElement::TypeAsset;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<STPathElement> normPath;
|
std::vector<STPathElement> normPath;
|
||||||
// reserve enough for the path, the implied source, destination,
|
// reserve enough for the path, the implied source, destination,
|
||||||
|
|||||||
@@ -187,7 +187,8 @@ AccountSet::preclaim(PreclaimContext const& ctx)
|
|||||||
if (!dirIsEmpty(ctx.view, keylet::ownerDir(id)))
|
if (!dirIsEmpty(ctx.view, keylet::ownerDir(id)))
|
||||||
{
|
{
|
||||||
JLOG(ctx.j.trace()) << "Retry: Owner directory not empty.";
|
JLOG(ctx.j.trace()) << "Retry: Owner directory not empty.";
|
||||||
return ((ctx.flags & TapRetry) != 0u) ? TER{terOWNERS} : TER{tecOWNERS};
|
return ((ctx.flags & ApplyFlags::Retry) != ApplyFlags::None) ? TER{terOWNERS}
|
||||||
|
: TER{tecOWNERS};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ applyVote(ApplyContext& ctx, Sandbox& sb, AccountID const& accountID, beast::Jou
|
|||||||
auto const lpTokensNew = ammLPHolds(sb, *ammSle, accountID, ctx.journal);
|
auto const lpTokensNew = ammLPHolds(sb, *ammSle, accountID, ctx.journal);
|
||||||
std::optional<STAmount> minTokens;
|
std::optional<STAmount> minTokens;
|
||||||
std::size_t minPos{0};
|
std::size_t minPos{0};
|
||||||
AccountID minAccount{0};
|
AccountID minAccount;
|
||||||
std::uint32_t minFee{0};
|
std::uint32_t minFee{0};
|
||||||
STArray updatedVoteSlots;
|
STArray updatedVoteSlots;
|
||||||
Number num{0};
|
Number num{0};
|
||||||
|
|||||||
@@ -295,7 +295,8 @@ OfferCreate::checkAcceptAsset(
|
|||||||
JLOG(j.debug()) << "delay: can't receive IOUs from non-existent issuer: "
|
JLOG(j.debug()) << "delay: can't receive IOUs from non-existent issuer: "
|
||||||
<< to_string(asset.getIssuer());
|
<< to_string(asset.getIssuer());
|
||||||
|
|
||||||
return ((flags & TapRetry) != 0u) ? TER{terNO_ACCOUNT} : TER{tecNO_ISSUER};
|
return ((flags & ApplyFlags::Retry) != ApplyFlags::None) ? TER{terNO_ACCOUNT}
|
||||||
|
: TER{tecNO_ISSUER};
|
||||||
}
|
}
|
||||||
|
|
||||||
// An account cannot create a trustline to itself, so no line can exist
|
// An account cannot create a trustline to itself, so no line can exist
|
||||||
@@ -318,7 +319,8 @@ OfferCreate::checkAcceptAsset(
|
|||||||
{
|
{
|
||||||
JLOG(j.debug()) << "delay: can't receive IOUs from issuer with "
|
JLOG(j.debug()) << "delay: can't receive IOUs from issuer with "
|
||||||
"DisallowIncomingTrustline set";
|
"DisallowIncomingTrustline set";
|
||||||
return ((flags & TapRetry) != 0u) ? TER{terNO_LINE} : TER{tecNO_LINE};
|
return ((flags & ApplyFlags::Retry) != ApplyFlags::None) ? TER{terNO_LINE}
|
||||||
|
: TER{tecNO_LINE};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,7 +328,8 @@ OfferCreate::checkAcceptAsset(
|
|||||||
{
|
{
|
||||||
if (!trustLine)
|
if (!trustLine)
|
||||||
{
|
{
|
||||||
return ((flags & TapRetry) != 0u) ? TER{terNO_LINE} : TER{tecNO_LINE};
|
return ((flags & ApplyFlags::Retry) != ApplyFlags::None) ? TER{terNO_LINE}
|
||||||
|
: TER{tecNO_LINE};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entries have a canonical representation, determined by a
|
// Entries have a canonical representation, determined by a
|
||||||
@@ -342,7 +345,8 @@ OfferCreate::checkAcceptAsset(
|
|||||||
JLOG(j.debug()) << "delay: can't receive IOUs from "
|
JLOG(j.debug()) << "delay: can't receive IOUs from "
|
||||||
"issuer without auth.";
|
"issuer without auth.";
|
||||||
|
|
||||||
return ((flags & TapRetry) != 0u) ? TER{terNO_AUTH} : TER{tecNO_AUTH};
|
return ((flags & ApplyFlags::Retry) != ApplyFlags::None) ? TER{terNO_AUTH}
|
||||||
|
: TER{tecNO_AUTH};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <xrpl/basics/Log.h>
|
#include <xrpl/basics/Log.h>
|
||||||
#include <xrpl/ledger/ApplyView.h>
|
#include <xrpl/ledger/ApplyView.h>
|
||||||
#include <xrpl/ledger/View.h>
|
#include <xrpl/ledger/View.h>
|
||||||
|
#include <xrpl/ledger/entries/EscrowEntry.h>
|
||||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||||
#include <xrpl/ledger/helpers/EscrowHelpers.h>
|
#include <xrpl/ledger/helpers/EscrowHelpers.h>
|
||||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||||
@@ -94,13 +95,12 @@ EscrowCancel::preclaim(PreclaimContext const& ctx)
|
|||||||
if (ctx.view.rules().enabled(featureTokenEscrow))
|
if (ctx.view.rules().enabled(featureTokenEscrow))
|
||||||
{
|
{
|
||||||
auto const seqProxy = SeqProxy::rawSequence(ctx.tx[sfOfferSequence]);
|
auto const seqProxy = SeqProxy::rawSequence(ctx.tx[sfOfferSequence]);
|
||||||
auto const k = keylet::escrow(ctx.tx[sfOwner], seqProxy);
|
EscrowEntryR const escrow(ctx.tx[sfOwner], seqProxy, ctx.view);
|
||||||
auto const slep = ctx.view.read(k);
|
if (!escrow)
|
||||||
if (!slep)
|
|
||||||
return tecNO_TARGET;
|
return tecNO_TARGET;
|
||||||
|
|
||||||
AccountID const account = (*slep)[sfAccount];
|
AccountID const account = (*escrow)[sfAccount];
|
||||||
STAmount const amount = (*slep)[sfAmount];
|
STAmount const amount = (*escrow)[sfAmount];
|
||||||
|
|
||||||
if (!isXRP(amount))
|
if (!isXRP(amount))
|
||||||
{
|
{
|
||||||
@@ -120,9 +120,8 @@ TER
|
|||||||
EscrowCancel::doApply()
|
EscrowCancel::doApply()
|
||||||
{
|
{
|
||||||
auto const seqProxy = SeqProxy::rawSequence(ctx_.tx[sfOfferSequence]);
|
auto const seqProxy = SeqProxy::rawSequence(ctx_.tx[sfOfferSequence]);
|
||||||
auto const k = keylet::escrow(ctx_.tx[sfOwner], seqProxy);
|
EscrowEntryW escrow(ctx_.tx[sfOwner], seqProxy, ctx_.view(), j_);
|
||||||
auto const slep = ctx_.view().peek(k);
|
if (!escrow)
|
||||||
if (!slep)
|
|
||||||
{
|
{
|
||||||
if (ctx_.view().rules().enabled(featureTokenEscrow))
|
if (ctx_.view().rules().enabled(featureTokenEscrow))
|
||||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||||
@@ -133,19 +132,19 @@ EscrowCancel::doApply()
|
|||||||
auto const now = ctx_.view().header().parentCloseTime;
|
auto const now = ctx_.view().header().parentCloseTime;
|
||||||
|
|
||||||
// No cancel time specified: can't execute at all.
|
// No cancel time specified: can't execute at all.
|
||||||
if (!(*slep)[~sfCancelAfter])
|
if (!(*escrow)[~sfCancelAfter])
|
||||||
return tecNO_PERMISSION;
|
return tecNO_PERMISSION;
|
||||||
|
|
||||||
// Too soon: can't execute before the cancel time.
|
// Too soon: can't execute before the cancel time.
|
||||||
if (!after(now, (*slep)[sfCancelAfter]))
|
if (!after(now, (*escrow)[sfCancelAfter]))
|
||||||
return tecNO_PERMISSION;
|
return tecNO_PERMISSION;
|
||||||
|
|
||||||
AccountID const account = (*slep)[sfAccount];
|
AccountID const account = (*escrow)[sfAccount];
|
||||||
|
|
||||||
// Remove escrow from owner directory
|
// Remove escrow from owner directory
|
||||||
{
|
{
|
||||||
auto const page = (*slep)[sfOwnerNode];
|
auto const page = (*escrow)[sfOwnerNode];
|
||||||
if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, k.key, true))
|
if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, escrow.key(), true))
|
||||||
{
|
{
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
JLOG(j_.fatal()) << "Unable to delete Escrow from owner.";
|
JLOG(j_.fatal()) << "Unable to delete Escrow from owner.";
|
||||||
@@ -155,9 +154,10 @@ EscrowCancel::doApply()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove escrow from recipient's owner directory, if present.
|
// Remove escrow from recipient's owner directory, if present.
|
||||||
if (auto const optPage = (*slep)[~sfDestinationNode]; optPage)
|
if (auto const optPage = (*escrow)[~sfDestinationNode]; optPage)
|
||||||
{
|
{
|
||||||
if (!ctx_.view().dirRemove(keylet::ownerDir((*slep)[sfDestination]), *optPage, k.key, true))
|
if (!ctx_.view().dirRemove(
|
||||||
|
keylet::ownerDir((*escrow)[sfDestination]), *optPage, escrow.key(), true))
|
||||||
{
|
{
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
|
JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
|
||||||
@@ -167,13 +167,13 @@ EscrowCancel::doApply()
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto const sle = ctx_.view().peek(keylet::account(account));
|
auto const sle = ctx_.view().peek(keylet::account(account));
|
||||||
STAmount const amount = slep->getFieldAmount(sfAmount);
|
STAmount const amount = escrow->getFieldAmount(sfAmount);
|
||||||
|
|
||||||
// The return can re-create a holding the owner deleted while the escrow
|
// The return can re-create a holding the owner deleted while the escrow
|
||||||
// was pending; the removed escrow must not be counted against its reserve.
|
// was pending; the removed escrow must not be counted against its reserve.
|
||||||
bool const recycleReserve = ctx_.view().rules().enabled(fixCleanup3_4_0);
|
bool const recycleReserve = ctx_.view().rules().enabled(fixCleanup3_4_0);
|
||||||
if (recycleReserve)
|
if (recycleReserve)
|
||||||
decreaseOwnerCountForObject(ctx_.view(), sle, slep, 1, ctx_.journal);
|
decreaseOwnerCountForObject(ctx_.view(), sle, escrow.mutableRawSle(), 1, ctx_.journal);
|
||||||
|
|
||||||
// Transfer amount back to the owner
|
// Transfer amount back to the owner
|
||||||
if (isXRP(amount))
|
if (isXRP(amount))
|
||||||
@@ -192,7 +192,7 @@ EscrowCancel::doApply()
|
|||||||
return escrowUnlockApplyHelper<T>(
|
return escrowUnlockApplyHelper<T>(
|
||||||
ctx_.getApplyViewContext(),
|
ctx_.getApplyViewContext(),
|
||||||
kParityRate,
|
kParityRate,
|
||||||
ctx_.view().rules().enabled(fixCleanup3_2_0) ? sle : slep,
|
ctx_.view().rules().enabled(fixCleanup3_2_0) ? sle : escrow.mutableRawSle(),
|
||||||
preFeeBalance_,
|
preFeeBalance_,
|
||||||
amount,
|
amount,
|
||||||
issuer,
|
issuer,
|
||||||
@@ -206,9 +206,9 @@ EscrowCancel::doApply()
|
|||||||
return ret; // LCOV_EXCL_LINE
|
return ret; // LCOV_EXCL_LINE
|
||||||
|
|
||||||
// Remove escrow from issuers owner directory, if present.
|
// Remove escrow from issuers owner directory, if present.
|
||||||
if (auto const optPage = (*slep)[~sfIssuerNode]; optPage)
|
if (auto const optPage = (*escrow)[~sfIssuerNode]; optPage)
|
||||||
{
|
{
|
||||||
if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, k.key, true))
|
if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, escrow.key(), true))
|
||||||
{
|
{
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
|
JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
|
||||||
@@ -219,10 +219,10 @@ EscrowCancel::doApply()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!recycleReserve)
|
if (!recycleReserve)
|
||||||
decreaseOwnerCountForObject(ctx_.view(), sle, slep, 1, ctx_.journal);
|
decreaseOwnerCountForObject(ctx_.view(), sle, escrow.mutableRawSle(), 1, ctx_.journal);
|
||||||
|
|
||||||
// Remove escrow from ledger
|
// Remove escrow from ledger
|
||||||
ctx_.view().erase(slep);
|
escrow.erase();
|
||||||
|
|
||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <xrpl/core/ServiceRegistry.h>
|
#include <xrpl/core/ServiceRegistry.h>
|
||||||
#include <xrpl/ledger/ApplyView.h>
|
#include <xrpl/ledger/ApplyView.h>
|
||||||
#include <xrpl/ledger/View.h>
|
#include <xrpl/ledger/View.h>
|
||||||
|
#include <xrpl/ledger/entries/EscrowEntry.h>
|
||||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||||
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
|
||||||
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
|
||||||
@@ -481,30 +482,31 @@ EscrowCreate::doApply()
|
|||||||
|
|
||||||
// Create escrow in ledger. Note that we use the value from the
|
// Create escrow in ledger. Note that we use the value from the
|
||||||
// sequence or ticket. For more explanation see comments in SeqProxy.h.
|
// sequence or ticket. For more explanation see comments in SeqProxy.h.
|
||||||
Keylet const escrowKeylet = keylet::escrow(accountID_, ctx_.tx.getSeqProxy());
|
EscrowEntryW escrow(accountID_, ctx_.tx.getSeqProxy(), ctx_.view(), j_);
|
||||||
auto const slep = std::make_shared<SLE>(escrowKeylet);
|
Keylet const escrowKeylet = escrow.keylet();
|
||||||
(*slep)[sfAmount] = amount;
|
escrow.newSLE();
|
||||||
(*slep)[sfAccount] = accountID_;
|
(*escrow)[sfAmount] = amount;
|
||||||
(*slep)[~sfCondition] = ctx_.tx[~sfCondition];
|
(*escrow)[sfAccount] = accountID_;
|
||||||
(*slep)[~sfSourceTag] = ctx_.tx[~sfSourceTag];
|
(*escrow)[~sfCondition] = ctx_.tx[~sfCondition];
|
||||||
(*slep)[sfDestination] = ctx_.tx[sfDestination];
|
(*escrow)[~sfSourceTag] = ctx_.tx[~sfSourceTag];
|
||||||
(*slep)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter];
|
(*escrow)[sfDestination] = ctx_.tx[sfDestination];
|
||||||
(*slep)[~sfFinishAfter] = ctx_.tx[~sfFinishAfter];
|
(*escrow)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter];
|
||||||
(*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag];
|
(*escrow)[~sfFinishAfter] = ctx_.tx[~sfFinishAfter];
|
||||||
|
(*escrow)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag];
|
||||||
|
|
||||||
if (ctx_.view().rules().enabled(fixIncludeKeyletFields))
|
if (ctx_.view().rules().enabled(fixIncludeKeyletFields))
|
||||||
{
|
{
|
||||||
(*slep)[sfSequence] = ctx_.tx.getSeqProxy().value();
|
(*escrow)[sfSequence] = ctx_.tx.getSeqProxy().value();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx_.view().rules().enabled(featureTokenEscrow) && !isXRP(amount))
|
if (ctx_.view().rules().enabled(featureTokenEscrow) && !isXRP(amount))
|
||||||
{
|
{
|
||||||
auto const xferRate = transferRate(ctx_.view(), amount);
|
auto const xferRate = transferRate(ctx_.view(), amount);
|
||||||
if (xferRate != kParityRate)
|
if (xferRate != kParityRate)
|
||||||
(*slep)[sfTransferRate] = xferRate.value;
|
(*escrow)[sfTransferRate] = xferRate.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx_.view().insert(slep);
|
escrow.insert();
|
||||||
|
|
||||||
// Add escrow to sender's owner directory
|
// Add escrow to sender's owner directory
|
||||||
{
|
{
|
||||||
@@ -512,7 +514,7 @@ EscrowCreate::doApply()
|
|||||||
keylet::ownerDir(accountID_), escrowKeylet, describeOwnerDir(accountID_));
|
keylet::ownerDir(accountID_), escrowKeylet, describeOwnerDir(accountID_));
|
||||||
if (!page)
|
if (!page)
|
||||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||||
(*slep)[sfOwnerNode] = *page;
|
(*escrow)[sfOwnerNode] = *page;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's not a self-send, add escrow to recipient's owner directory.
|
// If it's not a self-send, add escrow to recipient's owner directory.
|
||||||
@@ -523,7 +525,7 @@ EscrowCreate::doApply()
|
|||||||
ctx_.view().dirInsert(keylet::ownerDir(dest), escrowKeylet, describeOwnerDir(dest));
|
ctx_.view().dirInsert(keylet::ownerDir(dest), escrowKeylet, describeOwnerDir(dest));
|
||||||
if (!page)
|
if (!page)
|
||||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||||
(*slep)[sfDestinationNode] = *page;
|
(*escrow)[sfDestinationNode] = *page;
|
||||||
}
|
}
|
||||||
|
|
||||||
// IOU escrow objects are added to the issuer's owner directory to help
|
// IOU escrow objects are added to the issuer's owner directory to help
|
||||||
@@ -536,7 +538,7 @@ EscrowCreate::doApply()
|
|||||||
ctx_.view().dirInsert(keylet::ownerDir(issuer), escrowKeylet, describeOwnerDir(issuer));
|
ctx_.view().dirInsert(keylet::ownerDir(issuer), escrowKeylet, describeOwnerDir(issuer));
|
||||||
if (!page)
|
if (!page)
|
||||||
return tecDIR_FULL; // LCOV_EXCL_LINE
|
return tecDIR_FULL; // LCOV_EXCL_LINE
|
||||||
(*slep)[sfIssuerNode] = *page;
|
(*escrow)[sfIssuerNode] = *page;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deduct owner's balance
|
// Deduct owner's balance
|
||||||
@@ -559,7 +561,7 @@ EscrowCreate::doApply()
|
|||||||
|
|
||||||
// increment owner count
|
// increment owner count
|
||||||
increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, ctx_.journal);
|
increaseOwnerCount(ctx_.getApplyViewContext(), sle, 1, ctx_.journal);
|
||||||
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), slep);
|
addSponsorToLedgerEntry(ctx_.getApplyViewContext(), escrow.mutableRawSle());
|
||||||
ctx_.view().update(sle);
|
ctx_.view().update(sle);
|
||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <xrpl/ledger/ApplyView.h>
|
#include <xrpl/ledger/ApplyView.h>
|
||||||
#include <xrpl/ledger/ReadView.h>
|
#include <xrpl/ledger/ReadView.h>
|
||||||
#include <xrpl/ledger/View.h>
|
#include <xrpl/ledger/View.h>
|
||||||
|
#include <xrpl/ledger/entries/EscrowEntry.h>
|
||||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||||
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
#include <xrpl/ledger/helpers/CredentialHelpers.h>
|
||||||
#include <xrpl/ledger/helpers/EscrowHelpers.h>
|
#include <xrpl/ledger/helpers/EscrowHelpers.h>
|
||||||
@@ -211,13 +212,12 @@ EscrowFinish::preclaim(PreclaimContext const& ctx)
|
|||||||
if (ctx.view.rules().enabled(featureTokenEscrow))
|
if (ctx.view.rules().enabled(featureTokenEscrow))
|
||||||
{
|
{
|
||||||
auto const seqProxy = SeqProxy::rawSequence(ctx.tx[sfOfferSequence]);
|
auto const seqProxy = SeqProxy::rawSequence(ctx.tx[sfOfferSequence]);
|
||||||
auto const k = keylet::escrow(ctx.tx[sfOwner], seqProxy);
|
EscrowEntryR const escrow(ctx.tx[sfOwner], seqProxy, ctx.view);
|
||||||
auto const slep = ctx.view.read(k);
|
if (!escrow)
|
||||||
if (!slep)
|
|
||||||
return tecNO_TARGET;
|
return tecNO_TARGET;
|
||||||
|
|
||||||
AccountID const dest = (*slep)[sfDestination];
|
AccountID const dest = (*escrow)[sfDestination];
|
||||||
STAmount const amount = (*slep)[sfAmount];
|
STAmount const amount = (*escrow)[sfAmount];
|
||||||
|
|
||||||
if (!isXRP(amount))
|
if (!isXRP(amount))
|
||||||
{
|
{
|
||||||
@@ -237,9 +237,8 @@ TER
|
|||||||
EscrowFinish::doApply()
|
EscrowFinish::doApply()
|
||||||
{
|
{
|
||||||
auto const seqProxy = SeqProxy::rawSequence(ctx_.tx[sfOfferSequence]);
|
auto const seqProxy = SeqProxy::rawSequence(ctx_.tx[sfOfferSequence]);
|
||||||
auto const k = keylet::escrow(ctx_.tx[sfOwner], seqProxy);
|
EscrowEntryW escrow(ctx_.tx[sfOwner], seqProxy, ctx_.view(), j_);
|
||||||
auto const slep = ctx_.view().peek(k);
|
if (!escrow)
|
||||||
if (!slep)
|
|
||||||
{
|
{
|
||||||
if (ctx_.view().rules().enabled(featureTokenEscrow))
|
if (ctx_.view().rules().enabled(featureTokenEscrow))
|
||||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||||
@@ -252,11 +251,11 @@ EscrowFinish::doApply()
|
|||||||
auto const now = ctx_.view().header().parentCloseTime;
|
auto const now = ctx_.view().header().parentCloseTime;
|
||||||
|
|
||||||
// Too soon: can't execute before the finish time
|
// Too soon: can't execute before the finish time
|
||||||
if ((*slep)[~sfFinishAfter] && !after(now, (*slep)[sfFinishAfter]))
|
if ((*escrow)[~sfFinishAfter] && !after(now, (*escrow)[sfFinishAfter]))
|
||||||
return tecNO_PERMISSION;
|
return tecNO_PERMISSION;
|
||||||
|
|
||||||
// Too late: can't execute after the cancel time
|
// Too late: can't execute after the cancel time
|
||||||
if ((*slep)[~sfCancelAfter] && after(now, (*slep)[sfCancelAfter]))
|
if ((*escrow)[~sfCancelAfter] && after(now, (*escrow)[sfCancelAfter]))
|
||||||
return tecNO_PERMISSION;
|
return tecNO_PERMISSION;
|
||||||
|
|
||||||
// Check cryptocondition fulfillment
|
// Check cryptocondition fulfillment
|
||||||
@@ -296,7 +295,7 @@ EscrowFinish::doApply()
|
|||||||
return tecCRYPTOCONDITION_ERROR;
|
return tecCRYPTOCONDITION_ERROR;
|
||||||
|
|
||||||
// Check against condition in the ledger entry:
|
// Check against condition in the ledger entry:
|
||||||
auto const cond = (*slep)[~sfCondition];
|
auto const cond = (*escrow)[~sfCondition];
|
||||||
|
|
||||||
// If a condition wasn't specified during creation,
|
// If a condition wasn't specified during creation,
|
||||||
// one shouldn't be included now.
|
// one shouldn't be included now.
|
||||||
@@ -313,7 +312,7 @@ EscrowFinish::doApply()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Escrow payments cannot be used to fund accounts.
|
// NOTE: Escrow payments cannot be used to fund accounts.
|
||||||
AccountID const destID = (*slep)[sfDestination];
|
AccountID const destID = (*escrow)[sfDestination];
|
||||||
auto const sled = ctx_.view().peek(keylet::account(destID));
|
auto const sled = ctx_.view().peek(keylet::account(destID));
|
||||||
if (!sled)
|
if (!sled)
|
||||||
return tecNO_DST;
|
return tecNO_DST;
|
||||||
@@ -323,12 +322,12 @@ EscrowFinish::doApply()
|
|||||||
!isTesSuccess(err))
|
!isTesSuccess(err))
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
AccountID const account = (*slep)[sfAccount];
|
AccountID const account = (*escrow)[sfAccount];
|
||||||
|
|
||||||
// Remove escrow from owner directory
|
// Remove escrow from owner directory
|
||||||
{
|
{
|
||||||
auto const page = (*slep)[sfOwnerNode];
|
auto const page = (*escrow)[sfOwnerNode];
|
||||||
if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, k.key, true))
|
if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, escrow.key(), true))
|
||||||
{
|
{
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
JLOG(j_.fatal()) << "Unable to delete Escrow from owner.";
|
JLOG(j_.fatal()) << "Unable to delete Escrow from owner.";
|
||||||
@@ -338,9 +337,9 @@ EscrowFinish::doApply()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove escrow from recipient's owner directory, if present.
|
// Remove escrow from recipient's owner directory, if present.
|
||||||
if (auto const optPage = (*slep)[~sfDestinationNode])
|
if (auto const optPage = (*escrow)[~sfDestinationNode])
|
||||||
{
|
{
|
||||||
if (!ctx_.view().dirRemove(keylet::ownerDir(destID), *optPage, k.key, true))
|
if (!ctx_.view().dirRemove(keylet::ownerDir(destID), *optPage, escrow.key(), true))
|
||||||
{
|
{
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
|
JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
|
||||||
@@ -355,9 +354,9 @@ EscrowFinish::doApply()
|
|||||||
bool const recycleReserve =
|
bool const recycleReserve =
|
||||||
ctx_.view().rules().enabled(featureSponsor) || ctx_.view().rules().enabled(fixCleanup3_4_0);
|
ctx_.view().rules().enabled(featureSponsor) || ctx_.view().rules().enabled(fixCleanup3_4_0);
|
||||||
if (recycleReserve)
|
if (recycleReserve)
|
||||||
decreaseOwnerCountForObject(ctx_.view(), account, slep, 1, ctx_.journal);
|
decreaseOwnerCountForObject(ctx_.view(), account, escrow.mutableRawSle(), 1, ctx_.journal);
|
||||||
|
|
||||||
STAmount const amount = slep->getFieldAmount(sfAmount);
|
STAmount const amount = escrow->getFieldAmount(sfAmount);
|
||||||
// Transfer amount to destination
|
// Transfer amount to destination
|
||||||
if (isXRP(amount))
|
if (isXRP(amount))
|
||||||
{
|
{
|
||||||
@@ -368,8 +367,8 @@ EscrowFinish::doApply()
|
|||||||
if (!ctx_.view().rules().enabled(featureTokenEscrow))
|
if (!ctx_.view().rules().enabled(featureTokenEscrow))
|
||||||
return temDISABLED; // LCOV_EXCL_LINE
|
return temDISABLED; // LCOV_EXCL_LINE
|
||||||
|
|
||||||
Rate lockedRate = slep->isFieldPresent(sfTransferRate)
|
Rate lockedRate = escrow->isFieldPresent(sfTransferRate)
|
||||||
? xrpl::Rate(slep->getFieldU32(sfTransferRate))
|
? xrpl::Rate(escrow->getFieldU32(sfTransferRate))
|
||||||
: kParityRate;
|
: kParityRate;
|
||||||
auto const issuer = amount.getIssuer();
|
auto const issuer = amount.getIssuer();
|
||||||
bool const createAsset = destID == accountID_;
|
bool const createAsset = destID == accountID_;
|
||||||
@@ -392,9 +391,9 @@ EscrowFinish::doApply()
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
// Remove escrow from issuers owner directory, if present.
|
// Remove escrow from issuers owner directory, if present.
|
||||||
if (auto const optPage = (*slep)[~sfIssuerNode]; optPage)
|
if (auto const optPage = (*escrow)[~sfIssuerNode]; optPage)
|
||||||
{
|
{
|
||||||
if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, k.key, true))
|
if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, escrow.key(), true))
|
||||||
{
|
{
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
|
JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
|
||||||
@@ -407,10 +406,10 @@ EscrowFinish::doApply()
|
|||||||
ctx_.view().update(sled);
|
ctx_.view().update(sled);
|
||||||
|
|
||||||
if (!recycleReserve)
|
if (!recycleReserve)
|
||||||
decreaseOwnerCountForObject(ctx_.view(), account, slep, 1, ctx_.journal);
|
decreaseOwnerCountForObject(ctx_.view(), account, escrow.mutableRawSle(), 1, ctx_.journal);
|
||||||
|
|
||||||
// Remove escrow from ledger
|
// Remove escrow from ledger
|
||||||
ctx_.view().erase(slep);
|
escrow.erase();
|
||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ LoanSet::preflight(PreflightContext const& ctx)
|
|||||||
if (tx.isFlag(tfInnerBatchTxn) && ctx.rules.enabled(featureBatchV1_1) &&
|
if (tx.isFlag(tfInnerBatchTxn) && ctx.rules.enabled(featureBatchV1_1) &&
|
||||||
!tx.isFieldPresent(sfCounterparty))
|
!tx.isFieldPresent(sfCounterparty))
|
||||||
{
|
{
|
||||||
auto const parentBatchId = ctx.parentBatchId.value_or(UInt256{0});
|
auto const parentBatchId = ctx.parentBatchId.value_or(beast::kZero);
|
||||||
JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: "
|
JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: "
|
||||||
<< "no Counterparty for inner LoanSet transaction.";
|
<< "no Counterparty for inner LoanSet transaction.";
|
||||||
return temBAD_SIGNER;
|
return temBAD_SIGNER;
|
||||||
@@ -91,7 +91,8 @@ LoanSet::preflight(PreflightContext const& ctx)
|
|||||||
// A proposed LoanSet is stored unsigned; its CounterpartySignature is
|
// A proposed LoanSet is stored unsigned; its CounterpartySignature is
|
||||||
// collected on-ledger afterward, so its absence here is expected, not an
|
// collected on-ledger afterward, so its absence here is expected, not an
|
||||||
// error (On-Chain Cosigner spec §5.3.1.2).
|
// error (On-Chain Cosigner spec §5.3.1.2).
|
||||||
if (!tx.isFlag(tfInnerBatchTxn) && !counterPartySig && (ctx.flags & TapProposal) == 0)
|
if (!tx.isFlag(tfInnerBatchTxn) && !counterPartySig &&
|
||||||
|
(ctx.flags & ApplyFlags::Proposal) == ApplyFlags::None)
|
||||||
{
|
{
|
||||||
JLOG(ctx.j.warn()) << "LoanSet transaction must have a CounterpartySignature.";
|
JLOG(ctx.j.warn()) << "LoanSet transaction must have a CounterpartySignature.";
|
||||||
return temBAD_SIGNER;
|
return temBAD_SIGNER;
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ NFTokenMint::createNFTokenID(
|
|||||||
std::distance(buf.data(), ptr) == buf.size(),
|
std::distance(buf.data(), ptr) == buf.size(),
|
||||||
"xrpl::NFTokenMint::createNFTokenID : data size matches the buffer");
|
"xrpl::NFTokenMint::createNFTokenID : data size matches the buffer");
|
||||||
|
|
||||||
return UInt256::fromVoid(buf.data());
|
return uint256{buf};
|
||||||
}
|
}
|
||||||
|
|
||||||
TER
|
TER
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ TransactionProposalCreate::preflight(PreflightContext const& ctx)
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
STTx const stx{STObject{proposedTx}};
|
STTx const stx{STObject{proposedTx}};
|
||||||
auto const inner =
|
auto const inner = xrpl::preflight(
|
||||||
xrpl::preflight(ctx.registry, ctx.rules, stx, TapDryRun | TapProposal, ctx.j);
|
ctx.registry, ctx.rules, stx, ApplyFlags::DryRun | ApplyFlags::Proposal, ctx.j);
|
||||||
if (!isTesSuccess(inner.ter))
|
if (!isTesSuccess(inner.ter))
|
||||||
{
|
{
|
||||||
JLOG(ctx.j.debug()) << "TransactionProposalCreate: proposed txn "
|
JLOG(ctx.j.debug()) << "TransactionProposalCreate: proposed txn "
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ SponsorshipTransfer::preflight(PreflightContext const& ctx)
|
|||||||
// is collected on-ledger afterward, so its absence here is expected, not
|
// is collected on-ledger afterward, so its absence here is expected, not
|
||||||
// an error (On-Chain Cosigner spec §5.3.1.2).
|
// an error (On-Chain Cosigner spec §5.3.1.2).
|
||||||
if (isAccountReserveSponsorship && !ctx.tx.isFieldPresent(sfSponsorSignature) &&
|
if (isAccountReserveSponsorship && !ctx.tx.isFieldPresent(sfSponsorSignature) &&
|
||||||
(ctx.flags & TapProposal) == 0)
|
(ctx.flags & ApplyFlags::Proposal) == ApplyFlags::None)
|
||||||
{
|
{
|
||||||
JLOG(ctx.j.debug()) << "preflight: account sponsorship requires sfSponsorSignature";
|
JLOG(ctx.j.debug()) << "preflight: account sponsorship requires sfSponsorSignature";
|
||||||
return temMALFORMED;
|
return temMALFORMED;
|
||||||
|
|||||||
@@ -359,7 +359,8 @@ Batch::preflight(PreflightContext const& ctx)
|
|||||||
// TapBatch — PreflightContext with a parentBatchId requires it.
|
// TapBatch — PreflightContext with a parentBatchId requires it.
|
||||||
// LoanSet already short-circuits on tfInnerBatchTxn; it is also in
|
// LoanSet already short-circuits on tfInnerBatchTxn; it is also in
|
||||||
// kDisabledTxTypes, so it never reaches this call.
|
// kDisabledTxTypes, so it never reaches this call.
|
||||||
ApplyFlags const innerFlags = TapBatch | (ctx.flags & (TapProposal | TapDryRun));
|
ApplyFlags const innerFlags =
|
||||||
|
ApplyFlags::Batch | (ctx.flags & (ApplyFlags::Proposal | ApplyFlags::DryRun));
|
||||||
if (auto const preflightResult =
|
if (auto const preflightResult =
|
||||||
xrpl::preflight(ctx.registry, ctx.rules, parentBatchId, stx, innerFlags, ctx.j);
|
xrpl::preflight(ctx.registry, ctx.rules, parentBatchId, stx, innerFlags, ctx.j);
|
||||||
!isTesSuccess(preflightResult.ter))
|
!isTesSuccess(preflightResult.ter))
|
||||||
@@ -431,7 +432,7 @@ Batch::preflightSigValidated(PreflightContext const& ctx)
|
|||||||
// A proposed Batch is stored unsigned; its BatchSigners are collected
|
// A proposed Batch is stored unsigned; its BatchSigners are collected
|
||||||
// on-ledger afterward, so the signer-presence match belongs to submission
|
// on-ledger afterward, so the signer-presence match belongs to submission
|
||||||
// time, not proposal creation (On-Chain Cosigner spec §5.3.1.2).
|
// time, not proposal creation (On-Chain Cosigner spec §5.3.1.2).
|
||||||
if ((ctx.flags & TapProposal) != 0)
|
if ((ctx.flags & ApplyFlags::Proposal) != ApplyFlags::None)
|
||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
|
|
||||||
auto const parentBatchId = ctx.tx.getTransactionID();
|
auto const parentBatchId = ctx.tx.getTransactionID();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <xrpl/beast/utility/instrumentation.h>
|
#include <xrpl/beast/utility/instrumentation.h>
|
||||||
#include <xrpl/core/ServiceRegistry.h>
|
#include <xrpl/core/ServiceRegistry.h>
|
||||||
#include <xrpl/ledger/AmendmentTable.h>
|
#include <xrpl/ledger/AmendmentTable.h>
|
||||||
|
#include <xrpl/ledger/entries/FeeSettingsEntry.h>
|
||||||
#include <xrpl/protocol/Feature.h>
|
#include <xrpl/protocol/Feature.h>
|
||||||
#include <xrpl/protocol/Indexes.h>
|
#include <xrpl/protocol/Indexes.h>
|
||||||
#include <xrpl/protocol/Protocol.h>
|
#include <xrpl/protocol/Protocol.h>
|
||||||
@@ -260,16 +261,14 @@ Change::applyAmendment()
|
|||||||
TER
|
TER
|
||||||
Change::applyFee()
|
Change::applyFee()
|
||||||
{
|
{
|
||||||
auto const k = keylet::feeSettings();
|
FeeSettingsEntryW feeObject(view());
|
||||||
|
|
||||||
SLE::pointer feeObject = view().peek(k);
|
|
||||||
|
|
||||||
if (!feeObject)
|
if (!feeObject)
|
||||||
{
|
{
|
||||||
feeObject = std::make_shared<SLE>(k);
|
feeObject.newSLE();
|
||||||
view().insert(feeObject);
|
feeObject.insert();
|
||||||
}
|
}
|
||||||
auto set = [](SLE::pointer& feeObject, STTx const& tx, auto const& field) {
|
auto set = [](FeeSettingsEntryW& feeObject, STTx const& tx, auto const& field) {
|
||||||
feeObject->at(field) = tx[field];
|
feeObject->at(field) = tx[field];
|
||||||
};
|
};
|
||||||
if (view().rules().enabled(featureXRPFees))
|
if (view().rules().enabled(featureXRPFees))
|
||||||
@@ -291,7 +290,7 @@ Change::applyFee()
|
|||||||
set(feeObject, ctx_.tx, sfReserveIncrement);
|
set(feeObject, ctx_.tx, sfReserveIncrement);
|
||||||
}
|
}
|
||||||
|
|
||||||
view().update(feeObject);
|
feeObject.update();
|
||||||
|
|
||||||
JLOG(j_.warn()) << "Fees have been changed";
|
JLOG(j_.warn()) << "Fees have been changed";
|
||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
|
|||||||
@@ -1890,7 +1890,7 @@ private:
|
|||||||
auto const flowResult = [&] {
|
auto const flowResult = [&] {
|
||||||
STAmount const deliver(eth(51'000'000));
|
STAmount const deliver(eth(51'000'000));
|
||||||
STAmount smax(btc(61'000'000));
|
STAmount smax(btc(61'000'000));
|
||||||
PaymentSandbox sb(env.current().get(), TapNone);
|
PaymentSandbox sb(env.current().get(), ApplyFlags::None);
|
||||||
STPathSet paths;
|
STPathSet paths;
|
||||||
auto ipe = [](MPTTester const& iss) {
|
auto ipe = [](MPTTester const& iss) {
|
||||||
return STPathElement(
|
return STPathElement(
|
||||||
@@ -1928,7 +1928,7 @@ private:
|
|||||||
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
|
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
|
||||||
if (flowResult.removableOffers.empty())
|
if (flowResult.removableOffers.empty())
|
||||||
return false;
|
return false;
|
||||||
Sandbox sb(&view, TapNone);
|
Sandbox sb(&view, ApplyFlags::None);
|
||||||
for (auto const& o : flowResult.removableOffers)
|
for (auto const& o : flowResult.removableOffers)
|
||||||
{
|
{
|
||||||
if (auto ok = sb.peek(keylet::offer(o)))
|
if (auto ok = sb.peek(keylet::offer(o)))
|
||||||
|
|||||||
@@ -2049,7 +2049,7 @@ private:
|
|||||||
auto const flowResult = [&] {
|
auto const flowResult = [&] {
|
||||||
STAmount const deliver(USD(51));
|
STAmount const deliver(USD(51));
|
||||||
STAmount smax(BTC(61));
|
STAmount smax(BTC(61));
|
||||||
PaymentSandbox sb(env.current().get(), TapNone);
|
PaymentSandbox sb(env.current().get(), ApplyFlags::None);
|
||||||
STPathSet paths;
|
STPathSet paths;
|
||||||
auto ipe = [](Issue const& iss) {
|
auto ipe = [](Issue const& iss) {
|
||||||
return STPathElement(
|
return STPathElement(
|
||||||
@@ -2087,7 +2087,7 @@ private:
|
|||||||
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
|
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
|
||||||
if (flowResult.removableOffers.empty())
|
if (flowResult.removableOffers.empty())
|
||||||
return false;
|
return false;
|
||||||
Sandbox sb(&view, TapNone);
|
Sandbox sb(&view, ApplyFlags::None);
|
||||||
for (auto const& o : flowResult.removableOffers)
|
for (auto const& o : flowResult.removableOffers)
|
||||||
{
|
{
|
||||||
if (auto ok = sb.peek(keylet::offer(o)))
|
if (auto ok = sb.peek(keylet::offer(o)))
|
||||||
|
|||||||
@@ -461,8 +461,8 @@ private:
|
|||||||
|
|
||||||
// Invalid tokens
|
// Invalid tokens
|
||||||
{
|
{
|
||||||
auto const mpt1 = MPTIssue{MPTID(0xabc)};
|
auto const mpt1 = MPTIssue{MPTID{0xabc}};
|
||||||
auto const mpt2 = MPTIssue{MPTID(0xdef)};
|
auto const mpt2 = MPTIssue{MPTID{0xdef}};
|
||||||
ammAlice.deposit(
|
ammAlice.deposit(
|
||||||
alice_,
|
alice_,
|
||||||
1'000,
|
1'000,
|
||||||
@@ -4158,7 +4158,7 @@ private:
|
|||||||
auto jtx = env.jt(tx, Seq(1), Fee(10));
|
auto jtx = env.jt(tx, Seq(1), Fee(10));
|
||||||
env.app().config().features.erase(featureMPTokensV2);
|
env.app().config().features.erase(featureMPTokensV2);
|
||||||
PreflightContext const ctx(
|
PreflightContext const ctx(
|
||||||
env.app(), *jtx.stx, env.current()->rules(), TapNone, env.journal);
|
env.app(), *jtx.stx, env.current()->rules(), ApplyFlags::None, env.journal);
|
||||||
auto pf = AMMBid::checkExtraFeatures(ctx);
|
auto pf = AMMBid::checkExtraFeatures(ctx);
|
||||||
BEAST_EXPECT(pf == false);
|
BEAST_EXPECT(pf == false);
|
||||||
env.app().config().features.insert(featureMPTokensV2);
|
env.app().config().features.insert(featureMPTokensV2);
|
||||||
@@ -4170,7 +4170,7 @@ private:
|
|||||||
jtx.jv["Asset2"].removeMember("mpt_issuance_id");
|
jtx.jv["Asset2"].removeMember("mpt_issuance_id");
|
||||||
jtx.stx = env.ust(jtx);
|
jtx.stx = env.ust(jtx);
|
||||||
PreflightContext const ctx(
|
PreflightContext const ctx(
|
||||||
env.app(), *jtx.stx, env.current()->rules(), TapNone, env.journal);
|
env.app(), *jtx.stx, env.current()->rules(), ApplyFlags::None, env.journal);
|
||||||
auto pf = AMMBid::preflight(ctx);
|
auto pf = AMMBid::preflight(ctx);
|
||||||
BEAST_EXPECT(pf == temBAD_AMM_TOKENS);
|
BEAST_EXPECT(pf == temBAD_AMM_TOKENS);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -603,8 +603,8 @@ private:
|
|||||||
|
|
||||||
// Invalid tokens - bogus currency
|
// Invalid tokens - bogus currency
|
||||||
{
|
{
|
||||||
auto const iss1 = Issue{Currency(0xabc), gw_.id()};
|
auto const iss1 = Issue{Currency{0xabc}, gw_.id()};
|
||||||
auto const iss2 = Issue{Currency(0xdef), gw_.id()};
|
auto const iss2 = Issue{Currency{0xdef}, gw_.id()};
|
||||||
ammAlice.deposit(
|
ammAlice.deposit(
|
||||||
alice_,
|
alice_,
|
||||||
1'000,
|
1'000,
|
||||||
@@ -3263,7 +3263,7 @@ private:
|
|||||||
auto jtx = env.jt(tx, Seq(1), Fee(baseFee));
|
auto jtx = env.jt(tx, Seq(1), Fee(baseFee));
|
||||||
env.app().config().features.erase(featureAMM);
|
env.app().config().features.erase(featureAMM);
|
||||||
PreflightContext const pfCtx(
|
PreflightContext const pfCtx(
|
||||||
env.app(), *jtx.stx, env.current()->rules(), TapNone, env.journal);
|
env.app(), *jtx.stx, env.current()->rules(), ApplyFlags::None, env.journal);
|
||||||
auto pf = Transactor::invokePreflight<AMMBid>(pfCtx);
|
auto pf = Transactor::invokePreflight<AMMBid>(pfCtx);
|
||||||
BEAST_EXPECT(pf == temDISABLED);
|
BEAST_EXPECT(pf == temDISABLED);
|
||||||
env.app().config().features.insert(featureAMM);
|
env.app().config().features.insert(featureAMM);
|
||||||
@@ -3274,7 +3274,7 @@ private:
|
|||||||
jtx.jv["TxnSignature"] = "deadbeef";
|
jtx.jv["TxnSignature"] = "deadbeef";
|
||||||
jtx.stx = env.ust(jtx);
|
jtx.stx = env.ust(jtx);
|
||||||
PreflightContext const pfCtx(
|
PreflightContext const pfCtx(
|
||||||
env.app(), *jtx.stx, env.current()->rules(), TapNone, env.journal);
|
env.app(), *jtx.stx, env.current()->rules(), ApplyFlags::None, env.journal);
|
||||||
auto pf = Transactor::invokePreflight<AMMBid>(pfCtx);
|
auto pf = Transactor::invokePreflight<AMMBid>(pfCtx);
|
||||||
BEAST_EXPECT(!isTesSuccess(pf));
|
BEAST_EXPECT(!isTesSuccess(pf));
|
||||||
}
|
}
|
||||||
@@ -3285,7 +3285,7 @@ private:
|
|||||||
jtx.jv["Asset2"].removeMember("issuer");
|
jtx.jv["Asset2"].removeMember("issuer");
|
||||||
jtx.stx = env.ust(jtx);
|
jtx.stx = env.ust(jtx);
|
||||||
PreflightContext const pfCtx(
|
PreflightContext const pfCtx(
|
||||||
env.app(), *jtx.stx, env.current()->rules(), TapNone, env.journal);
|
env.app(), *jtx.stx, env.current()->rules(), ApplyFlags::None, env.journal);
|
||||||
auto pf = Transactor::invokePreflight<AMMBid>(pfCtx);
|
auto pf = Transactor::invokePreflight<AMMBid>(pfCtx);
|
||||||
BEAST_EXPECT(pf == temBAD_AMM_TOKENS);
|
BEAST_EXPECT(pf == temBAD_AMM_TOKENS);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -594,7 +594,7 @@ public:
|
|||||||
stx->at(sfSigningPubKey) = makeSlice(std::string("badkey"));
|
stx->at(sfSigningPubKey) = makeSlice(std::string("badkey"));
|
||||||
|
|
||||||
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
|
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
|
||||||
auto const result = xrpl::apply(env.app(), view, *stx, TapNone, j);
|
auto const result = xrpl::apply(env.app(), view, *stx, ApplyFlags::None, j);
|
||||||
BEAST_EXPECT(result.ter == temBAD_SIGNATURE);
|
BEAST_EXPECT(result.ter == temBAD_SIGNATURE);
|
||||||
BEAST_EXPECT(!result.applied);
|
BEAST_EXPECT(!result.applied);
|
||||||
return result.applied;
|
return result.applied;
|
||||||
|
|||||||
@@ -436,7 +436,7 @@ class Batch_test : public beast::unit_test::Suite
|
|||||||
{
|
{
|
||||||
auto jtx = env.jt(pay(alice, bob, XRP(1)), Txflags(tfInnerBatchTxn));
|
auto jtx = env.jt(pay(alice, bob, XRP(1)), Txflags(tfInnerBatchTxn));
|
||||||
PreflightContext const pfCtx(
|
PreflightContext const pfCtx(
|
||||||
env.app(), *jtx.stx, env.current()->rules(), TapNone, env.journal);
|
env.app(), *jtx.stx, env.current()->rules(), ApplyFlags::None, env.journal);
|
||||||
auto const pf = Transactor::invokePreflight<Payment>(pfCtx);
|
auto const pf = Transactor::invokePreflight<Payment>(pfCtx);
|
||||||
BEAST_EXPECT(pf == temINVALID_INNER_BATCH);
|
BEAST_EXPECT(pf == temINVALID_INNER_BATCH);
|
||||||
}
|
}
|
||||||
@@ -451,7 +451,7 @@ class Batch_test : public beast::unit_test::Suite
|
|||||||
disabledEnv.app(),
|
disabledEnv.app(),
|
||||||
*jtx.stx,
|
*jtx.stx,
|
||||||
disabledEnv.current()->rules(),
|
disabledEnv.current()->rules(),
|
||||||
TapNone,
|
ApplyFlags::None,
|
||||||
disabledEnv.journal);
|
disabledEnv.journal);
|
||||||
auto const pf = Transactor::invokePreflight<Payment>(pfCtx);
|
auto const pf = Transactor::invokePreflight<Payment>(pfCtx);
|
||||||
BEAST_EXPECT(pf == temINVALID_FLAG);
|
BEAST_EXPECT(pf == temINVALID_FLAG);
|
||||||
@@ -462,7 +462,12 @@ class Batch_test : public beast::unit_test::Suite
|
|||||||
{
|
{
|
||||||
auto jtx = env.jt(pay(alice, bob, XRP(1)));
|
auto jtx = env.jt(pay(alice, bob, XRP(1)));
|
||||||
PreflightContext const pfCtx(
|
PreflightContext const pfCtx(
|
||||||
env.app(), *jtx.stx, UInt256{1}, env.current()->rules(), TapBatch, env.journal);
|
env.app(),
|
||||||
|
*jtx.stx,
|
||||||
|
UInt256{1},
|
||||||
|
env.current()->rules(),
|
||||||
|
ApplyFlags::Batch,
|
||||||
|
env.journal);
|
||||||
auto const pf = Transactor::invokePreflight<Payment>(pfCtx);
|
auto const pf = Transactor::invokePreflight<Payment>(pfCtx);
|
||||||
BEAST_EXPECT(pf == temINVALID_INNER_BATCH);
|
BEAST_EXPECT(pf == temINVALID_INNER_BATCH);
|
||||||
}
|
}
|
||||||
@@ -1590,7 +1595,7 @@ class Batch_test : public beast::unit_test::Suite
|
|||||||
batch::Sig(std::vector<Reg>(kMaxBatchSigners + 1, bob)));
|
batch::Sig(std::vector<Reg>(kMaxBatchSigners + 1, bob)));
|
||||||
|
|
||||||
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
|
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
|
||||||
auto const result = xrpl::apply(env.app(), view, *jt.stx, TapNone, j);
|
auto const result = xrpl::apply(env.app(), view, *jt.stx, ApplyFlags::None, j);
|
||||||
BEAST_EXPECT(!result.applied && result.ter == temARRAY_TOO_LARGE);
|
BEAST_EXPECT(!result.applied && result.ter == temARRAY_TOO_LARGE);
|
||||||
return result.applied;
|
return result.applied;
|
||||||
});
|
});
|
||||||
@@ -4381,7 +4386,7 @@ class Batch_test : public beast::unit_test::Suite
|
|||||||
|
|
||||||
STTx const stx = STTx(ttAMENDMENT, [&](auto& obj) {
|
STTx const stx = STTx(ttAMENDMENT, [&](auto& obj) {
|
||||||
obj.setAccountID(sfAccount, AccountID());
|
obj.setAccountID(sfAccount, AccountID());
|
||||||
obj.setFieldH256(sfAmendment, UInt256(2));
|
obj.setFieldH256(sfAmendment, UInt256{2});
|
||||||
obj.setFieldU32(sfLedgerSequence, env.seq(alice));
|
obj.setFieldU32(sfLedgerSequence, env.seq(alice));
|
||||||
obj.setFieldU32(sfFlags, tfInnerBatchTxn);
|
obj.setFieldU32(sfFlags, tfInnerBatchTxn);
|
||||||
});
|
});
|
||||||
@@ -4391,7 +4396,7 @@ class Batch_test : public beast::unit_test::Suite
|
|||||||
BEAST_EXPECT(!passesLocalChecks(stx, reason));
|
BEAST_EXPECT(!passesLocalChecks(stx, reason));
|
||||||
BEAST_EXPECT(reason == "Cannot submit pseudo transactions.");
|
BEAST_EXPECT(reason == "Cannot submit pseudo transactions.");
|
||||||
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
|
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
|
||||||
auto const result = xrpl::apply(env.app(), view, stx, TapNone, j);
|
auto const result = xrpl::apply(env.app(), view, stx, ApplyFlags::None, j);
|
||||||
BEAST_EXPECT(!result.applied && result.ter == temINVALID_FLAG);
|
BEAST_EXPECT(!result.applied && result.ter == temINVALID_FLAG);
|
||||||
return result.applied;
|
return result.applied;
|
||||||
});
|
});
|
||||||
@@ -5637,7 +5642,7 @@ class Batch_test : public beast::unit_test::Suite
|
|||||||
obj.setFieldU32(sfFlags, tfInnerBatchTxn);
|
obj.setFieldU32(sfFlags, tfInnerBatchTxn);
|
||||||
});
|
});
|
||||||
|
|
||||||
auto const result = xrpl::apply(env.app(), view, stx, TapNone, j);
|
auto const result = xrpl::apply(env.app(), view, stx, ApplyFlags::None, j);
|
||||||
// Must NOT be applied — signature was never checked
|
// Must NOT be applied — signature was never checked
|
||||||
BEAST_EXPECT(!result.applied);
|
BEAST_EXPECT(!result.applied);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -432,16 +432,17 @@ class ConfidentialTransferExtended_test : public ConfidentialTransferTestBase
|
|||||||
{.account = carol, .payAmount = 100, .convertAmount = 50}}};
|
{.account = carol, .payAmount = 100, .convertAmount = 50}}};
|
||||||
auto& mpt = confEnv.mpt;
|
auto& mpt = confEnv.mpt;
|
||||||
|
|
||||||
std::vector<std::string> tooManyCredentials;
|
|
||||||
tooManyCredentials.reserve(9);
|
|
||||||
for (int i = 0; i < 9; ++i)
|
|
||||||
tooManyCredentials.push_back(to_string(UInt256(i)));
|
|
||||||
|
|
||||||
mpt.send({
|
mpt.send({
|
||||||
.account = carol,
|
.account = carol,
|
||||||
.dest = bob,
|
.dest = bob,
|
||||||
.amt = 10,
|
.amt = 10,
|
||||||
.credentials = tooManyCredentials,
|
.credentials =
|
||||||
|
[] {
|
||||||
|
std::vector<std::string> v;
|
||||||
|
for (int i = 0; i < 9; ++i)
|
||||||
|
v.push_back(std::string(63, '0') + static_cast<char>('0' + i));
|
||||||
|
return v;
|
||||||
|
}(),
|
||||||
.err = temMALFORMED,
|
.err = temMALFORMED,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -485,7 +486,7 @@ class ConfidentialTransferExtended_test : public ConfidentialTransferTestBase
|
|||||||
{.account = carol, .payAmount = 100, .convertAmount = 50}}};
|
{.account = carol, .payAmount = 100, .convertAmount = 50}}};
|
||||||
auto& mpt = confEnv.mpt;
|
auto& mpt = confEnv.mpt;
|
||||||
|
|
||||||
std::string const fakeCredIdx = to_string(UInt256(999));
|
std::string const fakeCredIdx = to_string(UInt256{999});
|
||||||
mpt.send({
|
mpt.send({
|
||||||
.account = carol,
|
.account = carol,
|
||||||
.dest = bob,
|
.dest = bob,
|
||||||
@@ -861,8 +862,7 @@ class ConfidentialTransferExtended_test : public ConfidentialTransferTestBase
|
|||||||
// than the ticket seq (ticketSeq1).
|
// than the ticket seq (ticketSeq1).
|
||||||
{
|
{
|
||||||
BEAST_EXPECT(env.seq(bob) != ticketSeq1);
|
BEAST_EXPECT(env.seq(bob) != ticketSeq1);
|
||||||
UInt256 const badCtxHash =
|
auto const badCtxHash = getConvertContextHash(bob, mptAlice.issuanceID(), env.seq(bob));
|
||||||
getConvertContextHash(bob, mptAlice.issuanceID(), env.seq(bob));
|
|
||||||
auto const badProof = requireOptional(
|
auto const badProof = requireOptional(
|
||||||
mptAlice.getSchnorrProof(bob, badCtxHash), "Missing Schnorr Proof.");
|
mptAlice.getSchnorrProof(bob, badCtxHash), "Missing Schnorr Proof.");
|
||||||
|
|
||||||
|
|||||||
@@ -1745,7 +1745,8 @@ class ConfidentialTransfer_test : public ConfidentialTransferTestBase
|
|||||||
(*sle)[sfConfidentialBalanceVersion] = wrappedFrom;
|
(*sle)[sfConfidentialBalanceVersion] = wrappedFrom;
|
||||||
view.rawReplace(sle);
|
view.rawReplace(sle);
|
||||||
|
|
||||||
auto const result = xrpl::apply(env.app(), view, *jt.stx, TapNone, env.journal);
|
auto const result =
|
||||||
|
xrpl::apply(env.app(), view, *jt.stx, ApplyFlags::None, env.journal);
|
||||||
BEAST_EXPECT(result.ter == tesSUCCESS);
|
BEAST_EXPECT(result.ter == tesSUCCESS);
|
||||||
return result.applied;
|
return result.applied;
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -901,8 +901,7 @@ struct Credentials_test : public beast::unit_test::Suite
|
|||||||
|
|
||||||
{
|
{
|
||||||
testcase("deleteSLE fail, bad SLE.");
|
testcase("deleteSLE fail, bad SLE.");
|
||||||
auto view =
|
auto view = std::make_shared<ApplyViewImpl>(env.current().get(), ApplyFlags::None);
|
||||||
std::make_shared<ApplyViewImpl>(env.current().get(), ApplyFlags::TapNone);
|
|
||||||
auto ter = xrpl::credentials::deleteSLE(*view, {}, env.journal);
|
auto ter = xrpl::credentials::deleteSLE(*view, {}, env.journal);
|
||||||
BEAST_EXPECT(ter == tecNO_ENTRY);
|
BEAST_EXPECT(ter == tecNO_ENTRY);
|
||||||
}
|
}
|
||||||
@@ -1125,7 +1124,7 @@ struct Credentials_test : public beast::unit_test::Suite
|
|||||||
// Create an ApplyViewImpl on top of the current closed ledger
|
// Create an ApplyViewImpl on top of the current closed ledger
|
||||||
// and corrupt it by erasing the issuer's account SLE
|
// and corrupt it by erasing the issuer's account SLE
|
||||||
auto const open = env.current();
|
auto const open = env.current();
|
||||||
ApplyViewImpl av(&*open, TapNone);
|
ApplyViewImpl av(&*open, ApplyFlags::None);
|
||||||
|
|
||||||
// Erase the issuer's account to simulate ledger corruption
|
// Erase the issuer's account to simulate ledger corruption
|
||||||
auto sleIssuer = av.peek(keylet::account(issuer.id()));
|
auto sleIssuer = av.peek(keylet::account(issuer.id()));
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include <test/jtx/ter.h>
|
#include <test/jtx/ter.h>
|
||||||
#include <test/jtx/trust.h>
|
#include <test/jtx/trust.h>
|
||||||
#include <test/jtx/txflags.h>
|
#include <test/jtx/txflags.h>
|
||||||
|
#include <test/jtx/utility.h>
|
||||||
#include <test/jtx/vault.h>
|
#include <test/jtx/vault.h>
|
||||||
|
|
||||||
#include <xrpl/basics/Number.h>
|
#include <xrpl/basics/Number.h>
|
||||||
|
|||||||
@@ -2795,10 +2795,10 @@ struct EscrowToken_test : public beast::unit_test::Suite
|
|||||||
|
|
||||||
auto const seq1 = env.seq(alice);
|
auto const seq1 = env.seq(alice);
|
||||||
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
|
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
|
||||||
Sandbox sb(&view, TapNone);
|
Sandbox sb(&view, ApplyFlags::None);
|
||||||
auto sleNew =
|
auto sleNew =
|
||||||
std::make_shared<SLE>(keylet::escrow(alice, SeqProxy::rawSequence(seq1)));
|
std::make_shared<SLE>(keylet::escrow(alice, SeqProxy::rawSequence(seq1)));
|
||||||
MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID(0x4985601))}};
|
MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID{0x4985601})}};
|
||||||
STAmount const amt(mpt, 10);
|
STAmount const amt(mpt, 10);
|
||||||
sleNew->setAccountID(sfDestination, bob);
|
sleNew->setAccountID(sfDestination, bob);
|
||||||
sleNew->setFieldAmount(sfAmount, amt);
|
sleNew->setFieldAmount(sfAmount, amt);
|
||||||
@@ -3023,10 +3023,10 @@ struct EscrowToken_test : public beast::unit_test::Suite
|
|||||||
|
|
||||||
auto const seq1 = env.seq(alice);
|
auto const seq1 = env.seq(alice);
|
||||||
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
|
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
|
||||||
Sandbox sb(&view, TapNone);
|
Sandbox sb(&view, ApplyFlags::None);
|
||||||
auto sleNew =
|
auto sleNew =
|
||||||
std::make_shared<SLE>(keylet::escrow(alice, SeqProxy::rawSequence(seq1)));
|
std::make_shared<SLE>(keylet::escrow(alice, SeqProxy::rawSequence(seq1)));
|
||||||
MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID(0x4985601))}};
|
MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID{0x4985601})}};
|
||||||
STAmount const amt(mpt, 10);
|
STAmount const amt(mpt, 10);
|
||||||
sleNew->setAccountID(sfDestination, bob);
|
sleNew->setAccountID(sfDestination, bob);
|
||||||
sleNew->setFieldAmount(sfAmount, amt);
|
sleNew->setFieldAmount(sfAmount, amt);
|
||||||
|
|||||||
@@ -1302,8 +1302,8 @@ struct Escrow_test : public beast::unit_test::Suite
|
|||||||
escrow::kFinishTime(env.now() + 1s),
|
escrow::kFinishTime(env.now() + 1s),
|
||||||
Seq(1),
|
Seq(1),
|
||||||
Fee(baseFee));
|
Fee(baseFee));
|
||||||
auto const pf =
|
auto const pf = preflight(
|
||||||
preflight(env.app(), env.current()->rules(), *jtx.stx, TapNone, env.journal);
|
env.app(), env.current()->rules(), *jtx.stx, ApplyFlags::None, env.journal);
|
||||||
BEAST_EXPECT(isTesSuccess(pf.ter));
|
BEAST_EXPECT(isTesSuccess(pf.ter));
|
||||||
BEAST_EXPECT(!pf.consequences.isBlocker());
|
BEAST_EXPECT(!pf.consequences.isBlocker());
|
||||||
BEAST_EXPECT(pf.consequences.fee() == drops(baseFee));
|
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 jtx = env.jt(escrow::cancel("bob", "alice", 3), Seq(1), Fee(baseFee));
|
||||||
auto const pf =
|
auto const pf = preflight(
|
||||||
preflight(env.app(), env.current()->rules(), *jtx.stx, TapNone, env.journal);
|
env.app(), env.current()->rules(), *jtx.stx, ApplyFlags::None, env.journal);
|
||||||
BEAST_EXPECT(isTesSuccess(pf.ter));
|
BEAST_EXPECT(isTesSuccess(pf.ter));
|
||||||
BEAST_EXPECT(!pf.consequences.isBlocker());
|
BEAST_EXPECT(!pf.consequences.isBlocker());
|
||||||
BEAST_EXPECT(pf.consequences.fee() == drops(baseFee));
|
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 jtx = env.jt(escrow::finish("bob", "alice", 3), Seq(1), Fee(baseFee));
|
||||||
auto const pf =
|
auto const pf = preflight(
|
||||||
preflight(env.app(), env.current()->rules(), *jtx.stx, TapNone, env.journal);
|
env.app(), env.current()->rules(), *jtx.stx, ApplyFlags::None, env.journal);
|
||||||
BEAST_EXPECT(isTesSuccess(pf.ter));
|
BEAST_EXPECT(isTesSuccess(pf.ter));
|
||||||
BEAST_EXPECT(!pf.consequences.isBlocker());
|
BEAST_EXPECT(!pf.consequences.isBlocker());
|
||||||
BEAST_EXPECT(pf.consequences.fee() == drops(baseFee));
|
BEAST_EXPECT(pf.consequences.fee() == drops(baseFee));
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ createInvalidFeeTx(
|
|||||||
bool
|
bool
|
||||||
applyFeeAndTestResult(jtx::Env& env, OpenView& view, STTx const& tx)
|
applyFeeAndTestResult(jtx::Env& env, OpenView& view, STTx const& tx)
|
||||||
{
|
{
|
||||||
auto const res = apply(env.app(), view, tx, ApplyFlags::TapNone, env.journal);
|
auto const res = apply(env.app(), view, tx, ApplyFlags::None, env.journal);
|
||||||
return isTesSuccess(res.ter);
|
return isTesSuccess(res.ter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -558,8 +558,7 @@ class FeeVote_test : public beast::unit_test::Suite
|
|||||||
// Test invalid transaction with non-zero account - this should fail
|
// Test invalid transaction with non-zero account - this should fail
|
||||||
// validation
|
// validation
|
||||||
auto invalidTx = STTx(ttFEE, [&](auto& obj) {
|
auto invalidTx = STTx(ttFEE, [&](auto& obj) {
|
||||||
obj.setAccountID(sfAccount,
|
obj.setAccountID(sfAccount, AccountID{1}); // Should be zero (this makes it invalid)
|
||||||
AccountID(1)); // Should be zero (this makes it invalid)
|
|
||||||
obj.setFieldU32(sfLedgerSequence, ledger->seq());
|
obj.setFieldU32(sfLedgerSequence, ledger->seq());
|
||||||
obj.setFieldAmount(sfBaseFeeDrops, XRPAmount{10});
|
obj.setFieldAmount(sfBaseFeeDrops, XRPAmount{10});
|
||||||
obj.setFieldAmount(sfReserveBaseDrops, XRPAmount{200000});
|
obj.setFieldAmount(sfReserveBaseDrops, XRPAmount{200000});
|
||||||
|
|||||||
@@ -333,7 +333,7 @@ struct FlowMPT_test : public beast::unit_test::Suite
|
|||||||
auto const flowResult = [&] {
|
auto const flowResult = [&] {
|
||||||
STAmount const deliver(usd(5'100));
|
STAmount const deliver(usd(5'100));
|
||||||
STAmount smax(btc(61));
|
STAmount smax(btc(61));
|
||||||
PaymentSandbox sb(env.current().get(), TapNone);
|
PaymentSandbox sb(env.current().get(), ApplyFlags::None);
|
||||||
STPathSet paths;
|
STPathSet paths;
|
||||||
auto ipe = [](Asset const& asset) {
|
auto ipe = [](Asset const& asset) {
|
||||||
return STPathElement(
|
return STPathElement(
|
||||||
@@ -371,7 +371,7 @@ struct FlowMPT_test : public beast::unit_test::Suite
|
|||||||
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
|
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
|
||||||
if (flowResult.removableOffers.empty())
|
if (flowResult.removableOffers.empty())
|
||||||
return false;
|
return false;
|
||||||
Sandbox sb(&view, TapNone);
|
Sandbox sb(&view, ApplyFlags::None);
|
||||||
for (auto const& o : flowResult.removableOffers)
|
for (auto const& o : flowResult.removableOffers)
|
||||||
{
|
{
|
||||||
if (auto ok = sb.peek(keylet::offer(o)))
|
if (auto ok = sb.peek(keylet::offer(o)))
|
||||||
|
|||||||
@@ -476,7 +476,7 @@ struct Flow_test : public beast::unit_test::Suite
|
|||||||
auto const flowResult = [&] {
|
auto const flowResult = [&] {
|
||||||
STAmount const deliver(usd(51));
|
STAmount const deliver(usd(51));
|
||||||
STAmount smax(btc(61));
|
STAmount smax(btc(61));
|
||||||
PaymentSandbox sb(env.current().get(), TapNone);
|
PaymentSandbox sb(env.current().get(), ApplyFlags::None);
|
||||||
STPathSet paths;
|
STPathSet paths;
|
||||||
auto ipe = [](Issue const& iss) {
|
auto ipe = [](Issue const& iss) {
|
||||||
return STPathElement(
|
return STPathElement(
|
||||||
@@ -514,7 +514,7 @@ struct Flow_test : public beast::unit_test::Suite
|
|||||||
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
|
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
|
||||||
if (flowResult.removableOffers.empty())
|
if (flowResult.removableOffers.empty())
|
||||||
return false;
|
return false;
|
||||||
Sandbox sb(&view, TapNone);
|
Sandbox sb(&view, ApplyFlags::None);
|
||||||
for (auto const& o : flowResult.removableOffers)
|
for (auto const& o : flowResult.removableOffers)
|
||||||
{
|
{
|
||||||
if (auto ok = sb.peek(keylet::offer(o)))
|
if (auto ok = sb.peek(keylet::offer(o)))
|
||||||
|
|||||||
@@ -34,13 +34,13 @@ class HashRouter_test : public beast::unit_test::Suite
|
|||||||
TestStopwatch stopwatch;
|
TestStopwatch stopwatch;
|
||||||
HashRouter router(getSetup(2s, 1s), stopwatch);
|
HashRouter router(getSetup(2s, 1s), stopwatch);
|
||||||
|
|
||||||
HashRouterFlags const key1(HashRouterFlags::PRIVATE1);
|
constexpr HashRouterFlags key1(HashRouterFlags::PRIVATE1);
|
||||||
HashRouterFlags const key2(HashRouterFlags::PRIVATE2);
|
constexpr HashRouterFlags key2(HashRouterFlags::PRIVATE2);
|
||||||
HashRouterFlags const key3(HashRouterFlags::PRIVATE3);
|
constexpr HashRouterFlags key3(HashRouterFlags::PRIVATE3);
|
||||||
|
|
||||||
auto const ukey1 = UInt256{static_cast<std::uint64_t>(key1)};
|
UInt256 const ukey1{std::to_underlying(key1)};
|
||||||
auto const ukey2 = UInt256{static_cast<std::uint64_t>(key2)};
|
UInt256 const ukey2{std::to_underlying(key2)};
|
||||||
auto const ukey3 = UInt256{static_cast<std::uint64_t>(key3)};
|
UInt256 const ukey3{std::to_underlying(key3)};
|
||||||
|
|
||||||
// t=0
|
// t=0
|
||||||
router.setFlags(ukey1, HashRouterFlags::PRIVATE1);
|
router.setFlags(ukey1, HashRouterFlags::PRIVATE1);
|
||||||
@@ -76,15 +76,15 @@ class HashRouter_test : public beast::unit_test::Suite
|
|||||||
TestStopwatch stopwatch;
|
TestStopwatch stopwatch;
|
||||||
HashRouter router(getSetup(2s, 1s), stopwatch);
|
HashRouter router(getSetup(2s, 1s), stopwatch);
|
||||||
|
|
||||||
HashRouterFlags const key1(HashRouterFlags::PRIVATE1);
|
constexpr HashRouterFlags key1(HashRouterFlags::PRIVATE1);
|
||||||
HashRouterFlags const key2(HashRouterFlags::PRIVATE2);
|
constexpr HashRouterFlags key2(HashRouterFlags::PRIVATE2);
|
||||||
HashRouterFlags const key3(HashRouterFlags::PRIVATE3);
|
constexpr HashRouterFlags key3(HashRouterFlags::PRIVATE3);
|
||||||
HashRouterFlags const key4(HashRouterFlags::PRIVATE4);
|
constexpr HashRouterFlags key4(HashRouterFlags::PRIVATE4);
|
||||||
|
|
||||||
auto const ukey1 = UInt256{static_cast<std::uint64_t>(key1)};
|
constexpr UInt256 ukey1{std::to_underlying(key1)};
|
||||||
auto const ukey2 = UInt256{static_cast<std::uint64_t>(key2)};
|
constexpr UInt256 ukey2{std::to_underlying(key2)};
|
||||||
auto const ukey3 = UInt256{static_cast<std::uint64_t>(key3)};
|
constexpr UInt256 ukey3{std::to_underlying(key3)};
|
||||||
auto const ukey4 = UInt256{static_cast<std::uint64_t>(key4)};
|
constexpr UInt256 ukey4{std::to_underlying(key4)};
|
||||||
|
|
||||||
BEAST_EXPECT(key1 != key2 && key2 != key3 && key3 != key4);
|
BEAST_EXPECT(key1 != key2 && key2 != key3 && key3 != key4);
|
||||||
|
|
||||||
@@ -162,10 +162,11 @@ class HashRouter_test : public beast::unit_test::Suite
|
|||||||
TestStopwatch stopwatch;
|
TestStopwatch stopwatch;
|
||||||
HashRouter router(getSetup(2s, 1s), stopwatch);
|
HashRouter router(getSetup(2s, 1s), stopwatch);
|
||||||
|
|
||||||
UInt256 const key1(1);
|
constexpr UInt256 key1{1};
|
||||||
UInt256 const key2(2);
|
constexpr UInt256 key2{2};
|
||||||
UInt256 const key3(3);
|
constexpr UInt256 key3{3};
|
||||||
UInt256 const key4(4);
|
constexpr UInt256 key4{4};
|
||||||
|
|
||||||
BEAST_EXPECT(key1 != key2 && key2 != key3 && key3 != key4);
|
BEAST_EXPECT(key1 != key2 && key2 != key3 && key3 != key4);
|
||||||
|
|
||||||
HashRouterFlags flags(HashRouterFlags::BAD); // This value is ignored
|
HashRouterFlags flags(HashRouterFlags::BAD); // This value is ignored
|
||||||
@@ -191,7 +192,8 @@ class HashRouter_test : public beast::unit_test::Suite
|
|||||||
TestStopwatch stopwatch;
|
TestStopwatch stopwatch;
|
||||||
HashRouter router(getSetup(2s, 1s), stopwatch);
|
HashRouter router(getSetup(2s, 1s), stopwatch);
|
||||||
|
|
||||||
UInt256 const key1(1);
|
constexpr UInt256 key1{1};
|
||||||
|
|
||||||
BEAST_EXPECT(router.setFlags(key1, HashRouterFlags::PRIVATE1));
|
BEAST_EXPECT(router.setFlags(key1, HashRouterFlags::PRIVATE1));
|
||||||
BEAST_EXPECT(!router.setFlags(key1, HashRouterFlags::PRIVATE1));
|
BEAST_EXPECT(!router.setFlags(key1, HashRouterFlags::PRIVATE1));
|
||||||
BEAST_EXPECT(router.setFlags(key1, HashRouterFlags::PRIVATE2));
|
BEAST_EXPECT(router.setFlags(key1, HashRouterFlags::PRIVATE2));
|
||||||
@@ -205,7 +207,7 @@ class HashRouter_test : public beast::unit_test::Suite
|
|||||||
TestStopwatch stopwatch;
|
TestStopwatch stopwatch;
|
||||||
HashRouter router(getSetup(50s, 1s), stopwatch);
|
HashRouter router(getSetup(50s, 1s), stopwatch);
|
||||||
|
|
||||||
UInt256 const key1(1);
|
constexpr UInt256 key1{1};
|
||||||
|
|
||||||
std::optional<std::set<HashRouter::PeerShortID>> peers;
|
std::optional<std::set<HashRouter::PeerShortID>> peers;
|
||||||
|
|
||||||
@@ -248,7 +250,7 @@ class HashRouter_test : public beast::unit_test::Suite
|
|||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
TestStopwatch stopwatch;
|
TestStopwatch stopwatch;
|
||||||
HashRouter router(getSetup(5s, 1s), stopwatch);
|
HashRouter router(getSetup(5s, 1s), stopwatch);
|
||||||
UInt256 const key(1);
|
constexpr UInt256 key{1};
|
||||||
HashRouter::PeerShortID const peer = 1;
|
HashRouter::PeerShortID const peer = 1;
|
||||||
HashRouterFlags flags = HashRouterFlags::UNDEFINED;
|
HashRouterFlags flags = HashRouterFlags::UNDEFINED;
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ namespace xrpl::test {
|
|||||||
|
|
||||||
class LedgerHistory_test : public beast::unit_test::Suite
|
class LedgerHistory_test : public beast::unit_test::Suite
|
||||||
{
|
{
|
||||||
|
UInt256 const dummyTxHash_ = uint256{1};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Generate a new ledger by hand, applying a specific close time offset
|
* Generate a new ledger by hand, applying a specific close time offset
|
||||||
@@ -59,7 +61,7 @@ public:
|
|||||||
if (stx)
|
if (stx)
|
||||||
{
|
{
|
||||||
OpenView accum(&*res);
|
OpenView accum(&*res);
|
||||||
applyTransaction(env.app(), accum, *stx, false, TapNone, env.journal);
|
applyTransaction(env.app(), accum, *stx, false, ApplyFlags::None, env.journal);
|
||||||
accum.apply(*res);
|
accum.apply(*res);
|
||||||
}
|
}
|
||||||
res->updateSkipList();
|
res->updateSkipList();
|
||||||
@@ -92,9 +94,8 @@ public:
|
|||||||
Env env{*this, envconfig(), std::make_unique<CheckMessageLogs>("MISMATCH ", &found)};
|
Env env{*this, envconfig(), std::make_unique<CheckMessageLogs>("MISMATCH ", &found)};
|
||||||
LedgerHistory lh{beast::insight::NullCollector::make(), env.app()};
|
LedgerHistory lh{beast::insight::NullCollector::make(), env.app()};
|
||||||
auto const genesis = makeLedger({}, env, lh, 0s);
|
auto const genesis = makeLedger({}, env, lh, 0s);
|
||||||
UInt256 const dummyTxHash{1};
|
lh.builtLedger(genesis, dummyTxHash_, {});
|
||||||
lh.builtLedger(genesis, dummyTxHash, {});
|
lh.validatedLedger(genesis, dummyTxHash_);
|
||||||
lh.validatedLedger(genesis, dummyTxHash);
|
|
||||||
|
|
||||||
BEAST_EXPECT(!found);
|
BEAST_EXPECT(!found);
|
||||||
}
|
}
|
||||||
@@ -111,9 +112,8 @@ public:
|
|||||||
auto const ledgerA = makeLedger(genesis, env, lh, 4s);
|
auto const ledgerA = makeLedger(genesis, env, lh, 4s);
|
||||||
auto const ledgerB = makeLedger(genesis, env, lh, 40s);
|
auto const ledgerB = makeLedger(genesis, env, lh, 40s);
|
||||||
|
|
||||||
UInt256 const dummyTxHash{1};
|
lh.builtLedger(ledgerA, dummyTxHash_, {});
|
||||||
lh.builtLedger(ledgerA, dummyTxHash, {});
|
lh.validatedLedger(ledgerB, dummyTxHash_);
|
||||||
lh.validatedLedger(ledgerB, dummyTxHash);
|
|
||||||
|
|
||||||
BEAST_EXPECT(found);
|
BEAST_EXPECT(found);
|
||||||
}
|
}
|
||||||
@@ -132,9 +132,8 @@ public:
|
|||||||
auto const ledgerAC = makeLedger(ledgerA, env, lh, 4s);
|
auto const ledgerAC = makeLedger(ledgerA, env, lh, 4s);
|
||||||
auto const ledgerBD = makeLedger(ledgerB, env, lh, 4s);
|
auto const ledgerBD = makeLedger(ledgerB, env, lh, 4s);
|
||||||
|
|
||||||
UInt256 const dummyTxHash{1};
|
lh.builtLedger(ledgerAC, dummyTxHash_, {});
|
||||||
lh.builtLedger(ledgerAC, dummyTxHash, {});
|
lh.validatedLedger(ledgerBD, dummyTxHash_);
|
||||||
lh.validatedLedger(ledgerBD, dummyTxHash);
|
|
||||||
|
|
||||||
BEAST_EXPECT(found);
|
BEAST_EXPECT(found);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ struct LedgerReplay_test : public beast::unit_test::Suite
|
|||||||
auto const lastClosedParent = ledgerMaster.getLedgerByHash(lastClosed->header().parentHash);
|
auto const lastClosedParent = ledgerMaster.getLedgerByHash(lastClosed->header().parentHash);
|
||||||
|
|
||||||
auto const replayed = buildLedger(
|
auto const replayed = buildLedger(
|
||||||
LedgerReplay(lastClosedParent, lastClosed), TapNone, env.app(), env.journal);
|
LedgerReplay(lastClosedParent, lastClosed), ApplyFlags::None, env.app(), env.journal);
|
||||||
|
|
||||||
BEAST_EXPECT(replayed->header().hash == lastClosed->header().hash);
|
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 lastClosedParent = ledgerMaster.getLedgerByHash(lastClosed->header().parentHash);
|
||||||
|
|
||||||
auto const replayed = buildLedger(
|
auto const replayed = buildLedger(
|
||||||
LedgerReplay(lastClosedParent, lastClosed), TapNone, env.app(), env.journal);
|
LedgerReplay(lastClosedParent, lastClosed), ApplyFlags::None, env.app(), env.journal);
|
||||||
|
|
||||||
BEAST_EXPECT(replayed->header().hash == lastClosed->header().hash);
|
BEAST_EXPECT(replayed->header().hash == lastClosed->header().hash);
|
||||||
}
|
}
|
||||||
@@ -857,7 +857,7 @@ struct LedgerReplayer_test : public beast::unit_test::Suite
|
|||||||
auto request = std::make_shared<protocol::TMProofPathRequest>();
|
auto request = std::make_shared<protocol::TMProofPathRequest>();
|
||||||
request->set_type(protocol::TMLedgerMapType::lmACCOUNT_STATE);
|
request->set_type(protocol::TMLedgerMapType::lmACCOUNT_STATE);
|
||||||
request->set_key(keylet::skip().key.data(), keylet::skip().key.size());
|
request->set_key(keylet::skip().key.data(), keylet::skip().key.size());
|
||||||
UInt256 hash(1234567);
|
constexpr UInt256 hash{1234567};
|
||||||
request->set_ledgerhash(hash.data(), hash.size());
|
request->set_ledgerhash(hash.data(), hash.size());
|
||||||
auto reply = std::make_shared<protocol::TMProofPathResponse>(
|
auto reply = std::make_shared<protocol::TMProofPathResponse>(
|
||||||
server.msgHandler.processProofPathRequest(request));
|
server.msgHandler.processProofPathRequest(request));
|
||||||
@@ -874,7 +874,8 @@ struct LedgerReplayer_test : public beast::unit_test::Suite
|
|||||||
auto reply = std::make_shared<protocol::TMProofPathResponse>(
|
auto reply = std::make_shared<protocol::TMProofPathResponse>(
|
||||||
server.msgHandler.processProofPathRequest(request));
|
server.msgHandler.processProofPathRequest(request));
|
||||||
BEAST_EXPECT(!reply->has_error());
|
BEAST_EXPECT(!reply->has_error());
|
||||||
BEAST_EXPECT(server.msgHandler.processProofPathResponse(reply) == ReplayMsgStatus::Ok);
|
auto resp = server.msgHandler.processProofPathResponse(reply);
|
||||||
|
BEAST_EXPECT(resp == ReplayMsgStatus::Ok);
|
||||||
|
|
||||||
{
|
{
|
||||||
// bad reply: invalid hash/key sizes
|
// bad reply: invalid hash/key sizes
|
||||||
@@ -966,7 +967,7 @@ struct LedgerReplayer_test : public beast::unit_test::Suite
|
|||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
server.msgHandler.processReplayDeltaResponse(reply) == ReplayMsgStatus::BadData);
|
server.msgHandler.processReplayDeltaResponse(reply) == ReplayMsgStatus::BadData);
|
||||||
// request, wrong hash
|
// request, wrong hash
|
||||||
UInt256 hash(1234567);
|
constexpr UInt256 hash{1234567};
|
||||||
request->set_ledgerhash(hash.data(), hash.size());
|
request->set_ledgerhash(hash.data(), hash.size());
|
||||||
reply = std::make_shared<protocol::TMReplayDeltaResponse>(
|
reply = std::make_shared<protocol::TMReplayDeltaResponse>(
|
||||||
server.msgHandler.processReplayDeltaRequest(request));
|
server.msgHandler.processReplayDeltaRequest(request));
|
||||||
@@ -1095,24 +1096,27 @@ struct LedgerReplayer_test : public beast::unit_test::Suite
|
|||||||
{
|
{
|
||||||
testcase("TaskParameter");
|
testcase("TaskParameter");
|
||||||
|
|
||||||
auto makeSkipList = [](int count) -> std::vector<UInt256> {
|
auto const makeSkipList = [](std::size_t count) {
|
||||||
std::vector<UInt256> sList;
|
std::vector<UInt256> sList;
|
||||||
sList.reserve(count);
|
sList.reserve(count);
|
||||||
for (int i = 0; i < count; ++i)
|
|
||||||
sList.emplace_back(i);
|
UInt256 id{};
|
||||||
|
for (std::size_t i = 0; i != count; ++i, ++id)
|
||||||
|
sList.push_back(id);
|
||||||
|
|
||||||
return sList;
|
return sList;
|
||||||
};
|
};
|
||||||
|
|
||||||
LedgerReplayTask::TaskParameter tp10(InboundLedger::Reason::GENERIC, UInt256(10), 10);
|
LedgerReplayTask::TaskParameter tp10(InboundLedger::Reason::GENERIC, UInt256{10}, 10);
|
||||||
BEAST_EXPECT(!tp10.update(UInt256(777), 5, makeSkipList(10)));
|
BEAST_EXPECT(!tp10.update(UInt256{777}, 5, makeSkipList(10)));
|
||||||
BEAST_EXPECT(!tp10.update(UInt256(10), 5, makeSkipList(8)));
|
BEAST_EXPECT(!tp10.update(UInt256{10}, 5, makeSkipList(8)));
|
||||||
BEAST_EXPECT(tp10.update(UInt256(10), 10, makeSkipList(10)));
|
BEAST_EXPECT(tp10.update(UInt256{10}, 10, makeSkipList(10)));
|
||||||
|
|
||||||
// can merge to self
|
// can merge to self
|
||||||
BEAST_EXPECT(tp10.canMergeInto(tp10));
|
BEAST_EXPECT(tp10.canMergeInto(tp10));
|
||||||
|
|
||||||
// smaller task
|
// smaller task
|
||||||
LedgerReplayTask::TaskParameter tp9(InboundLedger::Reason::GENERIC, UInt256(9), 9);
|
LedgerReplayTask::TaskParameter tp9(InboundLedger::Reason::GENERIC, UInt256{9}, 9);
|
||||||
|
|
||||||
BEAST_EXPECT(tp9.canMergeInto(tp10));
|
BEAST_EXPECT(tp9.canMergeInto(tp10));
|
||||||
BEAST_EXPECT(!tp10.canMergeInto(tp9));
|
BEAST_EXPECT(!tp10.canMergeInto(tp9));
|
||||||
@@ -1127,14 +1131,14 @@ struct LedgerReplayer_test : public beast::unit_test::Suite
|
|||||||
tp9.reason = InboundLedger::Reason::GENERIC;
|
tp9.reason = InboundLedger::Reason::GENERIC;
|
||||||
BEAST_EXPECT(tp9.canMergeInto(tp10));
|
BEAST_EXPECT(tp9.canMergeInto(tp10));
|
||||||
|
|
||||||
tp9.finishHash = UInt256(1234);
|
tp9.finishHash = UInt256{1234};
|
||||||
BEAST_EXPECT(!tp9.canMergeInto(tp10));
|
BEAST_EXPECT(!tp9.canMergeInto(tp10));
|
||||||
tp9.finishHash = UInt256(9);
|
tp9.finishHash = UInt256{9};
|
||||||
BEAST_EXPECT(tp9.canMergeInto(tp10));
|
BEAST_EXPECT(tp9.canMergeInto(tp10));
|
||||||
|
|
||||||
// larger task
|
// larger task
|
||||||
LedgerReplayTask::TaskParameter tp20(InboundLedger::Reason::GENERIC, UInt256(20), 20);
|
LedgerReplayTask::TaskParameter tp20(InboundLedger::Reason::GENERIC, UInt256{20}, 20);
|
||||||
BEAST_EXPECT(tp20.update(UInt256(20), 20, makeSkipList(20)));
|
BEAST_EXPECT(tp20.update(UInt256{20}, 20, makeSkipList(20)));
|
||||||
BEAST_EXPECT(tp10.canMergeInto(tp20));
|
BEAST_EXPECT(tp10.canMergeInto(tp20));
|
||||||
BEAST_EXPECT(tp9.canMergeInto(tp20));
|
BEAST_EXPECT(tp9.canMergeInto(tp20));
|
||||||
BEAST_EXPECT(!tp20.canMergeInto(tp10));
|
BEAST_EXPECT(!tp20.canMergeInto(tp10));
|
||||||
@@ -1368,7 +1372,7 @@ struct LedgerReplayer_test : public beast::unit_test::Suite
|
|||||||
auto skipList = net.client.findSkipListAcquire(finalHash);
|
auto skipList = net.client.findSkipListAcquire(finalHash);
|
||||||
|
|
||||||
std::uint8_t payload[55] = {0x6A, 0x09, 0xE6, 0x67, 0xF3, 0xBC, 0xC9, 0x08, 0xB2};
|
std::uint8_t payload[55] = {0x6A, 0x09, 0xE6, 0x67, 0xF3, 0xBC, 0xC9, 0x08, 0xB2};
|
||||||
auto item = makeShamapitem(UInt256(12345), Slice(payload, sizeof(payload)));
|
auto item = makeShamapitem(UInt256{12345}, Slice(payload, sizeof(payload)));
|
||||||
skipList->processData(l->seq(), item);
|
skipList->processData(l->seq(), item);
|
||||||
|
|
||||||
std::vector<TaskStatus> const deltaStatuses;
|
std::vector<TaskStatus> const deltaStatuses;
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ class MPToken_test : public beast::unit_test::Suite
|
|||||||
.assetScale = 0,
|
.assetScale = 0,
|
||||||
.metadata = "test",
|
.metadata = "test",
|
||||||
.flags = tfMPTRequireAuth,
|
.flags = tfMPTRequireAuth,
|
||||||
.domainID = UInt256(42),
|
.domainID{42},
|
||||||
.err = temDISABLED});
|
.err = temDISABLED});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -132,7 +132,7 @@ class MPToken_test : public beast::unit_test::Suite
|
|||||||
{.maxAmt = 100,
|
{.maxAmt = 100,
|
||||||
.assetScale = 0,
|
.assetScale = 0,
|
||||||
.metadata = "test",
|
.metadata = "test",
|
||||||
.domainID = UInt256(42),
|
.domainID{42},
|
||||||
.err = temMALFORMED});
|
.err = temMALFORMED});
|
||||||
|
|
||||||
// tries to set zero DomainID
|
// tries to set zero DomainID
|
||||||
@@ -622,7 +622,7 @@ class MPToken_test : public beast::unit_test::Suite
|
|||||||
.err = tecNO_PERMISSION});
|
.err = tecNO_PERMISSION});
|
||||||
|
|
||||||
// cannot set DomainID since SAV is not enabled
|
// cannot set DomainID since SAV is not enabled
|
||||||
mptAlice.set({.account = alice, .domainID = UInt256(42), .err = temDISABLED});
|
mptAlice.set({.account = alice, .domainID{42}, .err = temDISABLED});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -635,16 +635,13 @@ class MPToken_test : public beast::unit_test::Suite
|
|||||||
if (!features[featurePermissionedDomains] || !features[featureSingleAssetVault])
|
if (!features[featurePermissionedDomains] || !features[featureSingleAssetVault])
|
||||||
{
|
{
|
||||||
// cannot set DomainID since PD is not enabled
|
// cannot set DomainID since PD is not enabled
|
||||||
mptAlice.set({.account = alice, .domainID = UInt256(42), .err = temDISABLED});
|
mptAlice.set({.account = alice, .domainID{42}, .err = temDISABLED});
|
||||||
}
|
}
|
||||||
else if (features[featureSingleAssetVault])
|
else if (features[featureSingleAssetVault])
|
||||||
{
|
{
|
||||||
// cannot set DomainID since Holder is set
|
// cannot set DomainID since Holder is set
|
||||||
mptAlice.set(
|
mptAlice.set(
|
||||||
{.account = alice,
|
{.account = alice, .holder = bob, .domainID{42}, .err = temMALFORMED});
|
||||||
.holder = bob,
|
|
||||||
.domainID = UInt256(42),
|
|
||||||
.err = temMALFORMED});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -725,7 +722,7 @@ class MPToken_test : public beast::unit_test::Suite
|
|||||||
mptAlice.create({});
|
mptAlice.create({});
|
||||||
|
|
||||||
// Trying to set DomainID on a public MPTokenIssuance
|
// Trying to set DomainID on a public MPTokenIssuance
|
||||||
mptAlice.set({.domainID = UInt256(42), .err = tecNO_PERMISSION});
|
mptAlice.set({.domainID = uint256{42}, .err = tecNO_PERMISSION});
|
||||||
|
|
||||||
mptAlice.set({.domainID = UInt256{}, .err = tecNO_PERMISSION});
|
mptAlice.set({.domainID = UInt256{}, .err = tecNO_PERMISSION});
|
||||||
}
|
}
|
||||||
@@ -737,11 +734,10 @@ class MPToken_test : public beast::unit_test::Suite
|
|||||||
mptAlice.create({.flags = tfMPTRequireAuth});
|
mptAlice.create({.flags = tfMPTRequireAuth});
|
||||||
|
|
||||||
// Trying to set non-existing DomainID
|
// Trying to set non-existing DomainID
|
||||||
mptAlice.set({.domainID = UInt256(42), .err = tecOBJECT_NOT_FOUND});
|
mptAlice.set({.domainID = uint256{42}, .err = tecOBJECT_NOT_FOUND});
|
||||||
|
|
||||||
// Trying to lock but locking is disabled
|
// Trying to lock but locking is disabled
|
||||||
mptAlice.set(
|
mptAlice.set({.flags = tfMPTUnlock, .domainID{42}, .err = tecNO_PERMISSION});
|
||||||
{.flags = tfMPTUnlock, .domainID = UInt256(42), .err = tecNO_PERMISSION});
|
|
||||||
|
|
||||||
mptAlice.set(
|
mptAlice.set(
|
||||||
{.flags = tfMPTUnlock, .domainID = UInt256{}, .err = tecNO_PERMISSION});
|
{.flags = tfMPTUnlock, .domainID = UInt256{}, .err = tecNO_PERMISSION});
|
||||||
@@ -1655,7 +1651,7 @@ class MPToken_test : public beast::unit_test::Suite
|
|||||||
|
|
||||||
env.fund(XRP(1'000), alice, bob);
|
env.fund(XRP(1'000), alice, bob);
|
||||||
|
|
||||||
STAmount const mpt{MPTID{0}, 100};
|
STAmount const mpt{MPTID{}, 100};
|
||||||
auto const err =
|
auto const err =
|
||||||
!features[featureMPTokensV2] ? Ter(tecOBJECT_NOT_FOUND) : Ter(temBAD_CURRENCY);
|
!features[featureMPTokensV2] ? Ter(tecOBJECT_NOT_FOUND) : Ter(temBAD_CURRENCY);
|
||||||
env(pay(alice, bob, mpt), err);
|
env(pay(alice, bob, mpt), err);
|
||||||
@@ -4059,7 +4055,7 @@ class MPToken_test : public beast::unit_test::Suite
|
|||||||
TER expectedTer,
|
TER expectedTer,
|
||||||
std::optional<std::uint64_t> expectedOutstanding,
|
std::optional<std::uint64_t> expectedOutstanding,
|
||||||
std::string const& label) {
|
std::string const& label) {
|
||||||
ApplyViewImpl av(&*env.current(), TapNone);
|
ApplyViewImpl av(&*env.current(), ApplyFlags::None);
|
||||||
auto const ter =
|
auto const ter =
|
||||||
accountSendMulti(av, issuer.id(), asset, receivers, env.app().getJournal("View"));
|
accountSendMulti(av, issuer.id(), asset, receivers, env.app().getJournal("View"));
|
||||||
BEAST_EXPECTS(ter == expectedTer, label);
|
BEAST_EXPECTS(ter == expectedTer, label);
|
||||||
|
|||||||
@@ -1415,17 +1415,18 @@ public:
|
|||||||
Account const alice{"alice", KeyType::Ed25519};
|
Account const alice{"alice", KeyType::Ed25519};
|
||||||
env.fund(XRP(1000), alice);
|
env.fund(XRP(1000), alice);
|
||||||
env.close();
|
env.close();
|
||||||
uint8_t tag1[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03,
|
|
||||||
0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
|
|
||||||
0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
|
|
||||||
|
|
||||||
uint8_t tag2[] = "hello world some ascii 32b long"; // including 1 byte for NUL
|
|
||||||
|
|
||||||
UInt256 bogieTag = xrpl::BaseUInt<256>::fromVoid(tag1);
|
|
||||||
UInt256 demonTag = xrpl::BaseUInt<256>::fromVoid(tag2);
|
|
||||||
|
|
||||||
// Attach phantom signers to alice and use them for a transaction.
|
// Attach phantom signers to alice and use them for a transaction.
|
||||||
env(signers(alice, 1, {{bogie_, 1, bogieTag}, {demon_, 1, demonTag}}));
|
// The tags are arbitrary strings without meaning for this test.
|
||||||
|
env(signers(
|
||||||
|
alice,
|
||||||
|
1,
|
||||||
|
{{bogie_,
|
||||||
|
1,
|
||||||
|
UInt256{"FABC2EB666E9B9D7A8E897E1AC446E13CE3189A9D81554AC5F46B0B159932CB3"}},
|
||||||
|
{demon_,
|
||||||
|
1,
|
||||||
|
UInt256{"7DA3B4CC19DFE4269C95F9A9B8923BBEE3F4DC4EB9D6D503DD0CA10191C2D5C0"}}}));
|
||||||
env.close();
|
env.close();
|
||||||
env.require(Owners(alice, 1));
|
env.require(Owners(alice, 1));
|
||||||
|
|
||||||
|
|||||||
@@ -776,7 +776,13 @@ class NFTokenBurn_test : public beast::unit_test::Suite
|
|||||||
test::StreamSink sink{beast::Severity::Warning};
|
test::StreamSink sink{beast::Severity::Warning};
|
||||||
beast::Journal const jlog{sink};
|
beast::Journal const jlog{sink};
|
||||||
ApplyContext ac{
|
ApplyContext ac{
|
||||||
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, jlog};
|
env.app(),
|
||||||
|
ov,
|
||||||
|
tx,
|
||||||
|
tesSUCCESS,
|
||||||
|
env.current()->fees().base,
|
||||||
|
ApplyFlags::None,
|
||||||
|
jlog};
|
||||||
|
|
||||||
// Verify that the last page is present and contains one NFT.
|
// Verify that the last page is present and contains one NFT.
|
||||||
auto lastNFTokenPage = ac.view().peek(keylet::nftokenPageMax(alice));
|
auto lastNFTokenPage = ac.view().peek(keylet::nftokenPageMax(alice));
|
||||||
@@ -808,7 +814,13 @@ class NFTokenBurn_test : public beast::unit_test::Suite
|
|||||||
test::StreamSink sink{beast::Severity::Warning};
|
test::StreamSink sink{beast::Severity::Warning};
|
||||||
beast::Journal const jlog{sink};
|
beast::Journal const jlog{sink};
|
||||||
ApplyContext ac{
|
ApplyContext ac{
|
||||||
env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, TapNone, jlog};
|
env.app(),
|
||||||
|
ov,
|
||||||
|
tx,
|
||||||
|
tesSUCCESS,
|
||||||
|
env.current()->fees().base,
|
||||||
|
ApplyFlags::None,
|
||||||
|
jlog};
|
||||||
|
|
||||||
// Verify that the middle page is present.
|
// Verify that the middle page is present.
|
||||||
auto lastNFTokenPage = ac.view().peek(keylet::nftokenPageMax(alice));
|
auto lastNFTokenPage = ac.view().peek(keylet::nftokenPageMax(alice));
|
||||||
|
|||||||
@@ -6219,16 +6219,10 @@ public:
|
|||||||
// The type tag is a front prefix and the domain is a 32-byte suffix, so a
|
// The type tag is a front prefix and the domain is a 32-byte suffix, so a
|
||||||
// domain'd book must (a) stay distinct from its public counterpart and
|
// domain'd book must (a) stay distinct from its public counterpart and
|
||||||
// (b) preserve the mixed-branch tag distinction that the public case has.
|
// (b) preserve the mixed-branch tag distinction that the public case has.
|
||||||
AccountID issuerX, issuerY, iouIssuer;
|
constexpr AccountID issuerX{1};
|
||||||
Currency currency;
|
constexpr AccountID iouIssuer{2};
|
||||||
BEAST_EXPECT(issuerX.parseHex("1111111111111111111111111111111111111111"));
|
constexpr Currency currency{3};
|
||||||
BEAST_EXPECT(issuerY.parseHex("2222222222222222222222222222222222222222"));
|
constexpr uint256 domainA{4};
|
||||||
BEAST_EXPECT(currency.parseHex("3333333333333333333333333333333333333333"));
|
|
||||||
BEAST_EXPECT(iouIssuer.parseHex("4444444444444444444444444444444444444444"));
|
|
||||||
|
|
||||||
UInt256 const domainA = UInt256::fromVoid(
|
|
||||||
"\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD"
|
|
||||||
"\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD");
|
|
||||||
|
|
||||||
Asset const mptX{MPTIssue{1u, issuerX}};
|
Asset const mptX{MPTIssue{1u, issuerX}};
|
||||||
Asset const iou{Issue{currency, iouIssuer}};
|
Asset const iou{Issue{currency, iouIssuer}};
|
||||||
|
|||||||
@@ -1930,23 +1930,14 @@ public:
|
|||||||
{
|
{
|
||||||
testcase("STPathSet::assembleAdd deduplication — O(N^2) regression");
|
testcase("STPathSet::assembleAdd deduplication — O(N^2) regression");
|
||||||
|
|
||||||
static constexpr std::string_view kAccount1 = "A3F19C7B2E5D08146FB93A7C0E2D5184BC6F3A09";
|
constexpr AccountID kAccountID1{"A3F19C7B2E5D08146FB93A7C0E2D5184BC6F3A09"};
|
||||||
static constexpr std::string_view kAccount2 = "1D7E4B90C2A6F3851E0B9D47A2C5F8136E0A4B7D";
|
constexpr AccountID kAccountID2{"1D7E4B90C2A6F3851E0B9D47A2C5F8136E0A4B7D"};
|
||||||
static constexpr std::string_view kAccount3 = "F08C36A1D95E27B40CA1F63E8D204B7950E1C3A6";
|
constexpr AccountID kAccountID3{"F08C36A1D95E27B40CA1F63E8D204B7950E1C3A6"};
|
||||||
static constexpr std::string_view kAccount4 = "4B6209E7F1A3C85D0E94B27Af3D6018C5A7E92B4";
|
constexpr AccountID kAccountID4{"4B6209E7F1A3C85D0E94B27Af3D6018C5A7E92B4"};
|
||||||
static constexpr std::string_view kAccount5 = "9E2D7041BCA3F6589D013E7B2A4C6F80159D3E7A";
|
constexpr AccountID kAccountID5{"9E2D7041BCA3F6589D013E7B2A4C6F80159D3E7A"};
|
||||||
static constexpr std::string_view kAccount6 = "7C5A91E384F2D06BA19C4E73D820F516B3A9C0E4";
|
constexpr AccountID kAccountID6{"7C5A91E384F2D06BA19C4E73D820F516B3A9C0E4"};
|
||||||
static constexpr std::string_view kAccount7 = "2F8B043C6A1E9D75B0C38E14F6A2D509731BC4E8";
|
constexpr AccountID kAccountID7{"2F8B043C6A1E9D75B0C38E14F6A2D509731BC4E8"};
|
||||||
static constexpr std::string_view kAccount8 = "E61D9A30F47C285BA0D31E96C7B4F802513A8D6F";
|
constexpr AccountID kAccountID8{"E61D9A30F47C285BA0D31E96C7B4F802513A8D6F"};
|
||||||
|
|
||||||
static constexpr AccountID kAccountID1{kAccount1};
|
|
||||||
static constexpr AccountID kAccountID2{kAccount2};
|
|
||||||
static constexpr AccountID kAccountID3{kAccount3};
|
|
||||||
static constexpr AccountID kAccountID4{kAccount4};
|
|
||||||
static constexpr AccountID kAccountID5{kAccount5};
|
|
||||||
static constexpr AccountID kAccountID6{kAccount6};
|
|
||||||
static constexpr AccountID kAccountID7{kAccount7};
|
|
||||||
static constexpr AccountID kAccountID8{kAccount8};
|
|
||||||
|
|
||||||
auto ps = STPathSet{STPathSet::DeduplicationTag{}};
|
auto ps = STPathSet{STPathSet::DeduplicationTag{}};
|
||||||
|
|
||||||
|
|||||||
@@ -1404,6 +1404,30 @@ struct PayChan_test : public beast::unit_test::Suite
|
|||||||
rv = env.rpc("channel_authorize", "alice", chan1StrBad, "1000");
|
rv = env.rpc("channel_authorize", "alice", chan1StrBad, "1000");
|
||||||
BEAST_EXPECT(rv[jss::error] == "channelMalformed");
|
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
|
// give an ill formed base 58 public key
|
||||||
auto illFormedPk = chan1PkStr.substr(0, chan1PkStr.size() - 1);
|
auto illFormedPk = chan1PkStr.substr(0, chan1PkStr.size() - 1);
|
||||||
|
|||||||
@@ -572,7 +572,7 @@ struct PayStrandMPT_test : public beast::unit_test::Suite
|
|||||||
inputs.defaultPathsAllowed = true;
|
inputs.defaultPathsAllowed = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
PaymentSandbox sb{env.current().get(), TapNone};
|
PaymentSandbox sb{env.current().get(), ApplyFlags::None};
|
||||||
{
|
{
|
||||||
auto const r = ::xrpl::path::RippleCalc::rippleCalculate(
|
auto const r = ::xrpl::path::RippleCalc::rippleCalculate(
|
||||||
sb,
|
sb,
|
||||||
|
|||||||
@@ -768,7 +768,8 @@ struct PayStrand_test : public beast::unit_test::Suite
|
|||||||
env,
|
env,
|
||||||
usd,
|
usd,
|
||||||
std::nullopt,
|
std::nullopt,
|
||||||
STPath({STPathElement(0, xrpAccount(), xrpCurrency(), xrpAccount())}),
|
STPath({STPathElement(
|
||||||
|
STPathElement::Type::TypeNone, xrpAccount(), xrpCurrency(), xrpAccount())}),
|
||||||
temBAD_PATH);
|
temBAD_PATH);
|
||||||
|
|
||||||
// The same account can't appear more than once on a path
|
// The same account can't appear more than once on a path
|
||||||
@@ -1076,7 +1077,7 @@ struct PayStrand_test : public beast::unit_test::Suite
|
|||||||
inputs.defaultPathsAllowed = true;
|
inputs.defaultPathsAllowed = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
PaymentSandbox sb{env.current().get(), TapNone};
|
PaymentSandbox sb{env.current().get(), ApplyFlags::None};
|
||||||
{
|
{
|
||||||
auto const r = ::xrpl::path::RippleCalc::rippleCalculate(
|
auto const r = ::xrpl::path::RippleCalc::rippleCalculate(
|
||||||
sb,
|
sb,
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ using namespace jtx;
|
|||||||
|
|
||||||
class PermissionedDEX_test : public beast::unit_test::Suite
|
class PermissionedDEX_test : public beast::unit_test::Suite
|
||||||
{
|
{
|
||||||
|
UInt256 const badDomain_{"F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134E5"};
|
||||||
|
|
||||||
[[nodiscard]] static bool
|
[[nodiscard]] static bool
|
||||||
offerExists(Env const& env, Account const& account, std::uint32_t offerSeq)
|
offerExists(Env const& env, Account const& account, std::uint32_t offerSeq)
|
||||||
{
|
{
|
||||||
@@ -289,11 +291,7 @@ class PermissionedDEX_test : public beast::unit_test::Suite
|
|||||||
Env env(*this, features);
|
Env env(*this, features);
|
||||||
auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
|
auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
|
||||||
PermissionedDEX(env);
|
PermissionedDEX(env);
|
||||||
UInt256 const badDomain{
|
env(offer(bob, XRP(10), USD(10)), Domain(badDomain_), Ter(tecNO_PERMISSION));
|
||||||
"F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134"
|
|
||||||
"E5"};
|
|
||||||
|
|
||||||
env(offer(bob, XRP(10), USD(10)), Domain(badDomain), Ter(tecNO_PERMISSION));
|
|
||||||
env.close();
|
env.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,14 +436,11 @@ class PermissionedDEX_test : public beast::unit_test::Suite
|
|||||||
Env env(*this, features);
|
Env env(*this, features);
|
||||||
auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
|
auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
|
||||||
PermissionedDEX(env);
|
PermissionedDEX(env);
|
||||||
UInt256 const badDomain{
|
|
||||||
"F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134"
|
|
||||||
"E5"};
|
|
||||||
|
|
||||||
env(pay(bob, alice, USD(10)),
|
env(pay(bob, alice, USD(10)),
|
||||||
Path(~USD),
|
Path(~USD),
|
||||||
Sendmax(XRP(10)),
|
Sendmax(XRP(10)),
|
||||||
Domain(badDomain),
|
Domain(badDomain_),
|
||||||
Ter(tecNO_PERMISSION));
|
Ter(tecNO_PERMISSION));
|
||||||
env.close();
|
env.close();
|
||||||
}
|
}
|
||||||
@@ -1700,17 +1695,13 @@ class PermissionedDEX_test : public beast::unit_test::Suite
|
|||||||
auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
|
auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
|
||||||
PermissionedDEX(env);
|
PermissionedDEX(env);
|
||||||
|
|
||||||
UInt256 const badDomain{
|
|
||||||
"F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134"
|
|
||||||
"E5"};
|
|
||||||
|
|
||||||
env(offer(bob, XRP(10), USD(10)), Domain(domainID));
|
env(offer(bob, XRP(10), USD(10)), Domain(domainID));
|
||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
env(pay(alice, bob, USD(10)),
|
env(pay(alice, bob, USD(10)),
|
||||||
Path(~USD),
|
Path(~USD),
|
||||||
Sendmax(XRP(10)),
|
Sendmax(XRP(10)),
|
||||||
Domain(badDomain),
|
Domain(badDomain_),
|
||||||
Ter(tecNO_PERMISSION));
|
Ter(tecNO_PERMISSION));
|
||||||
env.close();
|
env.close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ class PermissionedDomains_test : public beast::unit_test::Suite
|
|||||||
env.fund(XRP(1000), alice);
|
env.fund(XRP(1000), alice);
|
||||||
pdomain::Credentials const credentials{{.issuer = alice, .credType = "first credential"}};
|
pdomain::Credentials const credentials{{.issuer = alice, .credType = "first credential"}};
|
||||||
env(pdomain::setTx(alice, credentials), Ter(temDISABLED));
|
env(pdomain::setTx(alice, credentials), Ter(temDISABLED));
|
||||||
env(pdomain::deleteTx(alice, UInt256(75)), Ter(temDISABLED));
|
env(pdomain::deleteTx(alice, UInt256{75}), Ter(temDISABLED));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that bad inputs fail for each of create new and update
|
// Verify that bad inputs fail for each of create new and update
|
||||||
@@ -391,10 +391,10 @@ class PermissionedDomains_test : public beast::unit_test::Suite
|
|||||||
env(pdomain::setTx(alice[2], credentials1, domain2), Ter(tecNO_PERMISSION));
|
env(pdomain::setTx(alice[2], credentials1, domain2), Ter(tecNO_PERMISSION));
|
||||||
|
|
||||||
// Update a UInt256(0) domain
|
// Update a UInt256(0) domain
|
||||||
env(pdomain::setTx(alice[0], credentials1, UInt256(0)), Ter(temMALFORMED));
|
env(pdomain::setTx(alice[0], credentials1, UInt256{0}), Ter(temMALFORMED));
|
||||||
|
|
||||||
// Update non-existent domain
|
// Update non-existent domain
|
||||||
env(pdomain::setTx(alice[0], credentials1, UInt256(75)), Ter(tecNO_ENTRY));
|
env(pdomain::setTx(alice[0], credentials1, UInt256{75}), Ter(tecNO_ENTRY));
|
||||||
|
|
||||||
// Wrong flag
|
// Wrong flag
|
||||||
env(pdomain::setTx(alice[0], credentials1), Txflags(tfClawTwoAssets), Ter(temINVALID_FLAG));
|
env(pdomain::setTx(alice[0], credentials1), Txflags(tfClawTwoAssets), Ter(temINVALID_FLAG));
|
||||||
@@ -452,16 +452,16 @@ class PermissionedDomains_test : public beast::unit_test::Suite
|
|||||||
env(pdomain::deleteTx(bob, domain), Ter(tecNO_PERMISSION));
|
env(pdomain::deleteTx(bob, domain), Ter(tecNO_PERMISSION));
|
||||||
|
|
||||||
// Delete a non-existent domain.
|
// Delete a non-existent domain.
|
||||||
env(pdomain::deleteTx(alice, UInt256(75)), Ter(tecNO_ENTRY));
|
env(pdomain::deleteTx(alice, UInt256{75}), Ter(tecNO_ENTRY));
|
||||||
|
|
||||||
// Test bad fee
|
// Test bad fee
|
||||||
env(pdomain::deleteTx(alice, UInt256(75)), Ter(temBAD_FEE), Fee(1, true));
|
env(pdomain::deleteTx(alice, UInt256{75}), Ter(temBAD_FEE), Fee(1, true));
|
||||||
|
|
||||||
// Wrong flag
|
// Wrong flag
|
||||||
env(pdomain::deleteTx(alice, domain), Ter(temINVALID_FLAG), Txflags(tfClawTwoAssets));
|
env(pdomain::deleteTx(alice, domain), Ter(temINVALID_FLAG), Txflags(tfClawTwoAssets));
|
||||||
|
|
||||||
// Delete a zero domain.
|
// Delete a zero domain.
|
||||||
env(pdomain::deleteTx(alice, UInt256(0)), Ter(temMALFORMED));
|
env(pdomain::deleteTx(alice, UInt256{0}), Ter(temMALFORMED));
|
||||||
|
|
||||||
// Make sure owner count reflects the existing domain.
|
// Make sure owner count reflects the existing domain.
|
||||||
BEAST_EXPECT(env.ownerCount(alice) == 1);
|
BEAST_EXPECT(env.ownerCount(alice) == 1);
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ struct PseudoTx_test : public beast::unit_test::Suite
|
|||||||
|
|
||||||
res.emplace_back(ttAMENDMENT, [&](auto& obj) {
|
res.emplace_back(ttAMENDMENT, [&](auto& obj) {
|
||||||
obj.setAccountID(sfAccount, AccountID());
|
obj.setAccountID(sfAccount, AccountID());
|
||||||
obj.setFieldH256(sfAmendment, UInt256(2));
|
obj.setFieldH256(sfAmendment, UInt256{2});
|
||||||
obj.setFieldU32(sfLedgerSequence, seq);
|
obj.setFieldU32(sfLedgerSequence, seq);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -59,11 +59,11 @@ struct PseudoTx_test : public beast::unit_test::Suite
|
|||||||
{
|
{
|
||||||
std::vector<STTx> res;
|
std::vector<STTx> res;
|
||||||
|
|
||||||
res.emplace_back(ttACCOUNT_SET, [&](auto& obj) { obj[sfAccount] = AccountID(1); });
|
res.emplace_back(ttACCOUNT_SET, [&](auto& obj) { obj[sfAccount] = AccountID{1}; });
|
||||||
|
|
||||||
res.emplace_back(ttPAYMENT, [&](auto& obj) {
|
res.emplace_back(ttPAYMENT, [&](auto& obj) {
|
||||||
obj.setAccountID(sfAccount, AccountID(2));
|
obj.setAccountID(sfAccount, AccountID{2});
|
||||||
obj.setAccountID(sfDestination, AccountID(3));
|
obj.setAccountID(sfDestination, AccountID{3});
|
||||||
});
|
});
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
@@ -82,7 +82,7 @@ struct PseudoTx_test : public beast::unit_test::Suite
|
|||||||
BEAST_EXPECT(!passesLocalChecks(stx, reason));
|
BEAST_EXPECT(!passesLocalChecks(stx, reason));
|
||||||
BEAST_EXPECT(reason == "Cannot submit pseudo transactions.");
|
BEAST_EXPECT(reason == "Cannot submit pseudo transactions.");
|
||||||
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
|
env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal j) {
|
||||||
auto const result = xrpl::apply(env.app(), view, stx, TapNone, j);
|
auto const result = xrpl::apply(env.app(), view, stx, ApplyFlags::None, j);
|
||||||
BEAST_EXPECT(!result.applied && result.ter == temINVALID);
|
BEAST_EXPECT(!result.applied && result.ter == temINVALID);
|
||||||
return result.applied;
|
return result.applied;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ class RCLValidations_test : public beast::unit_test::Suite
|
|||||||
{
|
{
|
||||||
RCLValidatedLedger const a{RCLValidatedLedger::MakeGenesis{}};
|
RCLValidatedLedger const a{RCLValidatedLedger::MakeGenesis{}};
|
||||||
BEAST_EXPECT(a.seq() == Seq{0});
|
BEAST_EXPECT(a.seq() == Seq{0});
|
||||||
BEAST_EXPECT(a[Seq{0}] == ID{0});
|
BEAST_EXPECT(a[Seq{0}] == ID{});
|
||||||
BEAST_EXPECT(a.minSeq() == Seq{0});
|
BEAST_EXPECT(a.minSeq() == Seq{0});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ class RCLValidations_test : public beast::unit_test::Suite
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
BEAST_EXPECT(a[s] == ID{0});
|
BEAST_EXPECT(a[s] == ID{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loaded 100 of 219 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user