diff --git a/cmake/scripts/codegen/templates/LedgerEntryTests.cpp.mako b/cmake/scripts/codegen/templates/LedgerEntryTests.cpp.mako index 011cf933dda..a3b9853d08c 100644 --- a/cmake/scripts/codegen/templates/LedgerEntryTests.cpp.mako +++ b/cmake/scripts/codegen/templates/LedgerEntryTests.cpp.mako @@ -31,7 +31,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(${name}Tests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; % for field in fields: auto const ${field["paramName"]}Value = ${canonical_expr(field)}; @@ -85,7 +85,7 @@ TEST(${name}Tests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(${name}Tests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; % for field in fields: auto const ${field["paramName"]}Value = ${canonical_expr(field)}; @@ -146,7 +146,7 @@ TEST(${name}Tests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(${name}Tests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -177,7 +177,7 @@ TEST(${name}Tests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(${name}Tests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type % if wrong_le_include == "Ticket": @@ -207,7 +207,7 @@ TEST(${name}Tests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(${name}Tests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; % for field in required_fields: auto const ${field["paramName"]}Value = ${canonical_expr(field)}; diff --git a/include/xrpl/basics/CountedObject.h b/include/xrpl/basics/CountedObject.h index bb7b0d88774..92c5e279bf4 100644 --- a/include/xrpl/basics/CountedObject.h +++ b/include/xrpl/basics/CountedObject.h @@ -1,29 +1,31 @@ #pragma once -#include +#include +#include + +#include #include +#include +#include +#include #include +#include #include -#include namespace xrpl { /** * Manages all counted object types. + * + * Counters register themselves on a lock-free intrusive list maintained + * by this object when constructed. Because counters are never destroyed + * or removed, the ABA problem does not apply. + * + * The registry is iterable as a forward range. */ class CountedObjects { -public: - static CountedObjects& - getInstance() noexcept; - - using Entry = std::pair; - using List = std::vector; - - [[nodiscard]] List - getCounts(int minimumThreshold) const; - public: /** * Implementation for @ref CountedObject. @@ -33,68 +35,147 @@ class CountedObjects class Counter { public: - Counter(std::string name) noexcept : name_(std::move(name)), count_(0) + Counter(std::string name) noexcept; + + // Counters are intrusive list nodes whose addresses are published + // in the registry; they must never be copied or moved. The atomic + // members already force this, but we make it explicit. + Counter(Counter const&) = delete; + Counter& + operator=(Counter const&) = delete; + Counter(Counter&&) = delete; + Counter& + operator=(Counter&&) = delete; + + std::uint32_t + increment() noexcept { - // Insert ourselves at the front of the lock-free linked list - CountedObjects& instance = CountedObjects::getInstance(); - Counter* head = nullptr; - - do - { - head = instance.head_.load(); - next_ = head; - } while (instance.head_.exchange(this) != head); + auto const newCount = count_.fetch_add(1, std::memory_order::relaxed) + 1; + XRPL_ASSERT(newCount != 0, "xrpl::CountedObjects::Counter::increment : no overflow"); - ++instance.count_; - } + auto maxCount = maxCount_.load(std::memory_order::relaxed); - ~Counter() noexcept = default; + while (newCount > maxCount && + !maxCount_.compare_exchange_weak(maxCount, newCount, std::memory_order::relaxed)) + { + } - int - increment() noexcept - { - return ++count_; + return newCount; } - int + std::uint32_t 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 - getCount() const noexcept + [[nodiscard]] std::uint32_t + count() const noexcept { - return count_.load(); + return count_.load(std::memory_order::relaxed); } - [[nodiscard]] Counter* - getNext() const noexcept + [[nodiscard]] std::uint32_t + max() const noexcept { - return next_; + return std::max( + count_.load(std::memory_order::relaxed), + maxCount_.load(std::memory_order::relaxed)); } [[nodiscard]] std::string const& - getName() const noexcept + name() const noexcept { return name_; } private: + friend class CountedObjects; + + Counter* next_ = nullptr; + std::atomic count_ = 0; + std::atomic maxCount_ = 0; std::string const name_; - std::atomic count_; - Counter* next_; }; -private: - CountedObjects() noexcept; - ~CountedObjects() noexcept = default; + class Iterator + { + public: + using value_type = Counter const; + using reference = value_type&; + using pointer = value_type*; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + explicit Iterator(Counter* c = nullptr) noexcept : current_(c) + { + } + + reference + operator*() const noexcept + { + return *current_; + } + + pointer + operator->() const noexcept + { + return current_; + } + + Iterator& + operator++() noexcept + { + current_ = current_->next_; + return *this; + } + + Iterator + operator++(int) noexcept + { + auto tmp = *this; + ++*this; + return tmp; + } + + bool + operator==(Iterator const&) const noexcept = default; + + private: + Counter* current_; + }; + + constexpr CountedObjects() noexcept = default; + + [[nodiscard]] auto + begin() const noexcept + { + return Iterator{head_.load(std::memory_order::acquire)}; + } + + [[nodiscard]] auto + end() const noexcept + { + return Iterator{}; + } private: - std::atomic count_; - std::atomic head_; + std::atomic head_ = nullptr; }; +/** 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 @@ class CountedObjects * Derived classes have their instances counted automatically. This is used * for reporting purposes. * + * The constructors are private and `Object` is befriended so that the + * CRTP parameter must be the deriving class itself: a copy-paste error + * like `class B : public CountedObject` fails to compile instead of + * silently polluting A's count. + * + * @note This class has no move operations by design: a derived class's + * move constructor falls back to the copy constructor for this + * base, so the newly created instance is counted. This keeps the + * invariant that count is the number of outstanding subobjects. + * + * @warning Counted objects constructed during dynamic initialization of + * other translation units may have their increments discarded when + * counter itself is dynamically initialized. Do not create counted + * objects before main() begins. + * * @ingroup basics */ template + requires std::is_class_v class CountedObject { -private: - static auto& - getCounter() noexcept - { - static CountedObjects::Counter kC{beast::typeName()}; - return kC; - } + static inline CountedObjects::Counter counter{boost::core::type_name()}; CountedObject() noexcept { - getCounter().increment(); + counter.increment(); } CountedObject(CountedObject const&) noexcept { - getCounter().increment(); + counter.increment(); } CountedObject& @@ -132,7 +223,7 @@ class CountedObject public: ~CountedObject() noexcept { - getCounter().decrement(); + counter.decrement(); } friend Object; diff --git a/include/xrpl/basics/SlabAllocator.h b/include/xrpl/basics/SlabAllocator.h index 7b6e88e8bc8..7008975316c 100644 --- a/include/xrpl/basics/SlabAllocator.h +++ b/include/xrpl/basics/SlabAllocator.h @@ -1,405 +1,1066 @@ -// Copyright (c) 2022, Nikolaos D. Bougalis +// Copyright (c) 2022, 2026, Nikolaos D. Bougalis #pragma once #include -#include -#include +#include #include -#include -#include +#include +#include #include +#include #include +#include #include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #if BOOST_OS_LINUX #include #endif -namespace xrpl { +namespace xrpl::slab { -template -class SlabAllocator +namespace detail { + +/** Parking allows allocations and deallocations to complete without locking. + + Each slab carries a small array of "parking" spots that are accessed + in a lock-free manner. Deallocation attempts will park an item in an + available spot using CAS; allocation attempts will unpark an item by + using an exchange. Both operations will fall back to the locked path + if parking cannot service them. + + Parked items still count as outstanding because we cannot adjust the + number of outstanding items without holding a lock. This is a detail + that does not impact users of this allocator. + + Parking avoids more than the lock: pops from the freelist must first + follow the link stored inside the item being popped, a load that can + take a cache miss while the lock is held. Parked handoffs will never + dereference the item; once parked, the memory is next touched by its + new owner. + + When disabled, allocation and deallocation requests always go through + the locked freelist path. + */ +inline constexpr bool allowParking = true; + +/** The number of items in the parking. + + This value is here for documentation purposes and should not be + adjusted; it has implications on the size and alignment of slab + objects. + */ +inline constexpr std::size_t parkingCapacity = 4; + +/** The minimum number of items per slab. + + It is hard to set a reasonable minimum value for an allocator + that is intended as somewhat-general-purpose and which allows + a wide range of sizes. + + When parking is enabled, a slab needs to have enough capacity + to allocate at least parkingCapacity + 1 items. + */ +inline constexpr std::size_t minimumSlabItems = 4 * parkingCapacity; + +/// Alignment for hugepage support. +inline constexpr std::size_t pageSize = megabytes(2); + +/** Allocate a new 2MB-aligned data buffer. + + @param size Size of the buffer to allocate in bytes. + @return Pointer to allocated memory, or nullptr on failure. + + @note On Linux, advises the kernel to back the allocation with + transparent huge pages if available. + */ +[[nodiscard, gnu::malloc]] inline std::uint8_t* +allocateBuffer(std::size_t size) noexcept { - static_assert( - sizeof(Type) >= sizeof(std::uint8_t*), - "SlabAllocator: the requested object must be larger than a pointer."); + auto ptr = static_cast(boost::alignment::aligned_alloc(pageSize, size)); + +#if BOOST_OS_LINUX + if (ptr != nullptr) [[likely]] + madvise(ptr, size, MADV_HUGEPAGE); +#endif + + return ptr; +} - static_assert(alignof(Type) == 8 || alignof(Type) == 4); +/** Deallocate a buffer previously allocated by allocateBuffer. - /** - * A block of memory that is owned by a slab allocator + @param ptr Pointer to buffer, or nullptr (no-op). + */ +inline void +deallocateBuffer(std::uint8_t* ptr) noexcept +{ + boost::alignment::aligned_free(ptr); +} + +//------------------------------------------------------------------------------ + +/** A node in the intrusive free list. + + Constructed in-place within free memory blocks. The next pointer + is const because nodes are never modified after construction; + they are simply constructed anew when reused. + */ +struct Item +{ + Item* const next; + + constexpr explicit Item(Item* n) noexcept : next(n) + { + } +}; + +// We need this to be trivially destructible so that we do not +// have to explicitly invoke the destructor when popping items +// from the free list or when deallocating the slab buffer. +static_assert(std::is_trivially_destructible_v); + +/** A slab-sized buffer, not yet owned by any slab, arranged into a freelist. + + The buffer must be private to the constructing thread, because the + freelist nodes are built with plain stores, and are only published + to other threads when the buffer is grafted onto a slab. + + */ +class BuiltBuffer +{ + std::uint8_t* base_ = nullptr; + Item* head_ = nullptr; + +public: + BuiltBuffer(std::span mem, std::size_t count) noexcept : base_(mem.data()) + { + while (mem.size() >= count) + { + head_ = std::construct_at(reinterpret_cast(mem.data()), head_); + mem = mem.subspan(count); + } + } + + /** Transfer the freelist out; the buffer is then consumed. */ + [[nodiscard]] Item* + take() noexcept + { + assert(head_ != nullptr); + return std::exchange(head_, nullptr); + } + + /** True once the freelist has been taken. */ + [[nodiscard]] bool + consumed() const noexcept + { + return head_ == nullptr; + } + + /** The base address associated with this buffer. */ + [[nodiscard]] std::uint8_t* + base() const noexcept + { + return base_; + } +}; + +/** A block of memory metadata for slab allocators. + + Each block manages a single slab buffer along with an associated free + list. Blocks are then chained together to form a linked list of slabs + per allocator. + + Blocks are sized and aligned to exactly one cache line: they contain + heavily contended atomics and are handed out from a dense array (see + SlabPool), so two blocks sharing a line would false-share, taxing + every operation on one slab with coherence traffic caused by threads + operating on its neighbor. + + @note SlabBlock instances are never destroyed or reclaimed once they + are allocated to support lock-free iteration of lists of slabs + without introducing ABA hazards. The data buffer which is held + by an instance can be released independently. + */ +struct alignas(64) SlabBlock +{ + /** Head of the intrusive freelist of available items. + + This is only ever mutated while holding the lock; but what the + atomic gets us is the ability to check whether the slab has an + empty freelist without taking a lock. Stale reads are benign. + */ + std::atomic head = nullptr; + + /** The next slab in this allocator's list, or nullptr at the tail. */ + SlabBlock* const next = nullptr; + + /** The slab's data buffer, or nullptr if the slab has no buffer. */ + std::atomic data = nullptr; + + /** Spinlock guarding head, outstanding and cycles. */ + std::atomic lock = 0; + + /** Number of buffer release cycles; retained for debugging. */ + std::uint16_t cycles = 0; + + /** Number of items from this slab's buffer not on the freelist. */ + std::uint32_t outstanding = 0; + + /** Recently freed items available for immediate, lock-free reuse. */ + std::array, parkingCapacity> parking{}; + + /** Construct a block that is unlinked. */ + constexpr SlabBlock() noexcept = default; + + /** Construct a block with a link to the next block in the chain. + + @param n Pointer to the next block, or nullptr for the tail. */ - struct SlabBlock + constexpr explicit SlabBlock(SlabBlock* n) noexcept : next(n) { - // A mutex to protect the freelist for this block: - std::mutex m; + } - // A linked list of appropriately sized free buffers: - std::uint8_t* l = nullptr; + ~SlabBlock() = default; - // The next memory block - SlabBlock* next; + SlabBlock(SlabBlock const&) = delete; + SlabBlock& + operator=(SlabBlock const&) = delete; + SlabBlock(SlabBlock&&) = delete; + SlabBlock& + operator=(SlabBlock&&) = delete; + + /** Determines whether this block owns the provided pointer. */ + [[nodiscard]] bool + owns(std::uint8_t const* ptr, std::size_t size) const noexcept + { + assert(ptr != nullptr); - // The underlying memory block: - std::uint8_t const* const p = nullptr; + auto const d = data.load(std::memory_order::acquire); - // The extent of the underlying memory block: - std::size_t const size; + if (d == nullptr) + return false; - SlabBlock(SlabBlock* next, std::uint8_t* data, std::size_t size, std::size_t item) - : next(next), p(data), size(size) + auto const b = reinterpret_cast(d); + auto const p = reinterpret_cast(ptr); + + return (p >= b) && (p < b + size); + } + + /** Attempt to allocate from this block. + + @return Pointer to allocated memory, or nullptr if block is empty. + */ + [[nodiscard]] std::uint8_t* + tryAllocate() noexcept + { + // Quickly reject slabs that don't have a buffer without taking a + // lock. The relaxed read may be stale, this is fine: + // + // - A stale null will only happen when we are racing a concurrent + // buffer assignment to this slab; the caller continues scanning + // other slabs and, if none can satisfy the request, the scan is + // re-run under the block lock, which closes the race. + // - A stale non-null will fall through, executing the code which + // would have run if the check was not here in the first place. + if (data.load(std::memory_order::relaxed) == nullptr) + return nullptr; + + if constexpr (allowParking) { - // We don't need to grab the mutex here, since we're the only - // ones with access at this moment. + // Lookaside first: peek (read-only, so the line stays shared) at + // the parked slots; only attempt an exchange, if we have a hit. + for (auto& slot : parking) + { + if (slot.load(std::memory_order::relaxed) != nullptr) + { + // We can race against, and lose to someone else; that is + // fine. + if (auto* p = slot.exchange(nullptr, std::memory_order::acquire)) + return p; + } + } + } + + Item* ret = nullptr; + + if (head.load(std::memory_order::relaxed) != nullptr) + { + spinLock(lock); - while (data + item <= p + size) + ret = head.load(std::memory_order::relaxed); + + if (ret != nullptr) { - // Use memcpy to avoid unaligned UB - // (will optimize to equivalent code) - std::memcpy(data, static_cast(&l), sizeof(std::uint8_t*)); - l = data; - data += item; + head.store(ret->next, std::memory_order::relaxed); + ++outstanding; } + + spinUnlock(lock); } - // Calling this destructor will release the allocated memory but - // will not properly destroy any objects that are constructed in - // the block itself. - ~SlabBlock() = default; + return reinterpret_cast(ret); + } + + /** Attempt to allocate from this block, while also offering it a buffer. + + Prefers existing capacity. If this slab has no buffer, it takes + ownership of the buffer we provided and satisfies the request. + + @param bb A built buffer. + + @return Pointer to allocated memory, or nullptr. + */ + [[nodiscard]] std::uint8_t* + tryAllocate(BuiltBuffer& bb) noexcept + { + if (auto ret = tryAllocate()) + return ret; + + if (std::uint8_t* expected = nullptr; !data.compare_exchange_strong( + expected, bb.base(), std::memory_order::release, std::memory_order::relaxed)) + return nullptr; + + // Pops an item from the freelist and accounts for the item. + auto const pop = [this]() noexcept { + auto* item = head.load(std::memory_order::relaxed); + assert(item != nullptr); + + head.store(item->next, std::memory_order::relaxed); + ++outstanding; + + return reinterpret_cast(item); + }; - SlabBlock(SlabBlock const& other) = delete; - SlabBlock& - operator=(SlabBlock const& other) = delete; + spinLock(lock); - SlabBlock(SlabBlock&& other) = delete; - SlabBlock& - operator=(SlabBlock&& other) = delete; + assert(head.load(std::memory_order::relaxed) == nullptr && outstanding == 0); - /** - * Determines whether the given pointer belongs to this allocator - */ - bool - own(std::uint8_t const* pIn) const noexcept + // Take ownership of this buffer + head.store(bb.take(), std::memory_order::relaxed); + + if constexpr (allowParking) { - return (pIn >= p) && (pIn < p + size); + // Pre-populate the parking: with "parking full" as the slab's + // resting state, the final free finds no empty slot and takes + // the lock, which will also release the parked slots. + for (auto& slot : parking) + slot.store(pop(), std::memory_order::release); } - std::uint8_t* - allocate() noexcept - { - std::uint8_t* ret = nullptr; // NOLINT(misc-const-correctness) + auto ret = pop(); - { - std::scoped_lock const lock(m); - ret = l; + spinUnlock(lock); + + return ret; + } + + /** Attempt to return a pointer to this block. - if (ret != nullptr) + @param ptr Pointer to memory block. + @param slabSize Size of the slab's data buffer. + @param releaseBuffer Callback invoked with a pointer to the block's + buffer so that it can be released. + @return true if ptr belonged to this block and was freed, + false otherwise. + */ + template + requires std::is_nothrow_invocable_v + [[nodiscard]] bool + tryDeallocate(std::uint8_t* ptr, std::size_t slabSize, ReleaseFunc&& releaseBuffer) noexcept + { + if (!owns(ptr, slabSize)) + return false; + + if constexpr (allowParking) + { + // Try to see if we can park this item without grabbing the + // lock. If we succeed, a future allocation will be able to + // satisfy an allocation request without grabbing a lock. + for (auto& slot : parking) + { + if (auto x = slot.load(std::memory_order::relaxed); x == nullptr) { - // Use memcpy to avoid unaligned UB - // (will optimize to equivalent code) - std::memcpy(static_cast(&l), ret, sizeof(std::uint8_t*)); + if (slot.compare_exchange_strong( + x, ptr, std::memory_order::release, std::memory_order::relaxed)) + return true; } } + } - return ret; + std::uint8_t* buf = nullptr; + + spinLock(lock); + + assert(ptr != nullptr); + assert(outstanding > 0); + + // Pushes an item onto the freelist. Must hold lock to call. + auto const push = [this](std::uint8_t* p) noexcept { + head.store( + std::construct_at( + reinterpret_cast(p), head.load(std::memory_order::relaxed)), + std::memory_order::relaxed); + }; + + if constexpr (allowParking) + { + // Sweep the parked slots only when this could result in the slab + // having no outstanding allocations: that is, when the number of + // outstanding items is equal to the number of parking spots plus + // one (for the item we are freeing) and the slab isn't the first + // block. + if (outstanding == parking.size() + 1 && next != nullptr) + { + // Because the parked slots are handled lock-free, we must be + // extra vigilant, since someone could have allocated an item + // from there. + for (auto& slot : parking) + { + if (auto park = slot.exchange(nullptr, std::memory_order::acquire)) + { + push(park); + --outstanding; + } + } + } } - /** - * Return an item to this allocator's freelist. - * - * @param ptr The pointer to the chunk of memory being deallocated. - * - * @note This is a dangerous, private interface; the item being - * returned should belong to this allocator. Debug builds - * will check and assert if this is not the case. Release - * builds will not. - */ - void - deallocate(std::uint8_t* ptr) noexcept + push(ptr); + + // If this block became empty, and it is not the first block, we + // can release its buffer. The first block (which is at the tail + // of the list with next == nullptr) is exempt from this policy + // so that we can always have one block ready. + if (--outstanding == 0 && next != nullptr) { - XRPL_ASSERT(own(ptr), "xrpl::SlabAllocator::SlabBlock::deallocate : own input"); + head.store(nullptr, std::memory_order::relaxed); + buf = data.exchange(buf, std::memory_order::relaxed); + cycles += (cycles < std::numeric_limits::max()); + } - std::scoped_lock const lock(m); + spinUnlock(lock); - // Use memcpy to avoid unaligned UB - // (will optimize to equivalent code) - std::memcpy(ptr, static_cast(&l), sizeof(std::uint8_t*)); - l = ptr; + if (buf != nullptr) + releaseBuffer(buf); + + return true; + } +}; + +static_assert(sizeof(SlabBlock) == 64 && alignof(SlabBlock) == 64); + +//------------------------------------------------------------------------------ + +/** Global pool of SlabBlock objects shared by all slab allocators. + + Provides a fixed-size array of pre-allocated slabs with fallback + to heap allocation if exhausted. + */ +class SlabPool +{ + std::array blocks_{}; + std::atomic count_{0}; + +public: + constexpr SlabPool() = default; + + SlabPool(SlabPool const&) = delete; + SlabPool& + operator=(SlabPool const&) = delete; + SlabPool(SlabPool&&) = delete; + SlabPool& + operator=(SlabPool&&) = delete; + + /** Acquire a slab from the pool. + + @param next Pointer to link as the slab's next pointer. + @return Pointer to acquired block, or nullptr if allocation failed. + */ + [[nodiscard]] SlabBlock* + acquire(SlabBlock* next) noexcept + { + auto idx = count_.load(std::memory_order::relaxed); + + while (idx < blocks_.size()) + { + if (count_.compare_exchange_weak(idx, idx + 1, std::memory_order::relaxed)) + { + return std::construct_at(&blocks_[idx], next); + } } - }; -private: - // A linked list of slabs - std::atomic slabs_ = nullptr; + // Pool exhausted (unlikely!) so fall back to heap + return new (std::nothrow) SlabBlock(next); + } +}; + +/// Single global block pool for all slab allocators. +inline constinit SlabPool gSlabPool; - // The alignment requirements of the item we're allocating: - std::size_t const itemAlignment_; +} // namespace detail +//------------------------------------------------------------------------------ + +/** A slab allocator for fixed-size memory blocks. + + Allocates memory in large slabs (multiples of 2MB) and sub-allocates + fixed-size blocks from them. Provides fast allocation with minimal + fragmentation for objects of uniform size. + + @tparam Type The type used to determine minimum block size and alignment. + @tparam Align Alignment for allocated blocks (must be >= alignof(Type)). + */ +template + requires( + std::has_single_bit(Align) && sizeof(Type) >= sizeof(detail::Item) && + Align >= std::max(alignof(detail::Item), alignof(Type))) +class alignas(64) SizedAllocator +{ + /// Linked list of slabs for this allocator. + std::atomic slabs_{nullptr}; - // The size of an item, including the extra bytes requested and - // any padding needed for alignment purposes: + /** A spare slab-sized buffer retained for reuse, or nullptr. + + When a slab drains completely, its buffer is released; when demand + returns, a slab needs a buffer again. If we are operating near the + boundary of a slab, we may end having to release and reacquire the + buffer repeatedly. By caching the released buffer, we can reuse it + directly for the expansion, avoiding that overhead. + */ + std::atomic cachedBuffer_{nullptr}; + + /** The slab that we used most recently allocated memory from. + + This slab exists in the list of slabs_, but may be deep enough + that it requires a lot of work to get to. By caching a pointer + to it, we can check it preferentially, which helps keep memory + hot. + */ + std::atomic cachedSlab_{nullptr}; + + /** Item size (sizeof(Type) + extra, rounded up for alignment). */ std::size_t const itemSize_; - // The size of each individual slab: + /** Size of each slab's data buffer (multiple of 2 MB). */ std::size_t const slabSize_; -public: - /** - * Constructs a slab allocator able to allocate objects of a fixed size - * - * @param count the number of items the slab allocator can allocate; note - * that a count of 0 is valid and means that the allocator - * is, effectively, disabled. This can be very useful in some - * contexts (e.g. when minimal memory usage is needed) and - * allows for graceful failure. + /** Spinlock to serialize the slow allocation path. */ + std::atomic blockLock_{0}; + + /** Retain a slab-sized buffer for later reuse, or release it. + + The allocator keeps at most one spare buffer, so that any + workloads oscillating around a slab boundary, do not have + to round-trip through the operating system. + + @param buf A buffer of exactly slabSize_ bytes that was + obtained from allocateBuffer, or nullptr (no-op). */ - constexpr explicit SlabAllocator( - std::size_t extra, - std::size_t alloc = 0, - std::size_t align = 0) - : itemAlignment_((align != 0u) ? align : alignof(Type)) - , itemSize_(boost::alignment::align_up(sizeof(Type) + extra, itemAlignment_)) - , slabSize_(alloc) + void + discard(std::uint8_t* buf) noexcept { - XRPL_ASSERT( - (itemAlignment_ & (itemAlignment_ - 1)) == 0, - "xrpl::SlabAllocator::SlabAllocator : valid alignment"); + if (buf == nullptr) + return; + + if (std::uint8_t* expected = nullptr; + !cachedBuffer_.compare_exchange_strong(expected, buf, std::memory_order::release)) + detail::deallocateBuffer(buf); } - SlabAllocator(SlabAllocator const& other) = delete; - SlabAllocator& - operator=(SlabAllocator const& other) = delete; + /** Allocate a memory block from the existing slabs. + + Probes each slab in the list, except the one passed in, which the + caller already tried. If a slab successfully services the request, + we store it into cachedSlab_, to preferentially use it. - SlabAllocator(SlabAllocator&& other) = delete; - SlabAllocator& - operator=(SlabAllocator&& other) = delete; + This does not acquire the blockLock_, but can take the individual + slab locks during traversal. - // FIXME: We can't destroy the memory blocks we've allocated, because - // we can't be sure that they are not being used. Cleaning the - // shutdown process up could make this possible. - ~SlabAllocator() = default; + @param hint The slab the caller already tried to allocate memory + from, if any. We skip it if we come across it. - /** - * Returns the size of the memory block this allocator returns. + @return Pointer to allocated memory, or nullptr if no existing + slab can satisfy the request. */ - [[nodiscard]] constexpr std::size_t - size() const noexcept + [[nodiscard]] std::uint8_t* + fastAllocate(detail::SlabBlock const* hint) noexcept { - return itemSize_; + auto slab = slabs_.load(std::memory_order::acquire); + + while (slab != nullptr) + { + if (slab != hint) + { + if (auto ret = slab->tryAllocate()) + { + cachedSlab_.store(slab, std::memory_order::release); + return ret; + } + } + + slab = slab->next; + } + + return nullptr; } - /** - * Returns a suitably aligned pointer, if one is available. - * - * @return a pointer to a block of memory from the allocator, or - * nullptr if the allocator can't satisfy this request. + /** Allocate a memory block, potentially adding new capacity. + + Must be called with blockLock_ held. + + @param bb A built buffer, private to the calling thread. The + buffer may or may not be consumed. + + @return Pointer to allocated memory, or nullptr on failure. */ - std::uint8_t* - allocate() noexcept + [[nodiscard]] std::uint8_t* + slowAllocate(detail::BuiltBuffer& bb) noexcept { - auto slab = slabs_.load(); + // Try to allocate again; if a slab is empty, offer it the newly + // allocated memory. + auto slab = slabs_.load(std::memory_order::acquire); while (slab != nullptr) { - if (auto ret = slab->allocate()) + if (auto ret = slab->tryAllocate(bb)) + { + cachedSlab_.store(slab, std::memory_order::release); return ret; + } slab = slab->next; } - // No slab can satisfy our request, so we attempt to allocate a new - // one here: - std::size_t const size = slabSize_; + // No existing slab could take the buffer; get a new slab from + // the global pool. + slab = detail::gSlabPool.acquire(slabs_.load(std::memory_order::relaxed)); - // We want to allocate the memory at a 2 MiB boundary, to make it - // possible to use hugepage mappings on Linux: - auto buf = boost::alignment::aligned_alloc(megabytes(std::size_t(2)), size); - if (buf == nullptr) [[unlikely]] + if (slab == nullptr) [[unlikely]] return nullptr; -#if BOOST_OS_LINUX - // When allocating large blocks, attempt to leverage Linux's - // transparent hugepage support. It is unclear and difficult - // to accurately determine if doing this impacts performance - // enough to justify using platform-specific tricks. - if (size >= megabytes(std::size_t(4))) - madvise(buf, size, MADV_HUGEPAGE); -#endif + // This is a fresh slab, so it must accept the buffer we provided + // it with and return a valid pointer. + auto ret = slab->tryAllocate(bb); + assert(ret != nullptr && slab->owns(ret, slabSize_)); + + cachedSlab_.store(slab, std::memory_order::release); + slabs_.store(slab, std::memory_order::release); + + return ret; + } - // We need to carve out a bit of memory for the slab header - // and then align the rest appropriately: - auto slabData = - reinterpret_cast(reinterpret_cast(buf) + sizeof(SlabBlock)); - auto slabSize = size - sizeof(SlabBlock); +public: + /** Construct a slab allocator. + + @param extra Extra bytes per item beyond sizeof(Type). + @param minItems Minimum number of items per slab (rounded up to 2MB). + */ + constexpr explicit SizedAllocator(std::size_t extra, std::size_t minItems) noexcept + : itemSize_(boost::alignment::align_up(sizeof(Type) + extra, Align)) + , slabSize_( + boost::alignment::align_up( + itemSize_ * std::max(minItems, detail::minimumSlabItems), + detail::pageSize)) + { + } - // This operation is essentially guaranteed not to fail but - // let's be careful anyways. - if (boost::alignment::align(itemAlignment_, itemSize_, slabData, slabSize) == nullptr) + ~SizedAllocator() noexcept + { + // We can free the cached buffer, if one is available. As for the data + // buffers, they are intentionally not released: C++ destruction order + // does not allow us to assume that all items that were allocated from + // this allocator have been freed by the time this destructor runs. + detail::deallocateBuffer(cachedBuffer_.exchange(nullptr)); + } + + SizedAllocator(SizedAllocator const&) = delete; + SizedAllocator& + operator=(SizedAllocator const&) = delete; + SizedAllocator(SizedAllocator&&) = delete; + SizedAllocator& + operator=(SizedAllocator&&) = delete; + + /** Returns the size of memory blocks returned by this allocator. + + @return Size of each allocated block in bytes. + */ + [[nodiscard]] constexpr std::size_t + size() const noexcept + { + return itemSize_; + } + + /** Allocate a memory block. + + @return Pointer to allocated memory, or nullptr on failure. + + @note The gnu::malloc attribute is an optimization hint that can + be leveraged by GCC and Clang. + */ + [[nodiscard, gnu::malloc]] void* + allocate() noexcept + { + // Check if the cached slab has memory available: + auto const hint = cachedSlab_.load(std::memory_order::acquire); + + if (hint != nullptr) { - boost::alignment::aligned_free(buf); - return nullptr; + if (auto ret = hint->tryAllocate()) + return ret; } - slab = new (buf) SlabBlock( - slabs_.load(), reinterpret_cast(slabData), slabSize, itemSize_); + // Fast path: try existing slabs + if (auto ret = fastAllocate(hint)) + return ret; - // Link the new slab - while (!slabs_.compare_exchange_weak( - slab->next, slab, std::memory_order_release, std::memory_order_relaxed)) + // Prepare capacity before taking the lock: obtain and build a + // buffer with no locks held. + auto buf = cachedBuffer_.exchange(nullptr, std::memory_order::acquire); + + if (buf == nullptr) { - ; // Nothing to do + buf = detail::allocateBuffer(slabSize_); + + if (buf == nullptr) [[unlikely]] + return nullptr; } - return slab->allocate(); + detail::BuiltBuffer bb(std::span{buf, slabSize_}, itemSize_); + + spinLock(blockLock_); + auto ret = slowAllocate(bb); + spinUnlock(blockLock_); + + // It is possible that the slowAllocate used existing memory, which + // became available after the fastAllocate above. If so, the buffer + // we allocated can be discarded. + if (!bb.consumed()) + discard(bb.base()); + + return ret; } - /** - * Returns the memory block to the allocator. - * - * @param ptr A pointer to a memory block. - * @param size If non-zero, a hint as to the size of the block. - * @return true if this memory block belonged to the allocator and has - * been released; false otherwise. + /** Return a memory block to the allocator. + + @param ptr Pointer to memory block. + @return true if the block belonged to this allocator and was freed. */ - bool - deallocate(std::uint8_t* ptr) noexcept + [[nodiscard]] bool + deallocate(void* ptr) noexcept { - XRPL_ASSERT( - ptr, - "xrpl::SlabAllocator::SlabAllocator::deallocate : non-null " - "input"); + assert(ptr != nullptr); + + auto p = static_cast(ptr); + + auto const release = [this](std::uint8_t* buf) noexcept { discard(buf); }; + + auto const hint = cachedSlab_.load(std::memory_order::acquire); - for (auto slab = slabs_.load(); slab != nullptr; slab = slab->next) + if (hint != nullptr) { - if (slab->own(ptr)) - { - slab->deallocate(ptr); + if (hint->tryDeallocate(p, slabSize_, release)) return true; - } + } + + auto slab = slabs_.load(std::memory_order::acquire); + + while (slab != nullptr) + { + if (slab != hint && slab->tryDeallocate(p, slabSize_, release)) + return true; + + slab = slab->next; } return false; } + + [[nodiscard]] bool + deallocate(void const* ptr) noexcept + { + return deallocate(const_cast(ptr)); + } }; -/** - * A collection of slab allocators of various sizes for a given type. +//------------------------------------------------------------------------------ +/** Requirements for a fallback policy used by AlignedAllocator. + + A fallback handles requests the slab bins cannot satisfy. Policies + must be constructible in a constant expression (the allocator is + typically a constinit global) and all operations must be noexcept. + */ +template +concept FallbackAllocator = std::is_nothrow_default_constructible_v && + requires(T f, void* p, std::size_t align, std::size_t size) { + { f.allocate(align, size) } noexcept -> std::same_as; + { f.deallocate(p) } noexcept -> std::same_as; + }; + +/** Fallback policy that routes to the aligned heap. + + Requests the slab bins cannot satisfy are served by the general + purpose allocator; deallocation is total. */ -template -class SlabAllocatorSet +struct HeapFallback { -private: - // The list of allocators that belong to this set - boost::container::static_vector, 64> allocators_{}; + /** Allocate size bytes aligned to align. - std::size_t maxSize_ = 0; + @return Pointer to allocated memory, or nullptr on failure. + */ + [[nodiscard, gnu::malloc]] void* + allocate(std::size_t align, std::size_t size) noexcept + { + assert(size != 0); + assert(std::has_single_bit(align)); -public: - class SlabConfig + return boost::alignment::aligned_alloc(align, size); + } + + /** Release memory previously obtained from allocate. + + @return true, unconditionally: this policy handles every pointer + given to it. + */ + bool + deallocate(void* ptr) noexcept { - friend class SlabAllocatorSet; - - private: - std::size_t extra_; - std::size_t alloc_; - std::size_t align_; - - public: - constexpr SlabConfig( - std::size_t extra, - std::size_t alloc = 0, - std::size_t align = alignof(Type)) - : extra_(extra), alloc_(alloc), align_(align) - { - } - }; + boost::alignment::aligned_free(ptr); + return true; + } +}; + +/** Fallback policy that refuses requests. - constexpr SlabAllocatorSet(std::vector cfg) + Allocation beyond the slab bins fails with nullptr; deallocation of + memory the bins do not own is reported back to the caller, which is + responsible for releasing it through whatever mechanism produced it. + */ +struct NoFallback +{ + [[nodiscard]] void* + allocate(std::size_t, std::size_t) noexcept { - // Ensure that the specified allocators are sorted from smallest to - // largest by size: - std::sort(std::begin(cfg), std::end(cfg), [](SlabConfig const& a, SlabConfig const& b) { - return a.extra_ < b.extra_; - }); - - // We should never have two slabs of the same size - if (std::adjacent_find( - std::begin(cfg), std::end(cfg), [](SlabConfig const& a, SlabConfig const& b) { - return a.extra_ == b.extra_; - }) != cfg.end()) - { - throw std::runtime_error( - "SlabAllocatorSet<" + beast::typeName() + ">: duplicate slab size"); - } + return nullptr; + } - for (auto const& c : cfg) - { - auto& a = allocators_.emplace_back(c.extra_, c.alloc_, c.align_); + [[nodiscard]] bool + deallocate(void*) noexcept + { + return false; + } +}; - if (a.size() > maxSize_) - maxSize_ = a.size(); - } +static_assert(FallbackAllocator); +static_assert(FallbackAllocator); + +//------------------------------------------------------------------------------ + +/** Configuration for a single slab allocator. + + @tparam MinItems Minimum number of items per slab (must be > 0). + @tparam Extra Extra bytes per item beyond sizeof(Type). + */ +template + requires(MinItems > 0) +struct Config +{ + static constexpr std::size_t minItems = MinItems; + static constexpr std::size_t extra = Extra; +}; + +/** Concept for valid slab configuration types. */ +template +concept SlabConfig = requires { + { T::minItems } -> std::convertible_to; + { T::extra } -> std::convertible_to; + requires T::minItems > 0; +}; + +/** Validate slab configurations at compile time. + + Checks that configurations produce strictly increasing sizes after + alignment. This catches both unsorted configs and configs that + collapse to the same size after alignment. + + @tparam Type The type used to determine base size. + @tparam Align Alignment for allocated blocks. + @tparam Configs Configuration types to validate. + @return true if configurations are valid, false otherwise. + */ +template +consteval bool +validateSlabConfig() +{ + constexpr auto alignUp = [](std::size_t n, std::size_t a) { return (n + a - 1) & ~(a - 1); }; + + constexpr std::array sizes{ + alignUp(sizeof(Type) + Configs::extra, Align)...}; + + for (std::size_t i = 1; i < sizes.size(); ++i) + { + if (sizes[i - 1] >= sizes[i]) + return false; + } + + return true; +} + +//------------------------------------------------------------------------------ + +/** A collection of slab allocators for different sizes. + + Manages multiple SizedAllocator instances, each configured for a + different block size. Allocations are routed to the smallest allocator + that can satisfy the request. + + @tparam Type The type used to determine minimum block size and alignment. + @tparam Align Alignment for allocated blocks (must be >= alignof(Type)). + @tparam Fallback Type satisfying FallbackAllocator to handle allocations + that cannot be satisfied by any of the size classes. + @tparam Configs Configuration types specifying each size class. + */ +template < + typename Type, + std::size_t Align, + FallbackAllocator Fallback = HeapFallback, + SlabConfig... Configs> + requires( + sizeof(Type) >= sizeof(void*) && Align >= alignof(Type) && std::has_single_bit(Align) && + sizeof...(Configs) > 0 && validateSlabConfig()) +class AlignedAllocator +{ + std::array, sizeof...(Configs)> allocators_; + std::size_t const exmax_; + Fallback fallback_; + +public: + /** Construct an allocator set. + + @param fallback Policy handling requests the bins cannot satisfy. + */ + constexpr AlignedAllocator(Fallback fallback = {}) noexcept + : allocators_{SizedAllocator(Configs::extra, Configs::minItems)...} + , exmax_(allocators_.back().size() - sizeof(Type)) + , fallback_(std::move(fallback)) + { } - SlabAllocatorSet(SlabAllocatorSet const& other) = delete; - SlabAllocatorSet& - operator=(SlabAllocatorSet const& other) = delete; + ~AlignedAllocator() = default; + + AlignedAllocator(AlignedAllocator const&) = delete; + AlignedAllocator& + operator=(AlignedAllocator const&) = delete; + AlignedAllocator(AlignedAllocator&&) = delete; + AlignedAllocator& + operator=(AlignedAllocator&&) = delete; - SlabAllocatorSet(SlabAllocatorSet&& other) = delete; - SlabAllocatorSet& - operator=(SlabAllocatorSet&& other) = delete; + /** Allocate memory for an object with extra bytes. - ~SlabAllocatorSet() = default; + @param extra Extra bytes needed beyond sizeof(Type). + @return Pointer to memory, or nullptr if no suitable allocator + or allocation failed. - /** - * Returns a suitably aligned pointer, if one is available. - * - * @param extra The number of extra bytes, above and beyond the size of - * the object, that should be returned by the allocator. - * - * @return a pointer to a block of memory, or nullptr if the allocator - * can't satisfy this request. + @note The gnu::malloc attribute is an optimization hint that can + be leveraged by GCC and Clang. */ - std::uint8_t* - allocate(std::size_t extra) noexcept + [[nodiscard, gnu::malloc]] void* + allocate(std::size_t extra = 0) noexcept { - if (auto const size = sizeof(Type) + extra; size <= maxSize_) + if (extra <= exmax_) [[likely]] { + auto const size = sizeof(Type) + extra; + for (auto& a : allocators_) { if (a.size() >= size) - return a.allocate(); + { + if (auto p = a.allocate()) + return p; + + break; + } } } - return nullptr; + return fallback_.allocate(Align, sizeof(Type) + extra); } - /** - * Returns the memory block to the allocator. - * - * @param ptr A pointer to a memory block. - * - * @return true if this memory block belonged to one of the allocators - * in this set and has been released; false otherwise. + /** Return memory to the allocator set. + + @param ptr Pointer to memory block. + @param extra The number of extra bytes requested via allocate. + @return true if the memory has been freed. + + @note When the sized overloads are used, extra must be precisely + equal to the value passed to allocate; supplying the wrong + size can cause the function to incorrectly decide that the + memory did not belong to this set, which, depending on the + fallback configuration and/or the caller's behavior, might + result in memory corruption or worse. */ - bool - deallocate(std::uint8_t* ptr) noexcept + /** @{ */ + [[nodiscard]] bool + deallocate(void* ptr) noexcept { + assert(ptr != nullptr); + for (auto& a : allocators_) { if (a.deallocate(ptr)) return true; } - return false; + return fallback_.deallocate(ptr); } + + [[nodiscard]] bool + deallocate(void const* ptr) noexcept + { + return deallocate(const_cast(ptr)); + } + + [[nodiscard]] bool + deallocate(void* ptr, std::size_t extra) noexcept + { + if (extra <= exmax_) [[likely]] + { + auto const size = sizeof(Type) + extra; + + for (auto& a : allocators_) + { + // The first bucket large enough to have allocated this + // must be the owner. If it is not, we did not allocate + // this pointer. + if (a.size() >= size) + { + if (a.deallocate(ptr)) + return true; + + break; + } + } + } + + return fallback_.deallocate(ptr); + } + + [[nodiscard]] bool + deallocate(void const* ptr, std::size_t extra) noexcept + { + return deallocate(const_cast(ptr), extra); + } + /** @} */ }; -} // namespace xrpl +/** Alias for AlignedAllocator with default alignment. */ +template +using Allocator = AlignedAllocator; + +} // namespace xrpl::slab diff --git a/include/xrpl/basics/base_uint.h b/include/xrpl/basics/base_uint.h index 3786f28c6a4..819b347d80a 100644 --- a/include/xrpl/basics/base_uint.h +++ b/include/xrpl/basics/base_uint.h @@ -5,66 +5,249 @@ #pragma once -#include -#include #include -#include +#include // IWUY: needed for extract<> #include #include #include -#include -#include #include +#include #include #include +#include +#include #include +#include #include #include #include -#include -#include +#include #include #include -#include +#include +#include #include #include +#include #include +#include + +#if BOOST_COMP_MSVC +#include +#endif + +static_assert( + std::endian::native == std::endian::little || std::endian::native == std::endian::big, + "mixed-endian targets are not supported"); namespace xrpl { +/** Required forward reference so that we define template-based concepts. */ +template + requires(Bits % 32 == 0) && (Bits >= 64) +class BaseUInt; + namespace detail { -template > -struct IsContiguousContainer : std::false_type +template +inline constexpr bool isBaseUInt = false; + +template +inline constexpr bool isBaseUInt> = true; + +/** + * A contiguous, sized range of one-byte elements that can be copied into a + * BaseUInt (e.g. `std::string`, `Blob`, `Slice`, `std::span` of bytes, + * `std::array` or a C array of bytes). + * + * Wider element types are deliberately excluded: copying their object + * representation would make the resulting value depend on the host's byte + * order, whereas a byte sequence has a single, unambiguous interpretation + * as the big-endian representation a BaseUInt stores. + * + * BaseUInt itself is excluded: the only conversions between BaseUInt + * specialisations are the explicit tag-attaching and tag-erasing + * constructors. + */ +template +concept ByteCopySource = + std::ranges::contiguous_range && std::ranges::sized_range && + (sizeof(std::ranges::range_value_t) == 1) && !isBaseUInt>; + +/** + * A ByteCopySource whose length is a compile-time constant equal to N, + * with an element type that is unambiguously a byte. + */ +template +concept FixedByteRange = + ByteCopySource && (decltype(std::span(std::declval()))::extent == N) && + (std::same_as, unsigned char> || + std::same_as, std::byte>); + +template +concept Limb = std::same_as || std::same_as; + +/** + * Convert a value between native and big-endian representations. + * + * Byte reversal is an involution, so this function performs both the + * native-to-big and big-to-native conversions. + */ +template +[[nodiscard]] constexpr T +toBigEndian(T v) noexcept { -}; + if constexpr (std::endian::native == std::endian::little) + v = std::byteswap(v); -template -struct IsContiguousContainer< - Container, - std::void_t< - decltype(std::declval().size()), - decltype(std::declval().data()), - typename Container::value_type>> : std::true_type + return v; +} + +/** + * Parse a hexadecimal string into a big-endian limb array. + * + * The input is consumed left-to-right, most significant digit first. Each + * group of `2 * sizeof(T)` characters forms one limb; limbs are stored in + * big-endian byte order with limb 0 being the most significant. + * + * The exact-length requirement has a single exception: the one-character + * string "0" yields a zero value. No other short form is accepted. + * + * Both upper- and lower-case hexadecimal digits are accepted. No prefix + * ("0x"), sign, or whitespace is permitted. + * + * @tparam T The limb type; must be std::uint32_t or std::uint64_t. + * @tparam N The number of limbs; must be greater than zero. + * + * @param sv The string to parse. Must be exactly `N * 2 * sizeof(T)` + * characters long, or the string "0". + * + * @return The parsed limb array on success. On failure, an unseated + * optional. + */ +template + requires(N > 0) +[[nodiscard]] constexpr std::optional> +parseHex(std::string_view sv) noexcept { -}; + constexpr std::size_t kHexPerLimb = sizeof(T) * 2; -template <> -struct IsContiguousContainer : std::true_type + std::array out{}; + + if (sv == "0") + return out; + + if (sv.size() != N * kHexPerLimb) + return std::nullopt; + + for (std::size_t i = 0; i != N; ++i) + { + auto const first = sv.data() + i * kHexPerLimb; + auto const last = first + kHexPerLimb; + + T value{}; + + if (auto const [ptr, ec] = std::from_chars(first, last, value, 16); + ec != std::errc{} || ptr != last) + return std::nullopt; + + out[i] = toBigEndian(value); + } + + return out; +} + +// Add with carry. The return value is the carry-out, when doing chain +// addition. +template +[[nodiscard]] constexpr bool +addCarry(T a, T b, bool carry, T& sum) noexcept { -}; +#if BOOST_COMP_GNUC || BOOST_COMP_CLANG + // In GCC and Clang, __builtin_add_overflow can be used at compile + // time, so we don't need a fallback path. + bool const c1 = __builtin_add_overflow(a, b, &sum); + bool const c2 = __builtin_add_overflow(sum, carry, &sum); + return c1 || c2; +#else +#if BOOST_COMP_MSVC && BOOST_ARCH_X86_64 + if !consteval + { + if constexpr (std::same_as) + return _addcarry_u64(carry, a, b, &sum) != 0; + else + return _addcarry_u32(carry, a, b, &sum) != 0; + } +#endif -template -struct AlwaysFalseT : std::bool_constant + // Portable implementation, used under constant evaluation with + // MSVC/x86-64 and as the fallback path on platforms that we do + // not have explicit support for. + T const s = static_cast(a + b); + bool const c1 = s < a; + sum = static_cast(s + carry); + bool const c2 = sum < s; + return c1 | c2; +#endif +} + +template +constexpr void +add(std::array& lhs, std::array const& rhs) noexcept { -}; + bool carry = false; + for (std::size_t i = N; i-- > 0;) + { + T sum; + carry = addCarry(toBigEndian(lhs[i]), toBigEndian(rhs[i]), carry, sum); + lhs[i] = toBigEndian(sum); + } +} + +template +constexpr void +increment(std::array& data) noexcept +{ + for (std::size_t i = N; i-- > 0;) + { + T native = toBigEndian(data[i]); + data[i] = toBigEndian(++native); + if (native != 0) + return; + } +} + +template +constexpr void +decrement(std::array& data) noexcept +{ + for (std::size_t i = N; i-- > 0;) + { + T const native = toBigEndian(data[i]); + data[i] = toBigEndian(native - 1); + if (native != 0) + return; + } +} + +template +[[nodiscard]] constexpr std::strong_ordering +compareLimbs(std::array const& lhs, std::array const& rhs) noexcept +{ + for (std::size_t i = 0; i != N; ++i) + { + if (auto const c = toBigEndian(lhs[i]) <=> toBigEndian(rhs[i]); c != 0) + return c; + } + return std::strong_ordering::equal; +} } // namespace detail /** - * Integers of any length that is a multiple of 32-bits + * Arbitrarily long unsigned integers. * * @note This class stores its values internally in big-endian * form and that internal representation is part of the @@ -77,22 +260,29 @@ struct AlwaysFalseT : std::bool_constant * the instantiation of "distinct" types that the same * number of bits. */ -template +template + requires(Bits % 32 == 0) && (Bits >= 64) class BaseUInt { - static_assert((Bits % 32) == 0, "The length of a base_uint in bits must be a multiple of 32."); - - static_assert(Bits >= 64, "The length of a base_uint in bits must be at least 64."); - - static constexpr std::size_t kWidth = Bits / 32; + /** The limb type used to store a BaseUInt of the given width. */ + using LimbType = std::conditional_t<(Bits % 64 == 0), std::uint64_t, std::uint32_t>; - // This is really big-endian in byte order. - // We sometimes use std::uint32_t for speed. + /** The number of limbs used to store a BaseUInt of the given width. */ + static constexpr std::size_t kLimbCount = Bits / (sizeof(LimbType) * 8); - std::array data_; + // Internal storage: big-endian limbs. data_[0] is the most significant + // limb. Within each limb, bytes are stored in big-endian order (via + // toBigEndian at write time). + std::array data_; public: - //-------------------------------------------------------------------------- + /** + * Value hashing function. + * The seed prevents crafted inputs from causing degenerate parent + * containers. + */ + using hasher = HardenedHash<>; + // // STL Container Interface // @@ -109,401 +299,276 @@ class BaseUInt using const_reference = value_type const&; using iterator = pointer; using const_iterator = const_pointer; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; using tag_type = Tag; - pointer - data() + [[nodiscard]] pointer + data() noexcept { return reinterpret_cast(data_.data()); } [[nodiscard]] const_pointer - data() const + data() const noexcept { return reinterpret_cast(data_.data()); } - iterator - begin() + [[nodiscard]] iterator + begin() noexcept { return data(); } - iterator - end() + [[nodiscard]] iterator + end() noexcept { return data() + kBytes; } [[nodiscard]] const_iterator - begin() const + begin() const noexcept { return data(); } [[nodiscard]] const_iterator - end() const + end() const noexcept { return data() + kBytes; } [[nodiscard]] const_iterator - cbegin() const + cbegin() const noexcept { return data(); } [[nodiscard]] const_iterator - cend() const + cend() const noexcept { return data() + kBytes; } /** - * Value hashing function. - * The seed prevents crafted inputs from causing degenerate parent - * containers. + * Zero-initialized value. */ - using hasher = HardenedHash<>; - - //-------------------------------------------------------------------------- + constexpr BaseUInt(beast::Zero) noexcept : data_{} + { + } -private: /** - * Construct from a raw pointer. - * The buffer pointed to by `data` must be at least Bits/8 bytes. + * Default construction: the value is zero-initialized. * - * @note the structure is used to disambiguate this from the std::uint64_t - * constructor: something like base_uint(0) is ambiguous. + * @note In an ideal world, the default constructor would leave the data + * uninitialized, but code may depend on the current semantics, so + * we cannot change this at this time without extensive auditing. */ - // NIKB TODO Remove the need for this constructor. - struct VoidHelper - { - explicit VoidHelper() = default; - }; - - explicit BaseUInt(void const* data, VoidHelper) - { - memcpy(data_.data(), data, kBytes); - } - - // Helper function to initialize a base_uint from a std::string_view. - enum class ParseResult { - Okay, - BadLength, - BadChar, - }; - - constexpr std::expected - parseFromStringView(std::string_view sv) noexcept - { - // Local lambda that converts a single hex char to four bits and - // ORs those bits into a uint32_t. - auto hexCharToUInt = [](char c, std::uint32_t shift, std::uint32_t& accum) -> ParseResult { - std::uint32_t nibble = 0xFFu; - if (c < '0' || c > 'f') - return ParseResult::BadChar; - - if (c >= 'a') - { - nibble = static_cast(c - 'a' + 0xA); - } - else if (c >= 'A') - { - nibble = static_cast(c - 'A' + 0xA); - } - else if (c <= '9') - { - nibble = static_cast(c - '0'); - } - - if (nibble > 0xFu) - return ParseResult::BadChar; - - accum |= (nibble << shift); - - return ParseResult::Okay; - }; - - decltype(data_) ret{}; - - if (sv == "0") - { - return ret; - } - - if (sv.size() != size() * 2) - return std::unexpected(ParseResult::BadLength); - - std::size_t i = 0u; - auto in = sv.begin(); - while (in != sv.end()) - { - std::uint32_t accum = {}; - for (std::uint32_t const shift : {4u, 0u, 12u, 8u, 20u, 16u, 28u, 24u}) - { - if (auto const result = hexCharToUInt(*in++, shift, accum); - result != ParseResult::Okay) - return std::unexpected(result); - } - ret[i++] = accum; - } - return ret; - } - - constexpr decltype(data_) - parseFromStringViewThrows(std::string_view sv) noexcept(false) + constexpr BaseUInt() noexcept : BaseUInt(beast::kZero) { - auto const result = parseFromStringView(sv); - if (!result) - { - if (result.error() == ParseResult::BadLength) - Throw("invalid length for hex string"); - - Throw("invalid hex character"); - } - return *result; + static_assert(sizeof(BaseUInt) == kBytes); + static_assert(std::has_unique_object_representations_v); } -public: - constexpr BaseUInt() : data_{} - { - } - - constexpr BaseUInt(beast::Zero) : data_{} + /** + * Construct from a byte sequence whose length is fixed at compile time. + * + * Accepts any contiguous range of `unsigned char` or `std::byte` whose + * extent is a compile-time constant equal to `size()`. Byte i of the + * source becomes byte i of the big-endian representation, so the first + * byte is the most significant. + * + * A source with any other static extent does not compile. A source whose + * length is only known at run time (`std::string`, `Blob`, `Slice`, a + * dynamic-extent span) is not accepted here and goes through `fromRaw`, + * which checks the length and reports failure. Converting such a source to + * a fixed-extent span at the call site is the caller's assertion that the + * length is correct; that conversion is not checked. + * + * @tparam R A type satisfying `detail::FixedByteRange`. + * @param bytes The bytes to copy. + */ + template R> + constexpr explicit BaseUInt(R const& bytes) noexcept { + std::array tmp; + std::ranges::transform( + bytes, tmp.begin(), [](auto b) { return static_cast(b); }); + data_ = std::bit_cast(tmp); } - explicit BaseUInt(std::uint64_t b) + /** + * Convert to or from the untagged type of the same width. + * + * The conversion is a bit-for-bit copy; the value is unchanged. This + * operation simply attaches or discards a tag. + * + * The serialisation layer is the primary user of the erasing direction. + */ + template + requires(std::is_void_v != std::is_void_v) + constexpr explicit BaseUInt(BaseUInt const& other) noexcept + : BaseUInt(std::bit_cast(other)) { - *this = b; } - // This constructor is intended to be used at compile time since it might - // throw at runtime. Consider declaring this constructor consteval once - // we get to C++23. - explicit constexpr BaseUInt(std::string_view sv) noexcept(false) - : data_(parseFromStringViewThrows(sv)) + /** + * Compile-time construction from a hexadecimal string literal. + * + * @param hex Either the string literal "0" or precisely 2 * kBytes + * hexadecimal characters. + */ + explicit consteval BaseUInt(std::string_view hex) + : data_{[&] { + if (auto const r = detail::parseHex(hex)) + return *r; + throw "invalid hexadecimal literal string"; + }()} { } - template - explicit BaseUInt(Container const& c) - requires( - detail::IsContiguousContainer::value && - std::is_trivially_copyable_v) - { - // Use AlwaysFalseT so the static_assert condition is dependent - // and only triggers when this constructor template is instantiated. - static_assert( - detail::AlwaysFalseT::value, - "This constructor is not intended to be used and will be soon removed. " - "Use base_uint::fromRaw instead."); - } + /** + * Compile-time construction from a non-negative integer value. + */ + template + requires(sizeof(U) <= sizeof(std::uint64_t) && !std::same_as) + explicit consteval BaseUInt(U value) + : data_{[&] { + if constexpr (std::signed_integral) + { + if (value < 0) + throw "negative value"; + } - template - static BaseUInt - fromRaw(Container const& c) - requires( - detail::IsContiguousContainer::value && - std::is_trivially_copyable_v) + auto v = static_cast(value); + std::array bytes{}; + for (std::size_t i = 0; i != sizeof(v); ++i) + { + bytes[kBytes - 1 - i] = static_cast(v); + v >>= 8; + } + return std::bit_cast(bytes); + }()} { - BaseUInt result; - XRPL_ASSERT( - c.size() * sizeof(typename Container::value_type) == size(), - "xrpl::BaseUInt::fromRaw(Container auto) : input size match"); - std::size_t const canCopy = - std::min(size(), c.size() * sizeof(typename Container::value_type)); - std::memcpy(result.data_.data(), c.data(), canCopy); - return result; } - template - BaseUInt& - operator=(Container const& c) - requires( - detail::IsContiguousContainer::value && - std::is_trivially_copyable_v) - { - XRPL_ASSERT( - c.size() * sizeof(typename Container::value_type) == size(), - "xrpl::BaseUInt::operator=(Container auto) : input size match"); - std::size_t const canCopy = - std::min(size(), c.size() * sizeof(typename Container::value_type)); - if (canCopy < size()) - *this = beast::kZero; - std::memcpy(data_.data(), c.data(), canCopy); - return *this; - } + constexpr BaseUInt(BaseUInt const& b) noexcept = default; + constexpr BaseUInt& + operator=(BaseUInt const& b) noexcept = default; - /* Construct from a raw pointer. - The buffer pointed to by `data` must be at least Bits/8 bytes. - */ - static BaseUInt - fromVoid(void const* data) + /** + * Construct from a byte sequence whose length is only known at run time. + * + * @return The value, or an unseated optional if the source is not exactly + * `size()` bytes long. + */ + template + [[nodiscard]] static constexpr std::optional + fromRaw(Container const& c) noexcept { - return BaseUInt(data, VoidHelper()); - } + if (std::ranges::size(c) != size()) + return std::nullopt; - template - static std::optional - fromVoidChecked(T const& from) - { - if (from.size() != size()) - return {}; - return fromVoid(from.data()); + std::array bytes; + std::ranges::transform( + c, bytes.begin(), [](auto b) { return static_cast(b); }); + return BaseUInt{bytes}; } [[nodiscard]] constexpr int - signum() const + signum() const noexcept { - for (int i = 0; i < kWidth; i++) - { - if (data_[i] != 0) - return 1; - } - - return 0; + return std::ranges::any_of(data_, [](auto v) { return v != 0; }) ? 1 : 0; } - bool - operator!() const + constexpr bool + operator!() const noexcept { - return *this == beast::kZero; + return signum() == 0; } - constexpr BaseUInt - operator~() const + [[nodiscard]] constexpr BaseUInt + operator~() const noexcept { BaseUInt ret; - - for (int i = 0; i < kWidth; i++) - ret.data_[i] = ~data_[i]; - + std::ranges::transform(data_, ret.data_.begin(), std::bit_not{}); return ret; } - BaseUInt& - operator=(std::uint64_t uHost) + constexpr BaseUInt& + operator=(beast::Zero) noexcept { - *this = beast::kZero; - // NOLINTBEGIN(cppcoreguidelines-pro-type-member-init) - union - { - unsigned u[2]; - std::uint64_t ul; - }; - // NOLINTEND(cppcoreguidelines-pro-type-member-init) - // Put in least significant bits. - ul = boost::endian::native_to_big(uHost); - data_[kWidth - 2] = u[0]; - data_[kWidth - 1] = u[1]; + data_.fill(0); return *this; } - BaseUInt& - operator^=(BaseUInt const& b) + constexpr BaseUInt& + operator^=(BaseUInt const& b) noexcept { - for (int i = 0; i < kWidth; i++) - data_[i] ^= b.data_[i]; - + std::ranges::transform(data_, b.data_, data_.begin(), std::bit_xor{}); return *this; } - BaseUInt& - operator&=(BaseUInt const& b) + constexpr BaseUInt& + operator&=(BaseUInt const& b) noexcept { - for (int i = 0; i < kWidth; i++) - data_[i] &= b.data_[i]; - + std::ranges::transform(data_, b.data_, data_.begin(), std::bit_and{}); return *this; } - BaseUInt& - operator|=(BaseUInt const& b) + constexpr BaseUInt& + operator|=(BaseUInt const& b) noexcept { - for (int i = 0; i < kWidth; i++) - data_[i] |= b.data_[i]; - + std::ranges::transform(data_, b.data_, data_.begin(), std::bit_or{}); return *this; } - BaseUInt& - operator++() + constexpr BaseUInt& + operator+=(BaseUInt const& b) noexcept { - // prefix operator - for (int i = kWidth - 1; i >= 0; --i) - { - data_[i] = boost::endian::native_to_big(boost::endian::big_to_native(data_[i]) + 1); - if (data_[i] != 0) - break; - } + detail::add(data_, b.data_); + return *this; + } + constexpr BaseUInt& + operator++() noexcept + { + detail::increment(data_); return *this; } - BaseUInt - operator++(int) + constexpr BaseUInt + operator++(int) noexcept { - // postfix operator - BaseUInt const ret = *this; + BaseUInt ret = *this; ++(*this); return ret; } - BaseUInt& - operator--() + constexpr BaseUInt& + operator--() noexcept { - for (int i = kWidth - 1; i >= 0; --i) - { - auto prev = data_[i]; - data_[i] = boost::endian::native_to_big(boost::endian::big_to_native(data_[i]) - 1); - - if (prev != 0) - break; - } - + detail::decrement(data_); return *this; } - BaseUInt - operator--(int) + constexpr BaseUInt + operator--(int) noexcept { - // postfix operator - BaseUInt const ret = *this; + BaseUInt ret = *this; --(*this); return ret; } - [[nodiscard]] BaseUInt - next() const + [[nodiscard]] constexpr BaseUInt + next() const noexcept { auto ret = *this; return ++ret; } - [[nodiscard]] BaseUInt - prev() const + [[nodiscard]] constexpr BaseUInt + prev() const noexcept { auto ret = *this; return --ret; } - BaseUInt& - operator+=(BaseUInt const& b) - { - std::uint64_t carry = 0; - - for (int i = kWidth - 1; i >= 0; i--) - { - std::uint64_t const n = carry + boost::endian::big_to_native(data_[i]) + - boost::endian::big_to_native(b.data_[i]); - - data_[i] = boost::endian::native_to_big(static_cast(n)); - carry = n >> 32; - } - - return *this; - } - template friend void hash_append(Hasher& h, BaseUInt const& a) noexcept @@ -518,143 +583,107 @@ class BaseUInt * The input must be precisely `2 * bytes` hexadecimal characters * long, with one exception: the value '0'. * - * @param sv A string of hexadecimal characters + * @param sv The hexadecimal characters composing the string * @return true if the input was parsed properly; false otherwise. */ [[nodiscard]] constexpr bool - parseHex(std::string_view sv) + parseHex(std::string_view sv) noexcept { - auto const result = parseFromStringView(sv); - if (!result) - return false; + auto const r = detail::parseHex(sv); - data_ = *result; - return true; - } + if (r) + data_ = *r; - [[nodiscard]] constexpr bool - parseHex(char const* str) - { - return parseHex(std::string_view{str}); + return r.has_value(); } - [[nodiscard]] bool - parseHex(std::string const& str) + [[nodiscard]] static constexpr std::size_t + size() noexcept { - return parseHex(std::string_view{str}); + return kBytes; } - static constexpr std::size_t - size() + // Deprecated. + [[nodiscard]] constexpr bool + isZero() const noexcept { - return kBytes; + return *this == beast::kZero; } - BaseUInt& - operator=(beast::Zero) + [[nodiscard]] constexpr bool + isNonZero() const noexcept { - data_.fill(0); - return *this; + return !isZero(); } - // Deprecated. - [[nodiscard]] bool - isZero() const + constexpr void + zero() noexcept { - return *this == beast::kZero; + *this = beast::kZero; } - [[nodiscard]] bool - isNonZero() const + + // Comparison via hidden friends + friend constexpr std::strong_ordering + operator<=>(BaseUInt const& lhs, BaseUInt const& rhs) noexcept { - return *this != beast::kZero; + return detail::compareLimbs(lhs.data_, rhs.data_); } - void - zero() + + friend constexpr bool + operator==(BaseUInt const& lhs, BaseUInt const& rhs) noexcept { - *this = beast::kZero; + return lhs.data_ == rhs.data_; } }; +/** + * Specific instantiations, commonly used in the codebase. + */ using uint128 = BaseUInt<128>; using uint160 = BaseUInt<160>; -using uint256 = BaseUInt<256>; using uint192 = BaseUInt<192>; - -template -[[nodiscard]] constexpr std::strong_ordering -operator<=>(BaseUInt const& lhs, BaseUInt const& rhs) -{ - // This comparison might seem wrong on a casual inspection because it - // compares data internally stored as std::uint32_t byte-by-byte. But - // note that the underlying data is stored in big endian, even if the - // platform is little endian. This makes the comparison correct. - // - // FIXME: use std::lexicographical_compare_three_way once support is - // added to MacOS. - - auto const ret = std::mismatch(lhs.cbegin(), lhs.cend(), rhs.cbegin()); - - // a == b - if (ret.first == lhs.cend()) - return std::strong_ordering::equivalent; - - return (*ret.first > *ret.second) ? std::strong_ordering::greater : std::strong_ordering::less; -} - -template -[[nodiscard]] constexpr bool -operator==(BaseUInt const& lhs, BaseUInt const& rhs) -{ - return (lhs <=> rhs) == 0; // NOLINT(modernize-use-nullptr) -} - -//------------------------------------------------------------------------------ -template -constexpr bool -operator==(BaseUInt const& a, std::uint64_t b) -{ - return a == BaseUInt(b); -} +using uint256 = BaseUInt<256>; +using uint512 = BaseUInt<512>; //------------------------------------------------------------------------------ template -constexpr BaseUInt -operator^(BaseUInt const& a, BaseUInt const& b) +[[nodiscard]] constexpr BaseUInt +operator^(BaseUInt const& a, BaseUInt const& b) noexcept { return BaseUInt(a) ^= b; } template -constexpr BaseUInt -operator&(BaseUInt const& a, BaseUInt const& b) +[[nodiscard]] constexpr BaseUInt +operator&(BaseUInt const& a, BaseUInt const& b) noexcept { return BaseUInt(a) &= b; } template -constexpr BaseUInt -operator|(BaseUInt const& a, BaseUInt const& b) +[[nodiscard]] constexpr BaseUInt +operator|(BaseUInt const& a, BaseUInt const& b) noexcept { return BaseUInt(a) |= b; } template -constexpr BaseUInt -operator+(BaseUInt const& a, BaseUInt const& b) +[[nodiscard]] constexpr BaseUInt +operator+(BaseUInt const& a, BaseUInt const& b) noexcept { return BaseUInt(a) += b; } //------------------------------------------------------------------------------ template -inline std::string +[[nodiscard]] inline std::string to_string(BaseUInt const& a) { return strHex(a.cbegin(), a.cend()); } template -inline std::string +[[nodiscard]] inline std::string toShortString(BaseUInt const& a) { static_assert(BaseUInt::kBytes > 4, "For 4 bytes or less, use a native type"); @@ -668,6 +697,14 @@ operator<<(std::ostream& out, BaseUInt const& u) return out << to_string(u); } +/** Bucket selection for partitioned maps + * + * @note this is NOT a cryptographic hash and is only useful if the + * values being passed in are uniformly distributed. + * + * @param key The key value + * @return the bucket + */ template <> inline std::size_t extract(uint256 const& key) @@ -679,17 +716,12 @@ extract(uint256 const& key) return result; } -#ifndef __INTELLISENSE__ -static_assert(sizeof(uint128) == 128 / 8, "There should be no padding bytes"); -static_assert(sizeof(uint160) == 160 / 8, "There should be no padding bytes"); -static_assert(sizeof(uint192) == 192 / 8, "There should be no padding bytes"); -static_assert(sizeof(uint256) == 256 / 8, "There should be no padding bytes"); -#endif +template +concept BaseUIntType = detail::isBaseUInt>; } // namespace xrpl namespace beast { - template struct IsUniquelyRepresented> : public std::true_type { diff --git a/include/xrpl/basics/contract.h b/include/xrpl/basics/contract.h index 6588cb5d1a1..700cbdf4873 100644 --- a/include/xrpl/basics/contract.h +++ b/include/xrpl/basics/contract.h @@ -1,7 +1,8 @@ #pragma once #include -#include + +#include #include #include @@ -55,7 +56,8 @@ Throw(Args&&... args) std::is_convertible_v, "Exception must derive from std::exception."); E e(std::forward(args)...); - logThrow(std::string("Throwing exception of type " + beast::typeName() + ": ") + e.what()); + logThrow( + "Throwing exception of type " + boost::core::type_name() + ": " + e.what()); throw std::move(e); } diff --git a/include/xrpl/basics/safe_cast.h b/include/xrpl/basics/safe_cast.h index 7f2b93a7ebe..db1381ae722 100644 --- a/include/xrpl/basics/safe_cast.h +++ b/include/xrpl/basics/safe_cast.h @@ -2,107 +2,213 @@ #include // IWYU pragma: keep +#include +#include +#include +#include #include +#include namespace xrpl { -// safe_cast adds compile-time checks to a static_cast to ensure that -// the destination can hold all values of the source. This is particularly -// handy when the source or destination is an enumeration type. +/** Every value of @p Src can be represented by @p Dest. -template -concept SafeToCast = (std::is_integral_v && std::is_integral_v) && - (std::is_signed_v || std::is_unsigned_v) && - (std::is_signed_v != std::is_signed_v ? sizeof(Dest) > sizeof(Src) - : sizeof(Dest) >= sizeof(Src)); + Given two integral types, the cast is safe when the destination can + hold every possible value of the source. -template + 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 +concept SafeToCast = std::is_integral_v && std::is_integral_v && []() consteval { + using WideSrc = std::conditional_t, std::intmax_t, std::uintmax_t>; + using WideDest = std::conditional_t, 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(std::numeric_limits::min()), + static_cast(std::numeric_limits::min())) && + std::cmp_greater_equal( + static_cast(std::numeric_limits::max()), + static_cast(std::numeric_limits::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 + requires(std::is_integral_v && std::is_integral_v) constexpr Dest safeCast(Src s) noexcept - requires(std::is_integral_v && std::is_integral_v) { static_assert( - std::is_signed_v || std::is_unsigned_v, "Cannot cast signed to unsigned"); - constexpr unsigned kNotSame = std::is_signed_v != std::is_signed_v; - static_assert( - sizeof(Dest) >= sizeof(Src) + kNotSame, - "Destination is too small to hold all values of source"); + SafeToCast, "This cast is not value-preserving. Please use unsafeCast instead."); return static_cast(s); } -template +template + requires(std::is_enum_v && std::is_integral_v) constexpr Dest safeCast(Src s) noexcept - requires(std::is_enum_v && std::is_integral_v) { return static_cast(safeCast>(s)); } -template +template + requires(std::is_integral_v && std::is_enum_v) constexpr Dest safeCast(Src s) noexcept - requires(std::is_integral_v && std::is_enum_v) { - return safeCast(static_cast>(s)); + return safeCast(std::to_underlying(s)); } +/** @} */ -// unsafe_cast explicitly flags a static_cast as not necessarily able to hold -// all values of the source. It includes a compile-time check so that if -// underlying types become safe, it can be converted to a safe_cast. +/** Integral-to-integral cast that is known to be narrowing or sign-erasing. -template + 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 + requires(std::is_integral_v && std::is_integral_v) constexpr Dest unsafeCast(Src s) noexcept - requires(std::is_integral_v && std::is_integral_v) { static_assert( - !SafeToCast, - "Only unsafe if casting signed to unsigned or " - "destination is too small"); + !SafeToCast, "This cast is value-preserving. Please use safeCast instead."); return static_cast(s); } -template +template + requires(std::is_enum_v && std::is_integral_v) constexpr Dest unsafeCast(Src s) noexcept - requires(std::is_enum_v && std::is_integral_v) { return static_cast(unsafeCast>(s)); } -template +template + requires(std::is_integral_v && std::is_enum_v) constexpr Dest unsafeCast(Src s) noexcept +{ + return unsafeCast(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 + requires(std::is_integral_v && std::is_integral_v) +constexpr Dest +checkedCast(Src s) noexcept +{ + return static_cast(s); +} + +template + requires(std::is_enum_v && std::is_integral_v) +constexpr Dest +checkedCast(Src s) noexcept +{ + return static_cast(checkedCast>(s)); +} + +template requires(std::is_integral_v && std::is_enum_v) +constexpr Dest +checkedCast(Src s) noexcept { - return unsafeCast(static_cast>(s)); + return checkedCast(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 - requires std::is_pointer_v + requires( + std::is_pointer_v && std::is_polymorphic_v && + std::derived_from, Src> && + (std::is_const_v> || !std::is_const_v) && + (std::is_volatile_v> || !std::is_volatile_v)) inline Dest safeDowncast(Src* s) noexcept { -#ifdef NDEBUG + XRPL_ASSERT( + s == nullptr || dynamic_cast(s) != nullptr, + "xrpl::safeDowncast : valid downcast"); return static_cast(s); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast) -#else - auto* result = dynamic_cast(s); - XRPL_ASSERT(result != nullptr, "xrpl::safeDowncast : pointer downcast is valid"); - return result; -#endif } template - requires std::is_lvalue_reference_v + requires( + std::is_lvalue_reference_v && std::is_polymorphic_v && + std::derived_from, Src>) inline Dest safeDowncast(Src& s) noexcept { -#ifndef NDEBUG - XRPL_ASSERT( - dynamic_cast>>(&s) != nullptr, - "xrpl::safeDowncast : reference downcast is valid"); -#endif - return static_cast(s); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast) + return *safeDowncast>>(std::addressof(s)); } +/** @} */ } // namespace xrpl diff --git a/include/xrpl/basics/spinlock.h b/include/xrpl/basics/spinlock.h index 87611f20ba2..e013dbd5479 100644 --- a/include/xrpl/basics/spinlock.h +++ b/include/xrpl/basics/spinlock.h @@ -4,210 +4,266 @@ #include +#include + #include +#include #include #include -#ifndef __aarch64__ +#if BOOST_ARCH_X86 #include #endif namespace xrpl { +/** An unsigned integral type suitable for use as a spinlock. + + The type must be always lock-free when wrapped in std::atomic, so + that lock operations cannot themselves take a (library-level) lock. + */ +template +concept SpinlockValueType = std::is_unsigned_v && std::atomic::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 +concept PackedSpinlockValueType = SpinlockValueType && requires(std::atomic& a, T v) { + { a.fetch_or(v) } -> std::same_as; + { a.fetch_and(v) } -> std::same_as; +}; + namespace detail { -/** - * Inform the processor that we are in a tight spin-wait loop. - * - * Spinlocks caught in tight loops can result in the processor's pipeline - * filling up with comparison operations, resulting in a misprediction at - * the time the lock is finally acquired, necessitating pipeline flushing - * which is ridiculously expensive and results in very high latency. - * - * This function instructs the processor to "pause" for some architecture - * specific amount of time, to prevent this. + +/** Inform the processor that we are in a tight spin-wait loop. + + Spinlocks caught in tight loops can result in the processor's pipeline + filling up with comparison operations, resulting in a misprediction at + the time the lock is finally acquired, necessitating pipeline flushing + which is ridiculously expensive and results in very high latency. + + This function instructs the processor to "pause" for some architecture + specific amount of time, to prevent this. */ inline void spinPause() noexcept { -#ifdef __aarch64__ - asm volatile("yield"); -#else +#if BOOST_ARCH_X86 _mm_pause(); +#elif BOOST_ARCH_ARM + asm volatile("yield" ::: "memory"); +#else +#error No implementation available for spinPause to use #endif } } // 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. + + @note This interface is primarily intended for one-shot attempts to + acquire the lock. Avoid calling this function directly from a + loop and use @ref spinLock instead. + + @tparam T An unsigned integral type. + @param lock The atomic variable used as the lock. + @return true if the lock was acquired, false if it was already held. */ +template +[[nodiscard]] bool +spinTryLock(std::atomic& 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::max(), + std::memory_order::acquire, + std::memory_order::relaxed); + ; +} + +/** Acquire a spinlock, blocking until available. -/** - * A class that grabs a single packed spinlock from an atomic integer. - * - * This class meets the requirements of Lockable: - * https://en.cppreference.com/w/cpp/named_req/Lockable + 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 -class PackedSpinlock +template +void +spinLock(std::atomic& lock) noexcept { - // clang-format off - static_assert(std::is_unsigned_v); - static_assert(std::atomic::is_always_lock_free); - static_assert( - std::is_same_v&>().fetch_or(0)), T> && - std::is_same_v&>().fetch_and(0)), T>, - "std::atomic::fetch_and(T) and std::atomic::fetch_and(T) are required by packed_spinlock"); - // clang-format on - -private: - std::atomic& bits_; - T const mask_; + 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 +void +spinUnlock(std::atomic& 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 +class Spinlock +{ + std::atomic& lock_; public: - PackedSpinlock(PackedSpinlock const&) = delete; - PackedSpinlock& - operator=(PackedSpinlock const&) = delete; + Spinlock(Spinlock const&) = delete; + Spinlock& + operator=(Spinlock const&) = delete; + + /** Construct a spinlock handle. - /** - * A single spinlock packed inside the specified atomic - * - * @param lock The atomic integer inside which the spinlock is packed. - * @param index The index of the spinlock this object acquires. - * - * @note For performance reasons, you should strive to have `lock` be - * on a cacheline by itself. + @param lock The atomic integer to spin against. + + @note For performance reasons, you should strive to have `lock` be + on a cacheline by itself. */ - PackedSpinlock(std::atomic& lock, int index) : bits_(lock), mask_(static_cast(1) << index) + explicit Spinlock(std::atomic& lock) noexcept : lock_(lock) { - XRPL_ASSERT( - index >= 0 && (mask_ != 0), - "xrpl::PackedSpinlock::PackedSpinlock : valid index and mask"); } [[nodiscard]] bool - try_lock() // NOLINT(readability-identifier-naming) + try_lock() noexcept // NOLINT(readability-identifier-naming) { - return (bits_.fetch_or(mask_, std::memory_order_acquire) & mask_) == 0; + return spinTryLock(lock_); } void - lock() + lock() noexcept { - 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(); - } + spinLock(lock_); } void - unlock() + unlock() noexcept { - bits_.fetch_and(~mask_, std::memory_order_release); + spinUnlock(lock_); } }; -/** - * 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 +//------------------------------------------------------------------------------ + +/** 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 -class Spinlock +template +class PackedSpinlock { - static_assert(std::is_unsigned_v); - static_assert(std::atomic::is_always_lock_free); - -private: - std::atomic& lock_; + std::atomic& bits_; + T const mask_; public: - Spinlock(Spinlock const&) = delete; - Spinlock& - operator=(Spinlock const&) = delete; + PackedSpinlock(PackedSpinlock const&) = delete; + PackedSpinlock& + operator=(PackedSpinlock const&) = delete; + + /** Construct a packed spinlock handle for a single bit. - /** - * 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. + @param lock The atomic integer inside which the spinlock is packed. + @param index The index of the spinlock this object acquires. + + @note For performance reasons, you should strive to have `lock` be + on a cacheline by itself. */ - Spinlock(std::atomic& lock) : lock_(lock) + PackedSpinlock(std::atomic& lock, int index) noexcept + : bits_(lock), mask_([index]() { + XRPL_ASSERT( + index >= 0 && index < std::numeric_limits::digits, + "xrpl::PackedSpinlock::PackedSpinlock : valid index"); + return static_cast(T{1} << index); + }()) { } [[nodiscard]] bool - try_lock() // NOLINT(readability-identifier-naming) + try_lock() noexcept // NOLINT(readability-identifier-naming) { - T expected = 0; - - return lock_.compare_exchange_weak( - expected, - std::numeric_limits::max(), - std::memory_order_acquire, - std::memory_order_relaxed); + return (bits_.fetch_or(mask_, std::memory_order::acquire) & mask_) == 0; } void - lock() + lock() noexcept { - while (!try_lock()) + do { // 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 (lock_.load(std::memory_order_relaxed) != 0) + // of contention by avoiding writes that are unlikely to grab + // the requested lock. + while ((bits_.load(std::memory_order::relaxed) & mask_) != 0) detail::spinPause(); - } + } while (!try_lock()); } void - unlock() + unlock() noexcept { - lock_.store(0, std::memory_order_release); + bits_.fetch_and(~mask_, std::memory_order::release); } }; -/** @} */ } // namespace xrpl diff --git a/include/xrpl/beast/type_name.h b/include/xrpl/beast/type_name.h deleted file mode 100644 index 85fd9ae6a28..00000000000 --- a/include/xrpl/beast/type_name.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#ifndef _MSC_VER -#include -#endif - -namespace beast { - -template -std::string -typeName() -{ - using TR = std::remove_reference_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) - name += " const"; - if (std::is_volatile_v) - name += " volatile"; - if (std::is_lvalue_reference_v) - { - name += "&"; - } - else if (std::is_rvalue_reference_v) - { - name += "&&"; - } - - return name; -} - -} // namespace beast diff --git a/include/xrpl/beast/utility/Zero.h b/include/xrpl/beast/utility/Zero.h index 406921c5001..29d9b1553df 100644 --- a/include/xrpl/beast/utility/Zero.h +++ b/include/xrpl/beast/utility/Zero.h @@ -2,26 +2,24 @@ #pragma once +#include +#include + namespace beast { /** * Zero allows classes to offer efficient comparisons to zero. * - * Zero is a struct to allow classes to efficiently compare with zero without - * requiring an rvalue construction. - * * It's often the case that we have classes which combine a number and a unit. * In such cases, comparisons like t > 0 or t != 0 make sense, but comparisons - * like t > 1 or t != 1 do not. - * - * The class Zero allows such comparisons to be easily made. + * like t > 1 or t != 1 do not. Comparing against kZero expresses exactly that, + * without constructing a T. * - * The comparing class T either needs to have a method called signum() which - * returns a positive number, 0, or a negative; or there needs to be a signum - * function which resolves in the namespace which takes an instance of T and - * returns a positive, zero or negative number. + * A type T participates if either `t.signum()` or an unqualified `signum(t)` + * found by argument-dependent lookup returns an integer that is negative, + * zero, or positive according to the sign of t. Both `t == kZero` and + * `kZero == t` work, as do all six relational operators in either order. */ - struct Zero { explicit Zero() = default; @@ -30,115 +28,44 @@ struct Zero inline constexpr Zero kZero{}; /** - * Default implementation of signum calls the method on the class. + * Default implementation of signum: call the member function. */ -template -auto -signum(T const& t) +template + requires requires(T const& t) { + { t.signum() } -> std::integral; + } +[[nodiscard]] constexpr auto +signum(T const& t) noexcept(noexcept(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 -auto -callSignum(T const& t) -{ - return signum(t); -} - -} // namespace detail::zero_helper - -// Handle operators where T is on the left side using signum. - -template -bool -operator==(T const& t, Zero) -{ - return detail::zero_helper::callSignum(t) == 0; -} - -template -bool -operator!=(T const& t, Zero) -{ - return detail::zero_helper::callSignum(t) != 0; -} - -template -bool -operator<(T const& t, Zero) -{ - return detail::zero_helper::callSignum(t) < 0; -} - -template -bool -operator>(T const& t, Zero) -{ - return detail::zero_helper::callSignum(t) > 0; -} - -template -bool -operator>=(T const& t, Zero) -{ - return detail::zero_helper::callSignum(t) >= 0; -} - -template -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 -bool -operator==(Zero, T const& t) -{ - return t == kZero; -} - -template -bool -operator!=(Zero, T const& t) -{ - return t != kZero; -} - -template -bool -operator<(Zero, T const& t) -{ - return t > kZero; -} +concept HasSignum = requires(T const& t) { + { signum(t) } -> std::integral; +}; -template -bool -operator>(Zero, T const& t) -{ - return t < kZero; -} +} // namespace detail -template -bool -operator>=(Zero, T const& t) +template +[[nodiscard]] constexpr bool +operator==(T const& t, Zero) noexcept(noexcept(signum(t))) { - return t <= kZero; + return signum(t) == 0; } -template -bool -operator<=(Zero, T const& t) +template +[[nodiscard]] constexpr std::strong_ordering +operator<=>(T const& t, Zero) noexcept(noexcept(signum(t))) { - return t >= kZero; + return signum(t) <=> 0; } } // namespace beast diff --git a/include/xrpl/nodestore/detail/DecodedBlob.h b/include/xrpl/nodestore/detail/DecodedBlob.h index bd90ff2f1b8..eeafacc476b 100644 --- a/include/xrpl/nodestore/detail/DecodedBlob.h +++ b/include/xrpl/nodestore/detail/DecodedBlob.h @@ -1,8 +1,10 @@ #pragma once +#include #include #include +#include namespace xrpl::node_store { @@ -23,7 +25,7 @@ class DecodedBlob /** * Construct the decoded blob from raw data. */ - DecodedBlob(void const* key, void const* value, int valueBytes); + DecodedBlob(std::optional key, void const* value, int valueBytes); /** * Determine if the decoding was successful. @@ -43,10 +45,10 @@ class DecodedBlob private: bool success_{false}; - void const* key_; + uint256 key_; NodeObjectType objectType_{NodeObjectType::Unknown}; unsigned char const* objectData_{nullptr}; - int dataBytes_; + int dataBytes_ = 0; }; } // namespace xrpl::node_store diff --git a/include/xrpl/nodestore/detail/EncodedBlob.h b/include/xrpl/nodestore/detail/EncodedBlob.h index d171df3cc91..af4f92e18c2 100644 --- a/include/xrpl/nodestore/detail/EncodedBlob.h +++ b/include/xrpl/nodestore/detail/EncodedBlob.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -37,11 +38,6 @@ namespace xrpl::node_store { class EncodedBlob { - /** - * The 32-byte key of the serialized object. - */ - std::array key_{}; - /** * A pre-allocated buffer for the serialized object. * @@ -65,6 +61,11 @@ class EncodedBlob */ std::uint8_t* const ptr_; + /** + * The 32-byte key of the serialized object. + */ + uint256 key_; + public: explicit EncodedBlob(std::shared_ptr const& obj) : size_([&obj]() { @@ -76,11 +77,12 @@ class EncodedBlob return obj->getData().size() + 9; }()) , ptr_((size_ <= payload_.size()) ? payload_.data() : new std::uint8_t[size_]) + , key_(obj->getHash()) { std::fill_n(ptr_, 8, std::uint8_t{0}); ptr_[8] = static_cast(obj->getType()); + std::copy_n(obj->getData().data(), obj->getData().size(), ptr_ + 9); - std::copy_n(obj->getHash().data(), obj->getHash().size(), key_.data()); } ~EncodedBlob() @@ -95,10 +97,10 @@ class EncodedBlob delete[] ptr_; } - [[nodiscard]] void const* + [[nodiscard]] uint256 const& getKey() const noexcept { - return static_cast(key_.data()); + return key_; } [[nodiscard]] std::size_t diff --git a/include/xrpl/protocol/AccountID.h b/include/xrpl/protocol/AccountID.h index ab3c5a996b1..e29b3ac036e 100644 --- a/include/xrpl/protocol/AccountID.h +++ b/include/xrpl/protocol/AccountID.h @@ -61,14 +61,22 @@ parseBase58(std::string const& s); /** * A special account that's used as the "issuer" for XRP. */ -AccountID const& -xrpAccount(); +constexpr inline AccountID const& +xrpAccount() noexcept +{ + static constexpr AccountID kAccount(beast::kZero); + return kAccount; +} /** * A placeholder for empty accounts. */ -AccountID const& -noAccount(); +constexpr inline AccountID const& +noAccount() noexcept +{ + static constexpr AccountID kAccount = xrpAccount().next(); + return kAccount; +} /** * Convert hex or base58 string to AccountID. @@ -80,10 +88,10 @@ bool toIssuer(AccountID&, std::string const&); // DEPRECATED Should be checking the currency or native flag -inline bool -isXRP(AccountID const& c) +constexpr inline bool +isXRP(AccountID const& c) noexcept { - return c == beast::kZero; + return c == xrpAccount(); } // DEPRECATED @@ -101,22 +109,6 @@ operator<<(std::ostream& os, AccountID const& x) return os; } -/** - * Initialize the global cache used to map AccountID to base58 conversions. - * - * The cache is optional and need not be initialized. But because conversion - * is expensive (it requires a SHA-256 operation) in most cases the overhead - * of the cache is worth the benefit. - * - * @param count The number of entries the cache should accommodate. Zero will - * disable the cache, releasing any memory associated with it. - * - * @note The function will only initialize the cache the first time it is - * invoked. Subsequent invocations do nothing. - */ -void -initAccountIdCache(std::size_t count); - } // namespace xrpl //------------------------------------------------------------------------------ diff --git a/include/xrpl/protocol/KnownFormats.h b/include/xrpl/protocol/KnownFormats.h index 385feb2c278..baf5c0eb856 100644 --- a/include/xrpl/protocol/KnownFormats.h +++ b/include/xrpl/protocol/KnownFormats.h @@ -1,10 +1,10 @@ #pragma once #include -#include #include #include +#include #include #include @@ -84,7 +84,7 @@ class KnownFormats * Derived classes will load the object with all the known formats. */ private: - KnownFormats() : name_(beast::typeName()) + KnownFormats() : name_(boost::core::type_name()) { } diff --git a/include/xrpl/protocol/STBitString.h b/include/xrpl/protocol/STBitString.h index 6f71f48f252..ecc7e220b02 100644 --- a/include/xrpl/protocol/STBitString.h +++ b/include/xrpl/protocol/STBitString.h @@ -33,7 +33,20 @@ class STBitString final : public STBase, public CountedObject> STBitString(SField const& n); STBitString(value_type const& v); + + template + requires(!std::is_void_v) + STBitString(BaseUInt const& v) : value_(v) + { + } + STBitString(SField const& n, value_type const& v); + template + requires(!std::is_void_v) + STBitString(SField const& n, BaseUInt const& v) : STBase(n), value_(v) + { + } + STBitString(SerialIter& sit, SField const& name); [[nodiscard]] SerializedTypeID @@ -166,7 +179,7 @@ template void STBitString::setValue(BaseUInt const& v) { - value_ = v; + value_ = value_type{v}; } template diff --git a/include/xrpl/protocol/Serializer.h b/include/xrpl/protocol/Serializer.h index 997199629a0..58f216b215e 100644 --- a/include/xrpl/protocol/Serializer.h +++ b/include/xrpl/protocol/Serializer.h @@ -596,7 +596,7 @@ template BaseUInt SerialIter::getBitString() { - auto const n = Bits / 8; + constexpr auto n = BaseUInt::size(); if (remain_ < n) Throw("invalid SerialIter getBitString"); @@ -607,7 +607,7 @@ SerialIter::getBitString() used_ += n; remain_ -= n; - return BaseUInt::fromVoid(x); + return BaseUInt{std::span{x, n}}; } } // namespace xrpl diff --git a/include/xrpl/protocol/SystemParameters.h b/include/xrpl/protocol/SystemParameters.h index 6ca36c8d9a0..c3f65ad4a5b 100644 --- a/include/xrpl/protocol/SystemParameters.h +++ b/include/xrpl/protocol/SystemParameters.h @@ -14,7 +14,7 @@ namespace xrpl { // Various protocol and system specific constant globals. /* The name of the system. */ -static inline std::string const& +inline std::string const& systemName() { static std::string const kName = "xrpld"; @@ -52,11 +52,10 @@ isLegalAmountSigned(XRPAmount const& amount) } /* The currency code for the native currency. */ -static inline std::string const& +inline std::string systemCurrencyCode() { - static std::string const kCode = "XRP"; - return kCode; + return "XRP"; } /** diff --git a/include/xrpl/protocol/UintTypes.h b/include/xrpl/protocol/UintTypes.h index 25b0d1ffc9b..8b3b7cf042d 100644 --- a/include/xrpl/protocol/UintTypes.h +++ b/include/xrpl/protocol/UintTypes.h @@ -61,26 +61,40 @@ using Domain = BaseUInt<256>; /** * XRP currency. */ -Currency const& -xrpCurrency(); +constexpr inline Currency const& +xrpCurrency() noexcept +{ + static constexpr Currency const kCurrency(beast::kZero); + return kCurrency; +} /** * A placeholder for empty currencies. */ -Currency const& -noCurrency(); +constexpr inline Currency const& +noCurrency() noexcept +{ + static constexpr Currency const kCurrency(xrpCurrency().next()); + return kCurrency; +} /** * We deliberately disallow the currency that looks like "XRP" because too * many people were using it instead of the correct XRP currency. + * + * Note that this doesn't catch "xRP" or "xrp" or other case variations. */ -Currency const& -badCurrency(); +constexpr inline Currency const& +badCurrency() noexcept +{ + static constexpr Currency kCurrency{"0000000000000000000000005852500000000000"}; + return kCurrency; +} -inline bool -isXRP(Currency const& c) +constexpr inline bool +isXRP(Currency const& c) noexcept { - return c == beast::kZero; + return c == xrpCurrency(); } /** @@ -98,7 +112,7 @@ to_string(Currency const& c); * to rewrite some unit test code. */ bool -toCurrency(Currency&, std::string const&); +toCurrency(Currency&, std::string_view); /** * Tries to convert a string to a Currency, returns noCurrency() on failure. @@ -108,7 +122,7 @@ toCurrency(Currency&, std::string const&); * everywhere and may mean having to rewrite some unit test code. */ Currency -toCurrency(std::string const&); +toCurrency(std::string_view); inline std::ostream& operator<<(std::ostream& os, Currency const& x) diff --git a/include/xrpl/protocol/digest.h b/include/xrpl/protocol/digest.h index 44fca3d1ea8..e80eec7d165 100644 --- a/include/xrpl/protocol/digest.h +++ b/include/xrpl/protocol/digest.h @@ -8,6 +8,7 @@ #include #include #include +#include #include namespace xrpl { @@ -182,7 +183,7 @@ struct BasicSha512HalfHasher operator result_type() noexcept { auto const digest = sha512_hasher::result_type(h_); - return result_type::fromVoid(digest.data()); + return result_type{std::span{digest}.first()}; } private: diff --git a/include/xrpl/protocol/jss.h b/include/xrpl/protocol/jss.h index b294a846da8..1004124a180 100644 --- a/include/xrpl/protocol/jss.h +++ b/include/xrpl/protocol/jss.h @@ -392,6 +392,7 @@ JSS(max_ledger); // in/out: LedgerCleaner JSS(max_queue_size); // out: TxQ JSS(max_spend_drops); // out: AccountInfo JSS(max_spend_drops_total); // out: AccountInfo +JSS(maximum); JSS(mean); // out: get_aggregate_price JSS(median); // out: get_aggregate_price JSS(median_fee); // out: TxQ diff --git a/include/xrpl/protocol/nft.h b/include/xrpl/protocol/nft.h index 54227dd1eaa..ea0a0adbf55 100644 --- a/include/xrpl/protocol/nft.h +++ b/include/xrpl/protocol/nft.h @@ -8,6 +8,7 @@ #include #include +#include namespace xrpl::nft { @@ -98,7 +99,8 @@ getTaxon(uint256 const& id) inline AccountID getIssuer(uint256 const& id) { - return AccountID::fromVoid(id.data() + 4); + return AccountID{ + std::span{id}.subspan<4, AccountID::size()>()}; } } // namespace xrpl::nft diff --git a/include/xrpl/protocol/nftPageMask.h b/include/xrpl/protocol/nftPageMask.h index 827df34cf31..69757110ddb 100644 --- a/include/xrpl/protocol/nftPageMask.h +++ b/include/xrpl/protocol/nftPageMask.h @@ -2,13 +2,10 @@ #include -#include - namespace xrpl::nft { // NFT directory pages order their contents based only on the low 96 bits of // the NFToken value. This mask provides easy access to the necessary mask. -constexpr uint256 kPageMask( - std::string_view("0000000000000000000000000000000000000000ffffffffffffffffffffffff")); +constexpr uint256 kPageMask{"0000000000000000000000000000000000000000ffffffffffffffffffffffff"}; } // namespace xrpl::nft diff --git a/include/xrpl/shamap/SHAMapItem.h b/include/xrpl/shamap/SHAMapItem.h index d45d75a9429..ee1720ad5c3 100644 --- a/include/xrpl/shamap/SHAMapItem.h +++ b/include/xrpl/shamap/SHAMapItem.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include @@ -15,12 +14,13 @@ #include #include #include +#include #include namespace xrpl { // an item stored in a SHAMap -class SHAMapItem : public CountedObject +class alignas(8) SHAMapItem : public CountedObject { // These are used to support boost::intrusive_ptr reference counting // These functions are used internally by boost::intrusive_ptr to handle @@ -101,15 +101,15 @@ namespace detail { // The slab cutoffs and the number of megabytes per allocation are customized // based on the number of objects of each size we expect to need at any point // in time and with an eye to minimize the number of slack bytes in a block. -inline SlabAllocatorSet gSlabber({ - { 128, megabytes(std::size_t(60)) }, - { 192, megabytes(std::size_t(46)) }, - { 272, megabytes(std::size_t(60)) }, - { 384, megabytes(std::size_t(56)) }, - { 564, megabytes(std::size_t(40)) }, - { 772, megabytes(std::size_t(46)) }, - { 1052, megabytes(std::size_t(60)) }, -}); +inline constinit slab::Allocator, + slab::Config<1000000, 296>, + slab::Config<125000, 392>, + slab::Config<125000, 520>, + slab::Config<62500, 760>, + slab::Config<62500, 856>, + slab::Config<31250, 1048> +> gSlabber; // clang-format on } // namespace detail @@ -150,23 +150,13 @@ makeShamapitem(uint256 const& tag, Slice data) XRPL_ASSERT( data.size() <= megabytes(16), "xrpl::makeShamapitem : maximum input size"); - // NOLINTNEXTLINE(misc-const-correctness) - std::uint8_t* raw = detail::gSlabber.allocate(data.size()); - - // If we can't grab memory from the slab allocators, we fall back to - // the standard library and try to grab a precisely-sized memory block: - if (raw == nullptr) - raw = new std::uint8_t[sizeof(SHAMapItem) + data.size()]; - // We do not increment the reference count here on purpose: the - // constructor of SHAMapItem explicitly sets it to 1. We use the fact - // that the refcount can never be zero before incrementing as an - // invariant. - return {new (raw) SHAMapItem{tag, data}, false}; -} + // constructor of SHAMapItem explicitly sets it to 1. + if (auto raw = detail::gSlabber.allocate(data.size())) [[likely]] + return {new (raw) SHAMapItem{tag, data}, false}; -static_assert(alignof(SHAMapItem) != 40); -static_assert(alignof(SHAMapItem) == 8 || alignof(SHAMapItem) == 4); + Throw(); +} inline boost::intrusive_ptr makeShamapitem(SHAMapItem const& other) diff --git a/src/libxrpl/basics/CountedObject.cpp b/src/libxrpl/basics/CountedObject.cpp deleted file mode 100644 index f5b98354dca..00000000000 --- a/src/libxrpl/basics/CountedObject.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include - -#include - -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 diff --git a/src/libxrpl/ledger/BookDirs.cpp b/src/libxrpl/ledger/BookDirs.cpp index 099fec99e1b..18fc90330b0 100644 --- a/src/libxrpl/ledger/BookDirs.cpp +++ b/src/libxrpl/ledger/BookDirs.cpp @@ -80,10 +80,10 @@ BookDirs::const_iterator::operator++() XRPL_ASSERT(index_ != kZero, "xrpl::BookDirs::const_iterator::operator++ : nonzero index"); if (!cdirNext(*view_, curKey_, sle_, entry_, index_)) { - if (index_ == 0) + if (index_ == kZero) curKey_ = view_->succ(++curKey_, nextQuality_).value_or(kZero); - if (index_ != 0 || curKey_ == kZero) + if (index_ != kZero || curKey_ == kZero) { curKey_ = key_; entry_ = 0; diff --git a/src/libxrpl/ledger/Ledger.cpp b/src/libxrpl/ledger/Ledger.cpp index 188fb3cd2c5..a0b2de6336e 100644 --- a/src/libxrpl/ledger/Ledger.cpp +++ b/src/libxrpl/ledger/Ledger.cpp @@ -261,7 +261,7 @@ Ledger::Ledger(Ledger const& prevLedger, NetClock::time_point closeTime) { header_.seq = prevLedger.header_.seq + 1; header_.parentCloseTime = prevLedger.header_.closeTime; - header_.hash = prevLedger.header().hash + uint256(1); + header_.hash = prevLedger.header().hash.next(); header_.drops = prevLedger.header().drops; header_.closeTimeResolution = prevLedger.header_.closeTimeResolution; header_.parentHash = prevLedger.header().hash; diff --git a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp index 819ebb04d17..ff3cc10cd06 100644 --- a/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp +++ b/src/libxrpl/ledger/helpers/AccountRootHelpers.cpp @@ -506,7 +506,7 @@ pseudoAccountAddress(ReadView const& view, uint256 const& pseudoOwnerKey) RipeshaHasher rsh; auto const hash = sha512Half(i, view.header().parentHash, pseudoOwnerKey); rsh(hash.data(), hash.size()); - AccountID const ret = AccountID::fromRaw(static_cast(rsh)); + AccountID const ret{static_cast(rsh)}; if (!view.read(keylet::account(ret))) return ret; } diff --git a/src/libxrpl/nodestore/DecodedBlob.cpp b/src/libxrpl/nodestore/DecodedBlob.cpp index 321089d40f3..83976054c57 100644 --- a/src/libxrpl/nodestore/DecodedBlob.cpp +++ b/src/libxrpl/nodestore/DecodedBlob.cpp @@ -1,7 +1,6 @@ #include #include -#include #include #include #include @@ -12,8 +11,13 @@ namespace xrpl::node_store { -DecodedBlob::DecodedBlob(void const* key, void const* value, int valueBytes) : key_(key) +DecodedBlob::DecodedBlob(std::optional key, void const* value, int valueBytes) { + if (!key) + return; + + key_ = key.value(); + /* Data format: Bytes @@ -63,7 +67,7 @@ DecodedBlob::createObject() { 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; diff --git a/src/libxrpl/nodestore/backend/NuDBFactory.cpp b/src/libxrpl/nodestore/backend/NuDBFactory.cpp index 98173858e86..de06c6bdfaa 100644 --- a/src/libxrpl/nodestore/backend/NuDBFactory.cpp +++ b/src/libxrpl/nodestore/backend/NuDBFactory.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -216,7 +217,7 @@ class NuDBBackend : public Backend [&hash, pno, &status](void const* data, std::size_t size) { nudb::detail::buffer 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()) { status = Status::DataCorrupt; @@ -240,7 +241,7 @@ class NuDBBackend : public Backend nudb::error_code ec; nudb::detail::buffer 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) Throw(ec); } @@ -288,14 +289,25 @@ class NuDBBackend : public Backend Throw(ec); nudb::visit( dp, - [&](void const* key, + [&](void const* keyData, std::size_t keyBytes, void const* data, std::size_t size, nudb::error_code&) { + auto key = uint256::fromRaw( + std::span{static_cast(keyData), keyBytes}); + + if (!key) [[unlikely]] + { + ec = make_error_code(nudb::error::invalid_key_size); + return; + } + nudb::detail::buffer bf; auto const result = nodeobjectDecompress(data, size, bf); + DecodedBlob decoded(key, result.first, result.second); + if (!decoded.wasOk()) { ec = make_error_code(nudb::error::missing_value); diff --git a/src/libxrpl/nodestore/backend/RocksDBFactory.cpp b/src/libxrpl/nodestore/backend/RocksDBFactory.cpp index 6f00b762b27..457de6f9447 100644 --- a/src/libxrpl/nodestore/backend/RocksDBFactory.cpp +++ b/src/libxrpl/nodestore/backend/RocksDBFactory.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -110,6 +111,9 @@ class RocksDBBackend : public Backend, public BatchWriter::Callback if (!getIfExists(keyValues, Keys::kPath, name)) Throw("Missing path in RocksDBFactory backend"); + if (keyBytes != uint256::size()) + Throw("Incorrect key size: expected 32 bytes"); + rocksdb::BlockBasedTableOptions tableOptions; options.env = env; @@ -291,7 +295,7 @@ class RocksDBBackend : public Backend, public BatchWriter::Callback if (getStatus.ok()) { - DecodedBlob decoded(hash.data(), string.data(), string.size()); + DecodedBlob decoded(hash, string.data(), string.size()); if (decoded.wasOk()) { @@ -316,8 +320,8 @@ class RocksDBBackend : public Backend, public BatchWriter::Callback } else { - status = static_cast( - static_cast(Status::CustomCode) + unsafeCast(getStatus.code())); + status = checkedCast( + safeCast(Status::CustomCode) + safeCast(getStatus.code())); JLOG(journal.error()) << getStatus.ToString(); } @@ -346,7 +350,7 @@ class RocksDBBackend : public Backend, public BatchWriter::Callback EncodedBlob const encoded(e); wb.Put( - rocksdb::Slice(reinterpret_cast(encoded.getKey()), keyBytes), + rocksdb::Slice(reinterpret_cast(encoded.getKey().data()), keyBytes), rocksdb::Slice( reinterpret_cast(encoded.getData()), encoded.getSize())); } @@ -374,9 +378,12 @@ class RocksDBBackend : public Backend, public BatchWriter::Callback 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()) { diff --git a/src/libxrpl/protocol/AccountID.cpp b/src/libxrpl/protocol/AccountID.cpp index c6a5226566b..855d1cf94c0 100644 --- a/src/libxrpl/protocol/AccountID.cpp +++ b/src/libxrpl/protocol/AccountID.cpp @@ -8,6 +8,8 @@ #include #include +#include +#include #include #include #include @@ -19,95 +21,143 @@ namespace xrpl { -namespace detail { +namespace { -/** - * Caches the base58 representations of AccountIDs +/** If true, we implement a cache for account ID lookups. + + This is generally a good idea and can improve performance, especially + when serving client requests. + + The cache may generate warnings under some sanitizers; if you want to + disable caching, set this variable to false. + */ +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. */ -class AccountIdCache +struct alignas(64) CachedAccountID { -private: - struct CachedAccountID - { - AccountID id; - char encoding[40] = {0}; - }; + /** The sequence lock for the entry. - // The actual cache - std::vector cache_; + An even value means that the data is stable, whereas an odd + value indicates the data is being actively modified. - // We use a hash function designed to resist algorithmic complexity attacks - HardenedHash<> hasher_; + We use a 32-bit value to ensure that the counter will never + return to an observed value during a read window. + */ + std::atomic seq{0}; - // 64 spinlocks, packed into a single 64-bit value - std::atomic locks_ = 0; + /** The account id. */ + AccountID id{}; -public: - AccountIdCache(std::size_t count) : cache_(count) - { - // This is non-binding, but we try to avoid wasting memory that - // is caused by overallocation. - cache_.shrink_to_fit(); - } + /** The length of the cached encoding. - std::string - toBase58(AccountID const& id) - { - auto const index = hasher_(id) % cache_.size(); + This field disambiguates collisions between ACCOUNT_ZERO and + never-written-to slots, since they would otherwise share the + same encoded bits. + */ + std::uint8_t len = 0; - PackedSpinlock sl(locks_, index % 64); + /** The actual string encoding. */ + char encoding[39] = {0}; +}; - { - std::scoped_lock const lock(sl); +static_assert(sizeof(CachedAccountID) == 64 && alignof(CachedAccountID) == 64); - // 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; - } +// 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 gAccountIdCache{}; - auto ret = encodeBase58Token(TokenType::AccountID, id.data(), id.size()); +} // namespace - XRPL_ASSERT(ret.size() <= 38, "xrpl::detail::AccountIdCache : maximum result size"); +std::string +toBase58(AccountID const& v) +{ + // Since the input bits are generated by a cryptographically secure + // hash construct, we can just pick any subset of them, no muss, no + // fuss. + auto const index = [&v]() -> std::uint16_t { + auto const data = v.data(); + + return static_cast(data[0]) | (static_cast(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) { - std::scoped_lock const lock(sl); - cache_[index].id = id; - std::strcpy(cache_[index].encoding, ret.c_str()); + char buf[38]; + auto const len = std::min(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); } - - return ret; } -}; -} // namespace detail + auto ret = encodeBase58Token(TokenType::AccountID, v.data(), v.size()); -static std::unique_ptr gAccountIdCache; + XRPL_ASSERT(!ret.empty() && ret.size() <= 38, "xrpl::toBase58 : maximum result size"); -void -initAccountIdCache(std::size_t count) -{ - if (!gAccountIdCache && count != 0) - gAccountIdCache = std::make_unique(count); -} + if constexpr (cacheAccountIDs) + { + auto& slot = gAccountIdCache[index]; -std::string -toBase58(AccountID const& v) -{ - if (gAccountIdCache) - return gAccountIdCache->toBase58(v); + // 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(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 encodeBase58Token(TokenType::AccountID, v.data(), v.size()); + return ret; } template <> std::optional parseBase58(std::string const& s) { - auto const result = decodeBase58Token(s, TokenType::AccountID); - if (result.size() != AccountID::kBytes) - return std::nullopt; - return AccountID::fromRaw(result); + return AccountID::fromRaw(decodeBase58Token(s, TokenType::AccountID)); } //------------------------------------------------------------------------------ @@ -148,25 +198,9 @@ parseBase58(std::string const& s) AccountID calcAccountID(PublicKey const& pk) { - static_assert(AccountID::kBytes == sizeof(RipeshaHasher::result_type)); - RipeshaHasher rsh; rsh(pk.data(), pk.size()); - return AccountID::fromRaw(static_cast(rsh)); -} - -AccountID const& -xrpAccount() -{ - static AccountID const kAccount(beast::kZero); - return kAccount; -} - -AccountID const& -noAccount() -{ - static AccountID const kAccount(1); - return kAccount; + return AccountID{static_cast(rsh)}; } bool diff --git a/src/libxrpl/protocol/Indexes.cpp b/src/libxrpl/protocol/Indexes.cpp index 91ed5c893fd..6f414997863 100644 --- a/src/libxrpl/protocol/Indexes.cpp +++ b/src/libxrpl/protocol/Indexes.cpp @@ -184,8 +184,9 @@ getBookBase(Book const& book) uint256 getQualityNext(uint256 const& uBase) { - static constexpr uint256 kNextQuality( - "0000000000000000000000000000000000000000000000010000000000000000"); + static constexpr uint256 kNextQuality{ + "0000000000000000000000000000000000000000000000010000000000000000"}; + return uBase + kNextQuality; } @@ -420,9 +421,9 @@ payChannel(AccountID const& src, AccountID const& dst, SeqProxy const& seq) noex Keylet nftokenPageMin(AccountID const& owner) { - std::array buf{}; - std::memcpy(buf.data(), owner.data(), owner.size()); - return {ltNFTOKEN_PAGE, uint256::fromRaw(buf)}; + std::array buf{}; + std::ranges::copy(owner, buf.begin()); + return {ltNFTOKEN_PAGE, uint256{buf}}; } Keylet diff --git a/src/libxrpl/protocol/PublicKey.cpp b/src/libxrpl/protocol/PublicKey.cpp index cb6ea9e8516..746526b5945 100644 --- a/src/libxrpl/protocol/PublicKey.cpp +++ b/src/libxrpl/protocol/PublicKey.cpp @@ -295,11 +295,9 @@ verify(PublicKey const& publicKey, Slice const& m, Slice const& sig) noexcept NodeID calcNodeID(PublicKey const& pk) { - static_assert(NodeID::kBytes == sizeof(RipeshaHasher::result_type)); - RipeshaHasher h; h(pk.data(), pk.size()); - return NodeID::fromRaw(static_cast(h)); + return NodeID{static_cast(h)}; } } // namespace xrpl diff --git a/src/libxrpl/protocol/STAmount.cpp b/src/libxrpl/protocol/STAmount.cpp index 83b29837567..48a001427ab 100644 --- a/src/libxrpl/protocol/STAmount.cpp +++ b/src/libxrpl/protocol/STAmount.cpp @@ -147,12 +147,12 @@ STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name) } Issue issue; - issue.currency = sit.get160(); + issue.currency = Currency{sit.get160()}; if (isXRP(issue.currency)) Throw("invalid native currency"); - issue.account = sit.get160(); + issue.account = AccountID{sit.get160()}; if (isXRP(issue.account)) Throw("invalid native account"); diff --git a/src/libxrpl/protocol/STCurrency.cpp b/src/libxrpl/protocol/STCurrency.cpp index fc8bd756edf..67075f424e5 100644 --- a/src/libxrpl/protocol/STCurrency.cpp +++ b/src/libxrpl/protocol/STCurrency.cpp @@ -19,13 +19,13 @@ STCurrency::STCurrency(SField const& name) : STBase{name} { } -STCurrency::STCurrency(SerialIter& sit, SField const& name) : STBase{name} +STCurrency::STCurrency(SField const& name, Currency const& currency) + : STBase{name}, currency_{currency} { - currency_ = sit.get160(); } -STCurrency::STCurrency(SField const& name, Currency const& currency) - : STBase{name}, currency_{currency} +STCurrency::STCurrency(SerialIter& sit, SField const& name) + : STCurrency{name, Currency{sit.get160()}} { } diff --git a/src/libxrpl/protocol/STIssue.cpp b/src/libxrpl/protocol/STIssue.cpp index ba32c1214c8..74b578bb135 100644 --- a/src/libxrpl/protocol/STIssue.cpp +++ b/src/libxrpl/protocol/STIssue.cpp @@ -28,48 +28,38 @@ STIssue::STIssue(SField const& name) : STBase{name} STIssue::STIssue(SerialIter& sit, SField const& name) : STBase{name} { + // Either the currency of an Issue or the issuer of an MPT; the + // following 160 bits disambiguate. auto const currencyOrAccount = sit.get160(); - if (isXRP(Currency::fromRaw(currencyOrAccount))) + if (isXRP(Currency{currencyOrAccount})) { 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: - // - 160 bits MPT issuer account - // - 160 bits black hole account - // - 32 bits sequence - AccountID const account = AccountID::fromRaw(sit.get160()); - // MPT - if (noAccount() == account) - { - MPTID mptID; - std::uint32_t sequence = sit.get32(); - // MPTID stores the sequence in canonical big-endian bytes. STIssue - // ledger bytes are the legacy LE-host encoding, so convert the - // native get32() value to LE bytes before copying into the MPTID. - sequence = boost::endian::native_to_little(sequence); - static_assert(MPTID::size() == sizeof(sequence) + sizeof(currencyOrAccount)); - memcpy(mptID.data(), &sequence, sizeof(sequence)); - memcpy( - mptID.data() + sizeof(sequence), - currencyOrAccount.data(), - sizeof(currencyOrAccount)); - MPTIssue const issue{mptID}; - asset_ = issue; - } - else - { - Issue issue; - issue.currency = currencyOrAccount; - issue.account = account; - if (!isConsistent(issue)) - Throw("invalid issue: currency and account native mismatch"); - asset_ = issue; - } + // MPT: issuer (160), noAccount() (160), sequence (32). The sequence + // is stored in little-endian and get32() reads big-endian, so + // reverse it before building the canonical ID. + asset_ = MPTIssue{std::byteswap(sit.get32()), AccountID{currencyOrAccount}}; + return; } + + Issue const issue{Currency{currencyOrAccount}, account}; + + if (!isConsistent(issue)) + Throw("invalid issue: currency and account native mismatch"); + + asset_ = issue; } SerializedTypeID diff --git a/src/libxrpl/protocol/STPathSet.cpp b/src/libxrpl/protocol/STPathSet.cpp index 2c074c3f2fc..7e439b96da4 100644 --- a/src/libxrpl/protocol/STPathSet.cpp +++ b/src/libxrpl/protocol/STPathSet.cpp @@ -144,7 +144,7 @@ STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name) AccountID issuer; if (hasAccount) - account = sit.get160(); + account = AccountID{sit.get160()}; if (hasCurrency && hasMPT) { @@ -153,13 +153,13 @@ STPathSet::STPathSet(SerialIter& sit, SField const& name) : STBase(name) } if (hasCurrency) - asset = Currency::fromRaw(sit.get160()); + asset = Currency{sit.get160()}; if (hasMPT) asset = sit.get192(); if (hasIssuer) - issuer = sit.get160(); + issuer = AccountID{sit.get160()}; path.emplace_back(account, asset, issuer, hasCurrency || hasMPT); } diff --git a/src/libxrpl/protocol/STVector256.cpp b/src/libxrpl/protocol/STVector256.cpp index 877e34c73c8..ceea620efe4 100644 --- a/src/libxrpl/protocol/STVector256.cpp +++ b/src/libxrpl/protocol/STVector256.cpp @@ -9,7 +9,9 @@ #include #include +#include #include +#include #include #include @@ -17,7 +19,7 @@ namespace xrpl { 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) { @@ -25,12 +27,13 @@ STVector256::STVector256(SerialIter& sit, SField const& name) : STBase(name) "Bad serialization for STVector256: " + std::to_string(slice.size())); } - auto const cnt = slice.size() / uint256::size(); + value_.reserve(slice.size() / uint256::size()); - value_.reserve(cnt); - - for (std::size_t i = 0; i != cnt; ++i) - value_.push_back(uint256::fromRaw(slice.substr(i * uint256::size(), uint256::size()))); + while (!slice.empty()) + { + value_.emplace_back(std::span{slice.data(), uint256::size()}); + slice += uint256::size(); + } } STBase* diff --git a/src/libxrpl/protocol/Seed.cpp b/src/libxrpl/protocol/Seed.cpp index 42b2e854980..1bf8f801059 100644 --- a/src/libxrpl/protocol/Seed.cpp +++ b/src/libxrpl/protocol/Seed.cpp @@ -104,8 +104,9 @@ parseGenericSeed(std::string const& str, bool rfc1751) std::string key; if (RFC1751::getKeyFromEnglish(key, str) == 1) { - Blob const blob(key.rbegin(), key.rend()); - return Seed{uint128::fromRaw(blob)}; + std::array bytes{}; + std::ranges::reverse_copy(key, bytes.begin()); + return Seed{uint128{bytes}}; } } diff --git a/src/libxrpl/protocol/UintTypes.cpp b/src/libxrpl/protocol/UintTypes.cpp index e1b8895f449..30c1e4ca4fb 100644 --- a/src/libxrpl/protocol/UintTypes.cpp +++ b/src/libxrpl/protocol/UintTypes.cpp @@ -35,7 +35,7 @@ constexpr std::size_t kIsoCodeLength = 3; std::string to_string(Currency const& currency) { - if (currency == beast::kZero) + if (currency == xrpCurrency()) return systemCurrencyCode(); if (currency == noCurrency()) @@ -62,7 +62,7 @@ to_string(Currency const& currency) } bool -toCurrency(Currency& currency, std::string const& code) +toCurrency(Currency& currency, std::string_view code) { if (code.empty() || code == systemCurrencyCode()) { @@ -87,7 +87,7 @@ toCurrency(Currency& currency, std::string const& code) } Currency -toCurrency(std::string const& code) +toCurrency(std::string_view code) { Currency currency; if (!toCurrency(currency, code)) @@ -95,25 +95,4 @@ toCurrency(std::string const& code) return currency; } -Currency const& -xrpCurrency() -{ - static Currency const kCurrency(beast::kZero); - return kCurrency; -} - -Currency const& -noCurrency() -{ - static Currency const kCurrency(1); - return kCurrency; -} - -Currency const& -badCurrency() -{ - static Currency const kCurrency(0x5852500000000000); - return kCurrency; -} - } // namespace xrpl diff --git a/src/libxrpl/shamap/SHAMapNodeID.cpp b/src/libxrpl/shamap/SHAMapNodeID.cpp index 42b946b9210..5be8cf8fa39 100644 --- a/src/libxrpl/shamap/SHAMapNodeID.cpp +++ b/src/libxrpl/shamap/SHAMapNodeID.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -128,14 +129,15 @@ deserializeSHAMapNodeID(void const* data, std::size_t size) { std::optional ret; - if (size == 33) + if (size == uint256::size() + 1) { - unsigned int const depth = *(static_cast(data) + 32); - if (depth <= SHAMap::kLeafDepth) + std::span const bytes{static_cast(data), size}; + + if (unsigned int const depth = bytes[uint256::size()]; depth <= SHAMap::kLeafDepth) { // Reject a serialized ID carrying bits below its own depth. Checked before - // constructing, since the constructor asserts that same property. - if (auto const id = uint256::fromVoid(data); isPrefixOfAtDepth(id, depth, id)) + // constructing the node, since the constructor asserts that same property. + if (uint256 const id{bytes.first()}; isPrefixOfAtDepth(id, depth, id)) ret.emplace(depth, id); } } diff --git a/src/libxrpl/tx/transactors/dex/AMMVote.cpp b/src/libxrpl/tx/transactors/dex/AMMVote.cpp index 0f2b721ac43..a0fe19930fc 100644 --- a/src/libxrpl/tx/transactors/dex/AMMVote.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMVote.cpp @@ -89,7 +89,7 @@ applyVote(ApplyContext& ctx, Sandbox& sb, AccountID const& accountID, beast::Jou auto const lpTokensNew = ammLPHolds(sb, *ammSle, accountID, ctx.journal); std::optional minTokens; std::size_t minPos{0}; - AccountID minAccount{0}; + AccountID minAccount; std::uint32_t minFee{0}; STArray updatedVoteSlots; Number num{0}; diff --git a/src/libxrpl/tx/transactors/lending/LoanSet.cpp b/src/libxrpl/tx/transactors/lending/LoanSet.cpp index da2eb609d80..a9cff4c7797 100644 --- a/src/libxrpl/tx/transactors/lending/LoanSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanSet.cpp @@ -75,7 +75,7 @@ LoanSet::preflight(PreflightContext const& ctx) if (tx.isFlag(tfInnerBatchTxn) && ctx.rules.enabled(featureBatchV1_1) && !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 << "]: " << "no Counterparty for inner LoanSet transaction."; return temBAD_SIGNER; diff --git a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp index 5490c2de3a1..d6387922997 100644 --- a/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp +++ b/src/libxrpl/tx/transactors/nft/NFTokenMint.cpp @@ -176,7 +176,7 @@ NFTokenMint::createNFTokenID( std::distance(buf.data(), ptr) == buf.size(), "xrpl::NFTokenMint::createNFTokenID : data size matches the buffer"); - return uint256::fromVoid(buf.data()); + return uint256{buf}; } TER diff --git a/src/test/app/AMMMPT_test.cpp b/src/test/app/AMMMPT_test.cpp index 37e0ed585dd..7c855d6ca7f 100644 --- a/src/test/app/AMMMPT_test.cpp +++ b/src/test/app/AMMMPT_test.cpp @@ -461,8 +461,8 @@ struct AMMMPT_test : public jtx::AMMTest // Invalid tokens { - auto const mpt1 = MPTIssue{MPTID(0xabc)}; - auto const mpt2 = MPTIssue{MPTID(0xdef)}; + auto const mpt1 = MPTIssue{MPTID{0xabc}}; + auto const mpt2 = MPTIssue{MPTID{0xdef}}; ammAlice.deposit( alice_, 1'000, diff --git a/src/test/app/AMM_test.cpp b/src/test/app/AMM_test.cpp index 0212035c6e4..c5891865888 100644 --- a/src/test/app/AMM_test.cpp +++ b/src/test/app/AMM_test.cpp @@ -602,8 +602,8 @@ struct AMM_test : public jtx::AMMTest // Invalid tokens - bogus currency { - auto const iss1 = Issue{Currency(0xabc), gw_.id()}; - auto const iss2 = Issue{Currency(0xdef), gw_.id()}; + auto const iss1 = Issue{Currency{0xabc}, gw_.id()}; + auto const iss2 = Issue{Currency{0xdef}, gw_.id()}; ammAlice.deposit( alice_, 1'000, diff --git a/src/test/app/Batch_test.cpp b/src/test/app/Batch_test.cpp index 7e6ecfb8cae..e83b4c80474 100644 --- a/src/test/app/Batch_test.cpp +++ b/src/test/app/Batch_test.cpp @@ -462,7 +462,7 @@ class Batch_test : public beast::unit_test::Suite { auto jtx = env.jt(pay(alice, bob, XRP(1))); PreflightContext const pfCtx( - env.app(), *jtx.stx, uint256{1}, env.current()->rules(), TapBatch, env.journal); + env.app(), *jtx.stx, uint256{1}, env.current()->rules(), TapBatch, env.journal); auto const pf = Transactor::invokePreflight(pfCtx); BEAST_EXPECT(pf == temINVALID_INNER_BATCH); } @@ -4381,7 +4381,7 @@ class Batch_test : public beast::unit_test::Suite STTx const stx = STTx(ttAMENDMENT, [&](auto& obj) { obj.setAccountID(sfAccount, AccountID()); - obj.setFieldH256(sfAmendment, uint256(2)); + obj.setFieldH256(sfAmendment, uint256{2}); obj.setFieldU32(sfLedgerSequence, env.seq(alice)); obj.setFieldU32(sfFlags, tfInnerBatchTxn); }); diff --git a/src/test/app/ConfidentialTransferExtended_test.cpp b/src/test/app/ConfidentialTransferExtended_test.cpp index fe5e0b3064e..c7334b8bd70 100644 --- a/src/test/app/ConfidentialTransferExtended_test.cpp +++ b/src/test/app/ConfidentialTransferExtended_test.cpp @@ -431,16 +431,16 @@ class ConfidentialTransferExtended_test : public ConfidentialTransferTestBase {.account = carol, .payAmount = 100, .convertAmount = 50}}}; auto& mpt = confEnv.mpt; - std::vector tooManyCredentials; - tooManyCredentials.reserve(9); - for (int i = 0; i < 9; ++i) - tooManyCredentials.push_back(to_string(uint256(i))); - mpt.send({ .account = carol, .dest = bob, .amt = 10, - .credentials = tooManyCredentials, + .credentials = [] { + std::vector v; + for (int i = 0; i < 9; ++i) + v.push_back(std::string(63, '0') + static_cast('0' + i)); + return v; + }(), .err = temMALFORMED, }); } @@ -484,7 +484,7 @@ class ConfidentialTransferExtended_test : public ConfidentialTransferTestBase {.account = carol, .payAmount = 100, .convertAmount = 50}}}; auto& mpt = confEnv.mpt; - std::string const fakeCredIdx = to_string(uint256(999)); + std::string const fakeCredIdx = to_string(uint256{999}); mpt.send({ .account = carol, .dest = bob, diff --git a/src/test/app/Delegate_test.cpp b/src/test/app/Delegate_test.cpp index 5f57cfa21b0..ed990db9d10 100644 --- a/src/test/app/Delegate_test.cpp +++ b/src/test/app/Delegate_test.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include diff --git a/src/test/app/EscrowToken_test.cpp b/src/test/app/EscrowToken_test.cpp index 3a2bc141831..149763a2f9e 100644 --- a/src/test/app/EscrowToken_test.cpp +++ b/src/test/app/EscrowToken_test.cpp @@ -2798,7 +2798,7 @@ struct EscrowToken_test : public beast::unit_test::Suite Sandbox sb(&view, TapNone); auto sleNew = std::make_shared(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); sleNew->setAccountID(sfDestination, bob); sleNew->setFieldAmount(sfAmount, amt); @@ -3026,7 +3026,7 @@ struct EscrowToken_test : public beast::unit_test::Suite Sandbox sb(&view, TapNone); auto sleNew = std::make_shared(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); sleNew->setAccountID(sfDestination, bob); sleNew->setFieldAmount(sfAmount, amt); diff --git a/src/test/app/FeeVote_test.cpp b/src/test/app/FeeVote_test.cpp index 666dec1249a..353733ef957 100644 --- a/src/test/app/FeeVote_test.cpp +++ b/src/test/app/FeeVote_test.cpp @@ -559,7 +559,7 @@ class FeeVote_test : public beast::unit_test::Suite // validation auto invalidTx = STTx(ttFEE, [&](auto& obj) { 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.setFieldAmount(sfBaseFeeDrops, XRPAmount{10}); obj.setFieldAmount(sfReserveBaseDrops, XRPAmount{200000}); diff --git a/src/test/app/HashRouter_test.cpp b/src/test/app/HashRouter_test.cpp index 0266da2bc08..e3dd43b6178 100644 --- a/src/test/app/HashRouter_test.cpp +++ b/src/test/app/HashRouter_test.cpp @@ -6,6 +6,7 @@ #include #include + #include #include #include @@ -38,9 +39,9 @@ class HashRouter_test : public beast::unit_test::Suite HashRouterFlags const key2(HashRouterFlags::PRIVATE2); HashRouterFlags const key3(HashRouterFlags::PRIVATE3); - auto const ukey1 = uint256{static_cast(key1)}; - auto const ukey2 = uint256{static_cast(key2)}; - auto const ukey3 = uint256{static_cast(key3)}; + uint256 const ukey1{std::to_underlying(key1)}; + uint256 const ukey2{std::to_underlying(key2)}; + uint256 const ukey3{std::to_underlying(key3)}; // t=0 router.setFlags(ukey1, HashRouterFlags::PRIVATE1); @@ -81,10 +82,10 @@ class HashRouter_test : public beast::unit_test::Suite HashRouterFlags const key3(HashRouterFlags::PRIVATE3); HashRouterFlags const key4(HashRouterFlags::PRIVATE4); - auto const ukey1 = uint256{static_cast(key1)}; - auto const ukey2 = uint256{static_cast(key2)}; - auto const ukey3 = uint256{static_cast(key3)}; - auto const ukey4 = uint256{static_cast(key4)}; + uint256 const ukey1{std::to_underlying(key1)}; + uint256 const ukey2{std::to_underlying(key2)}; + uint256 const ukey3{std::to_underlying(key3)}; + uint256 const ukey4{std::to_underlying(key4)}; BEAST_EXPECT(key1 != key2 && key2 != key3 && key3 != key4); @@ -162,10 +163,11 @@ class HashRouter_test : public beast::unit_test::Suite TestStopwatch stopwatch; HashRouter router(getSetup(2s, 1s), stopwatch); - uint256 const key1(1); - uint256 const key2(2); - uint256 const key3(3); - uint256 const key4(4); + uint256 const key1{1}; + uint256 const key2{2}; + uint256 const key3{3}; + uint256 const key4{4}; + BEAST_EXPECT(key1 != key2 && key2 != key3 && key3 != key4); HashRouterFlags flags(HashRouterFlags::BAD); // This value is ignored @@ -191,7 +193,7 @@ class HashRouter_test : public beast::unit_test::Suite TestStopwatch stopwatch; HashRouter router(getSetup(2s, 1s), stopwatch); - uint256 const key1(1); + uint256 const key1{1}; BEAST_EXPECT(router.setFlags(key1, HashRouterFlags::PRIVATE1)); BEAST_EXPECT(!router.setFlags(key1, HashRouterFlags::PRIVATE1)); BEAST_EXPECT(router.setFlags(key1, HashRouterFlags::PRIVATE2)); @@ -205,7 +207,7 @@ class HashRouter_test : public beast::unit_test::Suite TestStopwatch stopwatch; HashRouter router(getSetup(50s, 1s), stopwatch); - uint256 const key1(1); + uint256 const key1{1}; std::optional> peers; @@ -248,7 +250,7 @@ class HashRouter_test : public beast::unit_test::Suite using namespace std::chrono_literals; TestStopwatch stopwatch; HashRouter router(getSetup(5s, 1s), stopwatch); - uint256 const key(1); + uint256 const key{1}; HashRouter::PeerShortID const peer = 1; HashRouterFlags flags = HashRouterFlags::UNDEFINED; diff --git a/src/test/app/LedgerHistory_test.cpp b/src/test/app/LedgerHistory_test.cpp index f8688899e04..b662c8cf4bb 100644 --- a/src/test/app/LedgerHistory_test.cpp +++ b/src/test/app/LedgerHistory_test.cpp @@ -28,6 +28,8 @@ namespace xrpl::test { class LedgerHistory_test : public beast::unit_test::Suite { + uint256 const dummyTxHash_ = uint256{1}; + public: /** * Generate a new ledger by hand, applying a specific close time offset @@ -92,9 +94,8 @@ class LedgerHistory_test : public beast::unit_test::Suite Env env{*this, envconfig(), std::make_unique("MISMATCH ", &found)}; LedgerHistory lh{beast::insight::NullCollector::make(), env.app()}; auto const genesis = makeLedger({}, env, lh, 0s); - uint256 const dummyTxHash{1}; - lh.builtLedger(genesis, dummyTxHash, {}); - lh.validatedLedger(genesis, dummyTxHash); + lh.builtLedger(genesis, dummyTxHash_, {}); + lh.validatedLedger(genesis, dummyTxHash_); BEAST_EXPECT(!found); } @@ -111,9 +112,8 @@ class LedgerHistory_test : public beast::unit_test::Suite auto const ledgerA = makeLedger(genesis, env, lh, 4s); auto const ledgerB = makeLedger(genesis, env, lh, 40s); - uint256 const dummyTxHash{1}; - lh.builtLedger(ledgerA, dummyTxHash, {}); - lh.validatedLedger(ledgerB, dummyTxHash); + lh.builtLedger(ledgerA, dummyTxHash_, {}); + lh.validatedLedger(ledgerB, dummyTxHash_); BEAST_EXPECT(found); } @@ -132,9 +132,8 @@ class LedgerHistory_test : public beast::unit_test::Suite auto const ledgerAC = makeLedger(ledgerA, env, lh, 4s); auto const ledgerBD = makeLedger(ledgerB, env, lh, 4s); - uint256 const dummyTxHash{1}; - lh.builtLedger(ledgerAC, dummyTxHash, {}); - lh.validatedLedger(ledgerBD, dummyTxHash); + lh.builtLedger(ledgerAC, dummyTxHash_, {}); + lh.validatedLedger(ledgerBD, dummyTxHash_); BEAST_EXPECT(found); } diff --git a/src/test/app/LedgerReplay_test.cpp b/src/test/app/LedgerReplay_test.cpp index bb0c790e373..821cd06aceb 100644 --- a/src/test/app/LedgerReplay_test.cpp +++ b/src/test/app/LedgerReplay_test.cpp @@ -857,7 +857,7 @@ struct LedgerReplayer_test : public beast::unit_test::Suite auto request = std::make_shared(); request->set_type(protocol::TMLedgerMapType::lmACCOUNT_STATE); request->set_key(keylet::skip().key.data(), keylet::skip().key.size()); - uint256 hash(1234567); + uint256 const hash{1234567}; request->set_ledgerhash(hash.data(), hash.size()); auto reply = std::make_shared( server.msgHandler.processProofPathRequest(request)); @@ -874,7 +874,8 @@ struct LedgerReplayer_test : public beast::unit_test::Suite auto reply = std::make_shared( server.msgHandler.processProofPathRequest(request)); 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 @@ -966,7 +967,7 @@ struct LedgerReplayer_test : public beast::unit_test::Suite BEAST_EXPECT( server.msgHandler.processReplayDeltaResponse(reply) == ReplayMsgStatus::BadData); // request, wrong hash - uint256 hash(1234567); + uint256 const hash{1234567}; request->set_ledgerhash(hash.data(), hash.size()); reply = std::make_shared( server.msgHandler.processReplayDeltaRequest(request)); @@ -1095,24 +1096,27 @@ struct LedgerReplayer_test : public beast::unit_test::Suite { testcase("TaskParameter"); - auto makeSkipList = [](int count) -> std::vector { + auto const makeSkipList = [](std::size_t count) { std::vector sList; 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; }; - LedgerReplayTask::TaskParameter tp10(InboundLedger::Reason::GENERIC, uint256(10), 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), 10, makeSkipList(10))); + LedgerReplayTask::TaskParameter tp10(InboundLedger::Reason::GENERIC, uint256{10}, 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}, 10, makeSkipList(10))); // can merge to self BEAST_EXPECT(tp10.canMergeInto(tp10)); // 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(!tp10.canMergeInto(tp9)); @@ -1127,14 +1131,14 @@ struct LedgerReplayer_test : public beast::unit_test::Suite tp9.reason = InboundLedger::Reason::GENERIC; BEAST_EXPECT(tp9.canMergeInto(tp10)); - tp9.finishHash = uint256(1234); + tp9.finishHash = uint256{1234}; BEAST_EXPECT(!tp9.canMergeInto(tp10)); - tp9.finishHash = uint256(9); + tp9.finishHash = uint256{9}; BEAST_EXPECT(tp9.canMergeInto(tp10)); // larger task - LedgerReplayTask::TaskParameter tp20(InboundLedger::Reason::GENERIC, uint256(20), 20); - BEAST_EXPECT(tp20.update(uint256(20), 20, makeSkipList(20))); + LedgerReplayTask::TaskParameter tp20(InboundLedger::Reason::GENERIC, uint256{20}, 20); + BEAST_EXPECT(tp20.update(uint256{20}, 20, makeSkipList(20))); BEAST_EXPECT(tp10.canMergeInto(tp20)); BEAST_EXPECT(tp9.canMergeInto(tp20)); BEAST_EXPECT(!tp20.canMergeInto(tp10)); @@ -1368,7 +1372,7 @@ struct LedgerReplayer_test : public beast::unit_test::Suite auto skipList = net.client.findSkipListAcquire(finalHash); 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); std::vector const deltaStatuses; diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index 7086adf743f..2349bc684d6 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -122,7 +122,7 @@ class MPToken_test : public beast::unit_test::Suite .assetScale = 0, .metadata = "test", .flags = tfMPTRequireAuth, - .domainID = uint256(42), + .domainID{42}, .err = temDISABLED}); } else @@ -132,7 +132,7 @@ class MPToken_test : public beast::unit_test::Suite {.maxAmt = 100, .assetScale = 0, .metadata = "test", - .domainID = uint256(42), + .domainID{42}, .err = temMALFORMED}); // tries to set zero DomainID @@ -622,7 +622,7 @@ class MPToken_test : public beast::unit_test::Suite .err = tecNO_PERMISSION}); // 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 { @@ -635,7 +635,7 @@ class MPToken_test : public beast::unit_test::Suite if (!features[featurePermissionedDomains] || !features[featureSingleAssetVault]) { // 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]) { @@ -643,7 +643,7 @@ class MPToken_test : public beast::unit_test::Suite mptAlice.set( {.account = alice, .holder = bob, - .domainID = uint256(42), + .domainID{42}, .err = temMALFORMED}); } } @@ -725,7 +725,7 @@ class MPToken_test : public beast::unit_test::Suite mptAlice.create({}); // 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}); } @@ -737,11 +737,11 @@ class MPToken_test : public beast::unit_test::Suite mptAlice.create({.flags = tfMPTRequireAuth}); // 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 mptAlice.set( - {.flags = tfMPTUnlock, .domainID = uint256(42), .err = tecNO_PERMISSION}); + {.flags = tfMPTUnlock, .domainID{42}, .err = tecNO_PERMISSION}); mptAlice.set( {.flags = tfMPTUnlock, .domainID = uint256{}, .err = tecNO_PERMISSION}); @@ -1655,7 +1655,7 @@ class MPToken_test : public beast::unit_test::Suite env.fund(XRP(1'000), alice, bob); - STAmount const mpt{MPTID{0}, 100}; + STAmount const mpt{MPTID{}, 100}; auto const err = !features[featureMPTokensV2] ? Ter(tecOBJECT_NOT_FOUND) : Ter(temBAD_CURRENCY); env(pay(alice, bob, mpt), err); diff --git a/src/test/app/MultiSign_test.cpp b/src/test/app/MultiSign_test.cpp index 5f02f1b079f..42a326f81fd 100644 --- a/src/test/app/MultiSign_test.cpp +++ b/src/test/app/MultiSign_test.cpp @@ -1415,17 +1415,18 @@ class MultiSign_test : public beast::unit_test::Suite Account const alice{"alice", KeyType::Ed25519}; env.fund(XRP(1000), alice); 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. - 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.require(Owners(alice, 1)); diff --git a/src/test/app/OfferMPT_test.cpp b/src/test/app/OfferMPT_test.cpp index 80541480e88..4e2db709119 100644 --- a/src/test/app/OfferMPT_test.cpp +++ b/src/test/app/OfferMPT_test.cpp @@ -6168,16 +6168,10 @@ class OfferMPT_test : public beast::unit_test::Suite // 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 // (b) preserve the mixed-branch tag distinction that the public case has. - AccountID issuerX, issuerY, iouIssuer; - Currency currency; - BEAST_EXPECT(issuerX.parseHex("1111111111111111111111111111111111111111")); - BEAST_EXPECT(issuerY.parseHex("2222222222222222222222222222222222222222")); - 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"); + constexpr AccountID issuerX{1}; + constexpr AccountID iouIssuer{2}; + constexpr Currency currency{3}; + constexpr uint256 domainA{4}; Asset const mptX{MPTIssue{1u, issuerX}}; Asset const iou{Issue{currency, iouIssuer}}; diff --git a/src/test/app/Path_test.cpp b/src/test/app/Path_test.cpp index 5ecad1a4202..3484186fcb8 100644 --- a/src/test/app/Path_test.cpp +++ b/src/test/app/Path_test.cpp @@ -1930,23 +1930,14 @@ class Path_test : public beast::unit_test::Suite { testcase("STPathSet::assembleAdd deduplication — O(N^2) regression"); - static constexpr std::string_view kAccount1 = "A3F19C7B2E5D08146FB93A7C0E2D5184BC6F3A09"; - static constexpr std::string_view kAccount2 = "1D7E4B90C2A6F3851E0B9D47A2C5F8136E0A4B7D"; - static constexpr std::string_view kAccount3 = "F08C36A1D95E27B40CA1F63E8D204B7950E1C3A6"; - static constexpr std::string_view kAccount4 = "4B6209E7F1A3C85D0E94B27Af3D6018C5A7E92B4"; - static constexpr std::string_view kAccount5 = "9E2D7041BCA3F6589D013E7B2A4C6F80159D3E7A"; - static constexpr std::string_view kAccount6 = "7C5A91E384F2D06BA19C4E73D820F516B3A9C0E4"; - static constexpr std::string_view kAccount7 = "2F8B043C6A1E9D75B0C38E14F6A2D509731BC4E8"; - static constexpr std::string_view kAccount8 = "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}; + constexpr AccountID kAccountID1{"A3F19C7B2E5D08146FB93A7C0E2D5184BC6F3A09"}; + constexpr AccountID kAccountID2{"1D7E4B90C2A6F3851E0B9D47A2C5F8136E0A4B7D"}; + constexpr AccountID kAccountID3{"F08C36A1D95E27B40CA1F63E8D204B7950E1C3A6"}; + constexpr AccountID kAccountID4{"4B6209E7F1A3C85D0E94B27Af3D6018C5A7E92B4"}; + constexpr AccountID kAccountID5{"9E2D7041BCA3F6589D013E7B2A4C6F80159D3E7A"}; + constexpr AccountID kAccountID6{"7C5A91E384F2D06BA19C4E73D820F516B3A9C0E4"}; + constexpr AccountID kAccountID7{"2F8B043C6A1E9D75B0C38E14F6A2D509731BC4E8"}; + constexpr AccountID kAccountID8{"E61D9A30F47C285BA0D31E96C7B4F802513A8D6F"}; auto ps = STPathSet{STPathSet::DeduplicationTag{}}; diff --git a/src/test/app/PermissionedDEX_test.cpp b/src/test/app/PermissionedDEX_test.cpp index fbe942948d0..0f76e7434cb 100644 --- a/src/test/app/PermissionedDEX_test.cpp +++ b/src/test/app/PermissionedDEX_test.cpp @@ -58,6 +58,8 @@ using namespace jtx; class PermissionedDEX_test : public beast::unit_test::Suite { + uint256 const badDomain_{"F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134E5"}; + [[nodiscard]] static bool 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); auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - uint256 const badDomain{ - "F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134" - "E5"}; - - env(offer(bob, XRP(10), USD(10)), Domain(badDomain), Ter(tecNO_PERMISSION)); + env(offer(bob, XRP(10), USD(10)), Domain(badDomain_), Ter(tecNO_PERMISSION)); env.close(); } @@ -438,14 +436,11 @@ class PermissionedDEX_test : public beast::unit_test::Suite Env env(*this, features); auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - uint256 const badDomain{ - "F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134" - "E5"}; env(pay(bob, alice, USD(10)), Path(~USD), Sendmax(XRP(10)), - Domain(badDomain), + Domain(badDomain_), Ter(tecNO_PERMISSION)); env.close(); } @@ -1700,17 +1695,13 @@ class PermissionedDEX_test : public beast::unit_test::Suite auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] = PermissionedDEX(env); - uint256 const badDomain{ - "F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134" - "E5"}; - env(offer(bob, XRP(10), USD(10)), Domain(domainID)); env.close(); env(pay(alice, bob, USD(10)), Path(~USD), Sendmax(XRP(10)), - Domain(badDomain), + Domain(badDomain_), Ter(tecNO_PERMISSION)); env.close(); } diff --git a/src/test/app/PermissionedDomains_test.cpp b/src/test/app/PermissionedDomains_test.cpp index 1a2472b3971..ba391b9fbf6 100644 --- a/src/test/app/PermissionedDomains_test.cpp +++ b/src/test/app/PermissionedDomains_test.cpp @@ -102,7 +102,7 @@ class PermissionedDomains_test : public beast::unit_test::Suite env.fund(XRP(1000), alice); pdomain::Credentials const credentials{{.issuer = alice, .credType = "first credential"}}; 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 @@ -391,10 +391,10 @@ class PermissionedDomains_test : public beast::unit_test::Suite env(pdomain::setTx(alice[2], credentials1, domain2), Ter(tecNO_PERMISSION)); // 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 - env(pdomain::setTx(alice[0], credentials1, uint256(75)), Ter(tecNO_ENTRY)); + env(pdomain::setTx(alice[0], credentials1, uint256{75}), Ter(tecNO_ENTRY)); // Wrong 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)); // 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 - 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 env(pdomain::deleteTx(alice, domain), Ter(temINVALID_FLAG), Txflags(tfClawTwoAssets)); // 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. BEAST_EXPECT(env.ownerCount(alice) == 1); diff --git a/src/test/app/PseudoTx_test.cpp b/src/test/app/PseudoTx_test.cpp index 9f65daad182..c2ea1ab2421 100644 --- a/src/test/app/PseudoTx_test.cpp +++ b/src/test/app/PseudoTx_test.cpp @@ -47,7 +47,7 @@ struct PseudoTx_test : public beast::unit_test::Suite res.emplace_back(ttAMENDMENT, [&](auto& obj) { obj.setAccountID(sfAccount, AccountID()); - obj.setFieldH256(sfAmendment, uint256(2)); + obj.setFieldH256(sfAmendment, uint256{2}); obj.setFieldU32(sfLedgerSequence, seq); }); @@ -59,11 +59,11 @@ struct PseudoTx_test : public beast::unit_test::Suite { std::vector 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) { - obj.setAccountID(sfAccount, AccountID(2)); - obj.setAccountID(sfDestination, AccountID(3)); + obj.setAccountID(sfAccount, AccountID{2}); + obj.setAccountID(sfDestination, AccountID{3}); }); return res; diff --git a/src/test/app/RCLValidations_test.cpp b/src/test/app/RCLValidations_test.cpp index 9ace2e21afd..499e95eb3d7 100644 --- a/src/test/app/RCLValidations_test.cpp +++ b/src/test/app/RCLValidations_test.cpp @@ -114,7 +114,7 @@ class RCLValidations_test : public beast::unit_test::Suite { RCLValidatedLedger const a{RCLValidatedLedger::MakeGenesis{}}; 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}); } @@ -133,7 +133,7 @@ class RCLValidations_test : public beast::unit_test::Suite } else { - BEAST_EXPECT(a[s] == ID{0}); + BEAST_EXPECT(a[s] == ID{}); } } } diff --git a/src/test/app/Regression_test.cpp b/src/test/app/Regression_test.cpp index 6ebde201764..00d972997b2 100644 --- a/src/test/app/Regression_test.cpp +++ b/src/test/app/Regression_test.cpp @@ -303,12 +303,11 @@ struct Regression_test : public beast::unit_test::Suite return digest.asUInt256(); }(); - auto const mapCounts = [&](CountedObjects::List const& list) { - std::map result; - for (auto const& e : list) - { - result[e.first] = e.second; - } + auto const mapCounts = [] { + std::map result; + + for (auto const& c : gCountedObjects) + result.emplace(c.name(), c.count()); return result; }; @@ -317,11 +316,11 @@ struct Regression_test : public beast::unit_test::Suite { auto& cache = env.app().getCachedSLEs(); cache.del(*digest, false); // NOLINT(bugprone-unchecked-optional-access) - auto const beforeCounts = mapCounts(CountedObjects::getInstance().getCounts(0)); + auto const beforeCounts = mapCounts(); env(check::cash(alice, bobIndex, check::DeliverMin(XRP(100))), Ter(tecNO_ENTRY)); - auto const afterCounts = mapCounts(CountedObjects::getInstance().getCounts(0)); + auto const afterCounts = mapCounts(); using namespace std::string_literals; BEAST_EXPECT( diff --git a/src/test/app/Sponsor_test.cpp b/src/test/app/Sponsor_test.cpp index 22b5d0bcdc8..eaab7b094b2 100644 --- a/src/test/app/Sponsor_test.cpp +++ b/src/test/app/Sponsor_test.cpp @@ -2207,7 +2207,7 @@ class Sponsor_test : public beast::unit_test::Suite BEAST_EXPECT( env.le(keylet::sponsorship(sponsor, alice))->isFieldPresent(sfFeeAmount)); auto sponsorAvailableFee = sponsor::sponsorshipFeeBalance(env, sponsor, alice); - env(check::cancel(alice, uint256(1)), + env(check::cancel(alice, uint256{1}), Fee(sponsorAvailableFee), sponsor::As(sponsor, spfSponsorFee), Ter(tecNO_ENTRY)); @@ -5315,7 +5315,7 @@ class Sponsor_test : public beast::unit_test::Suite checkBlocked(token::mint(alice, 0u)); checkBlocked(sponsor::set(alice, 0, 10, XRP(10))); checkBlocked(acctdelete(alice, bob)); - checkBlocked(loan::set(alice, uint256(1), Number{1})); + checkBlocked(loan::set(alice, uint256{1}, Number{1})); } void diff --git a/src/test/app/invariants/InvariantsEscrowNFT_test.cpp b/src/test/app/invariants/InvariantsEscrowNFT_test.cpp index f0afa2377c2..7a620529ffb 100644 --- a/src/test/app/invariants/InvariantsEscrowNFT_test.cpp +++ b/src/test/app/invariants/InvariantsEscrowNFT_test.cpp @@ -215,13 +215,13 @@ class InvariantsEscrowNFT_test : public InvariantsBase testcase << "NFTokenPage"; // lambda that returns an STArray of NFTokenIDs. - uint256 const firstNFTID( - "0000000000000000000000000000000000000001FFFFFFFFFFFFFFFF00000000"); - auto makeNFTokenIDs = [&firstNFTID](unsigned int nftCount) { + auto makeNFTokenIDs = [](unsigned int nftCount) { SOTemplate const* nfTokenTemplate = InnerObjectFormats::getInstance().findSOTemplateBySField(sfNFToken); - uint256 nftID(firstNFTID); + auto nftID = + uint256{"0000000000000000000000000000000000000001FFFFFFFFFFFFFFFF00000000"}; + STArray ret; for (int i = 0; i < nftCount; ++i) { diff --git a/src/test/app/lending/LendingHelpers_test.cpp b/src/test/app/lending/LendingHelpers_test.cpp index c509469de98..714e0e718bb 100644 --- a/src/test/app/lending/LendingHelpers_test.cpp +++ b/src/test/app/lending/LendingHelpers_test.cpp @@ -1594,7 +1594,7 @@ class LendingHelpers_test : public beast::unit_test::Suite Number const& principalOutstanding, Number const& managementFeeOutstanding) { - auto sle = std::make_shared(ltLOAN, uint256{1u}); + auto sle = std::make_shared(ltLOAN, uint256{1}); sle->at(sfTotalValueOutstanding) = totalValueOutstanding; sle->at(sfPrincipalOutstanding) = principalOutstanding; sle->at(sfManagementFeeOutstanding) = managementFeeOutstanding; @@ -1609,7 +1609,7 @@ class LendingHelpers_test : public beast::unit_test::Suite std::optional assetsMaximum = std::nullopt, std::optional assetsTotal = std::nullopt) { - auto sle = std::make_shared(ltVAULT, uint256{2u}); + auto sle = std::make_shared(ltVAULT, uint256{2}); if (leVersion) sle->at(sfLEVersion) = std::to_underlying(*leVersion); if (assetsMaximum) @@ -1861,7 +1861,7 @@ class LendingHelpers_test : public beast::unit_test::Suite for (auto const& tc : testCases) { testcase("canApplyToBrokerCover: " + tc.name); - auto sle = std::make_shared(ltLOAN_BROKER, uint256{1u}); + auto sle = std::make_shared(ltLOAN_BROKER, uint256{1}); sle->at(sfCoverAvailable) = tc.coverAvailable; BEAST_EXPECT( canApplyToBrokerCover(*env.current(), sle, iou, tc.amount, env.journal, "test") == @@ -1872,7 +1872,7 @@ class LendingHelpers_test : public beast::unit_test::Suite { testcase("canApplyToBrokerCover: amendment disabled"); Env const envOff{*this, testableAmendments() - fixCleanup3_2_0}; - auto sle = std::make_shared(ltLOAN_BROKER, uint256{1u}); + auto sle = std::make_shared(ltLOAN_BROKER, uint256{1}); sle->at(sfCoverAvailable) = Number{10}; BEAST_EXPECT( canApplyToBrokerCover( diff --git a/src/test/app/lending/LoanBroker_test.cpp b/src/test/app/lending/LoanBroker_test.cpp index 3bcda42c7e4..c2be5b13676 100644 --- a/src/test/app/lending/LoanBroker_test.cpp +++ b/src/test/app/lending/LoanBroker_test.cpp @@ -307,7 +307,7 @@ class LoanBroker_test : public beast::unit_test::Suite }; // Test Cover funding before allowing alterations - env(coverDeposit(alice, uint256(0), vault.asset(10)), Ter(temINVALID)); + env(coverDeposit(alice, uint256{0}, vault.asset(10)), Ter(temINVALID)); env(coverDeposit(evan, keylet.key, vault.asset(10)), Ter(tecNO_PERMISSION)); env(coverDeposit(evan, keylet.key, vault.asset(0)), Ter(temBAD_AMOUNT)); env(coverDeposit(evan, keylet.key, vault.asset(-10)), Ter(temBAD_AMOUNT)); @@ -318,7 +318,7 @@ class LoanBroker_test : public beast::unit_test::Suite // Test cover clawback failure cases BEFORE depositing any cover // Need one of brokerID or amount env(coverClawback(alice), Ter(temINVALID)); - env(coverClawback(alice), kLoanBrokerId(uint256(0)), Ter(temINVALID)); + env(coverClawback(alice), kLoanBrokerId(uint256{0}), Ter(temINVALID)); env(coverClawback(alice), kAmount(XRP(1000)), Ter(temBAD_AMOUNT)); env(coverClawback(alice), kAmount(vault.asset(-10)), Ter(temBAD_AMOUNT)); // Clawbacks with an MPT need to specify the broker ID @@ -377,7 +377,7 @@ class LoanBroker_test : public beast::unit_test::Suite verifyCoverAmount(10); // Test withdrawal failure cases - env(coverWithdraw(alice, uint256(0), vault.asset(10)), Ter(temINVALID)); + env(coverWithdraw(alice, uint256{0}, vault.asset(10)), Ter(temINVALID)); env(coverWithdraw(evan, keylet.key, vault.asset(10)), Ter(tecNO_PERMISSION)); env(coverWithdraw(evan, keylet.key, vault.asset(0)), Ter(temBAD_AMOUNT)); env(coverWithdraw(evan, keylet.key, vault.asset(-10)), Ter(temBAD_AMOUNT)); diff --git a/src/test/app/lending/LoanLifecycle_test.cpp b/src/test/app/lending/LoanLifecycle_test.cpp index 45420529c64..c018ca9eb11 100644 --- a/src/test/app/lending/LoanLifecycle_test.cpp +++ b/src/test/app/lending/LoanLifecycle_test.cpp @@ -236,6 +236,7 @@ class LoanLifecycle_test : public LoanTestBase BEAST_EXPECT(types[type] == 1); } } + auto const loanID = [&]() { json::Value params(json::ValueType::Object); params[jss::account] = lender.human(); @@ -274,12 +275,13 @@ class LoanLifecycle_test : public LoanTestBase return loan["index"].asString(); }(); - auto const loanKeylet{keylet::loan(uint256{std::string_view(loanID)})}; - env.close(startDate); + if (uint256 id; BEAST_EXPECT(id.parseHex(loanID))) { + env.close(startDate); - // Make a payment - env(pay(lender, loanKeylet.key, broker.asset(1000))); + // Make a payment + env(pay(lender, keylet::loan(id).key, broker.asset(1000))); + } } void diff --git a/src/test/app/lending/LoanTestBase.h b/src/test/app/lending/LoanTestBase.h index ab74ab6811b..9fd80f4e2c6 100644 --- a/src/test/app/lending/LoanTestBase.h +++ b/src/test/app/lending/LoanTestBase.h @@ -2300,7 +2300,7 @@ class LoanTestBase : public beast::unit_test::Suite verifyLoanStatus(state); // Send some bogus pay transactions - env(pay(borrower, keylet::loan(uint256(0)).key, broker.asset(10), txFlags), + env(pay(borrower, keylet::loan(uint256{0}).key, broker.asset(10), txFlags), Ter(temINVALID)); // broker.asset(80) is less than a single payment, but all these // checks fail before that matters diff --git a/src/test/app/vault/VaultDomain_test.cpp b/src/test/app/vault/VaultDomain_test.cpp index 5e058a13a82..7f4e95f5d2d 100644 --- a/src/test/app/vault/VaultDomain_test.cpp +++ b/src/test/app/vault/VaultDomain_test.cpp @@ -93,7 +93,7 @@ class VaultDomain_test : public VaultTestBase { testcase("private vault cannot set non-existing domain"); auto tx = vault.set({.owner = owner, .id = keylet.key}); - tx[sfDomainID] = to_string(BaseUInt<256>(42ul)); + tx[sfDomainID] = to_string(uint256{42}); env(tx, Ter{tecOBJECT_NOT_FOUND}); } diff --git a/src/test/app/vault/VaultLifecycle_test.cpp b/src/test/app/vault/VaultLifecycle_test.cpp index 8d7afe67f26..142bdc6b3bd 100644 --- a/src/test/app/vault/VaultLifecycle_test.cpp +++ b/src/test/app/vault/VaultLifecycle_test.cpp @@ -183,7 +183,7 @@ class VaultLifecycle_test : public VaultTestBase { testcase(prefix + " fail to set domain on public vault"); auto tx = vault.set({.owner = owner, .id = keylet.key}); - tx[sfDomainID] = to_string(BaseUInt<256>(42ul)); + tx[sfDomainID] = to_string(uint256{42}); env(tx, Ter{tecNO_PERMISSION}); env.close(); } diff --git a/src/test/app/vault/VaultValidation_test.cpp b/src/test/app/vault/VaultValidation_test.cpp index 45f6d1deaf5..5bafd99f168 100644 --- a/src/test/app/vault/VaultValidation_test.cpp +++ b/src/test/app/vault/VaultValidation_test.cpp @@ -141,14 +141,14 @@ class VaultValidation_test : public VaultTestBase env(tx); tx[sfFlags] = tx[sfFlags].asUInt() | tfVaultPrivate; - tx[sfDomainID] = to_string(BaseUInt<256>(42ul)); + tx[sfDomainID] = to_string(uint256{42}); env(tx, Ter{temDISABLED}); { auto tx = vault.set({.owner = owner, .id = keylet.key}); env(tx, kData("Test")); - tx[sfDomainID] = to_string(BaseUInt<256>(13ul)); + tx[sfDomainID] = to_string(uint256{13}); env(tx, Ter{temDISABLED}); } }, @@ -249,12 +249,12 @@ class VaultValidation_test : public VaultTestBase testcase("disabled permissioned domain"); auto [tx, keylet] = vault.create({.owner = owner, .asset = xrpIssue()}); - tx[sfDomainID] = to_string(BaseUInt<256>(42ul)); + tx[sfDomainID] = to_string(uint256{42}); env(tx, Ter{temDISABLED}); { auto tx = vault.set({.owner = owner, .id = keylet.key}); - tx[sfDomainID] = to_string(BaseUInt<256>(42ul)); + tx[sfDomainID] = to_string(uint256{42}); env(tx, Ter{temDISABLED}); } @@ -542,7 +542,7 @@ class VaultValidation_test : public VaultTestBase { auto tx = tx1; - tx[sfDomainID] = to_string(BaseUInt<256>(42ul)); + tx[sfDomainID] = to_string(uint256{42}); env(tx, Ter{temMALFORMED}); } @@ -701,7 +701,7 @@ class VaultValidation_test : public VaultTestBase Vault& vault) { auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); tx[sfFlags] = tfVaultPrivate; - tx[sfDomainID] = to_string(BaseUInt<256>(42ul)); + tx[sfDomainID] = to_string(uint256{42}); testcase("non-existing domain"); env(tx, Ter{tecOBJECT_NOT_FOUND}); }); diff --git a/src/test/consensus/NegativeUNL_test.cpp b/src/test/consensus/NegativeUNL_test.cpp index 142077a42f4..4b269a9aabd 100644 --- a/src/test/consensus/NegativeUNL_test.cpp +++ b/src/test/consensus/NegativeUNL_test.cpp @@ -729,8 +729,7 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::Suite testcase("Create UNLModify Tx"); jtx::Env env(*this); - NodeID const myId(0xA0); - NegativeUNLVote vote(myId, env.journal); + NegativeUNLVote vote(NodeID{0xA0}, env.journal); // one add, one remove auto txSet = std::make_shared(SHAMapType::TRANSACTION, env.app().getNodeFamily()); @@ -752,21 +751,24 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::Suite testcase("Pick One Candidate"); jtx::Env const env(*this); - NodeID const myId(0xA0); - NegativeUNLVote const vote(myId, env.journal); + NegativeUNLVote const vote(NodeID{0xA0}, env.journal); - uint256 const pad0(0); + uint256 const pad0{}; uint256 const padF = ~pad0; - NodeID const n1(1); - NodeID const n2(2); - NodeID const n3(3); - std::vector candidates({n1}); + + NodeID const n1{1}; + NodeID const n2{2}; + NodeID const n3{3}; + + std::vector candidates; + + candidates.emplace_back(n1); BEAST_EXPECT(vote.choose(pad0, candidates) == n1); BEAST_EXPECT(vote.choose(padF, candidates) == n1); - candidates.emplace_back(2); + candidates.emplace_back(n2); BEAST_EXPECT(vote.choose(pad0, candidates) == n1); BEAST_EXPECT(vote.choose(padF, candidates) == n2); - candidates.emplace_back(3); + candidates.emplace_back(n3); BEAST_EXPECT(vote.choose(pad0, candidates) == n1); BEAST_EXPECT(vote.choose(padF, candidates) == n3); } @@ -1072,8 +1074,8 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::Suite { // 2 new validators - NodeID const new1(0xbead); - NodeID const new2(0xbeef); + NodeID const new1{0xbead}; + NodeID const new2{0xbeef}; hash_set const nowTrusted = {new1, new2}; hash_set unlTemp = history.unlNodeIdSet; unlTemp.insert(new1); @@ -1129,7 +1131,7 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::Suite jtx::Env const env(*this); - NodeID const myId(0xA0); + NodeID const myId{0xA0}; NegativeUNLVote vote(myId, env.journal); std::array const unlSizes = {34, 35, 80}; @@ -1285,7 +1287,7 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::Suite testcase("New Validators"); jtx::Env const env(*this); - NodeID const myId(0xA0); + NodeID const myId{0xA0}; NegativeUNLVote vote(myId, env.journal); // test cases: @@ -1294,9 +1296,9 @@ class NegativeUNLVoteInternal_test : public beast::unit_test::Suite // add a new one and some already added // purge and see some are expired - NodeID const n1(0xA1); - NodeID const n2(0xA2); - NodeID const n3(0xA3); + NodeID const n1{0xA1}; + NodeID const n2{0xA2}; + NodeID const n3{0xA3}; vote.newValidators(2, {n1}); BEAST_EXPECT(vote.newValidators_.size() == 1); @@ -1683,7 +1685,7 @@ class NegativeUNLVoteRetiredValidator_test : public beast::unit_test::Suite [&](std::shared_ptr const& l, std::size_t idx) -> bool { return idx > 1; }); - BEAST_EXPECT(voteAndCheck(history, NodeID(0xdeadbeef), 0)); + BEAST_EXPECT(voteAndCheck(history, NodeID{0xdeadbeef}, 0)); } } diff --git a/src/test/jtx/impl/Env.cpp b/src/test/jtx/impl/Env.cpp index 35553bdeb24..83c84577f3d 100644 --- a/src/test/jtx/impl/Env.cpp +++ b/src/test/jtx/impl/Env.cpp @@ -483,7 +483,7 @@ Env::postconditions( json::Value const& jr, std::source_location const& loc) { - auto const locStr = std::string("(") + loc.file_name() + ":" + to_string(loc.line()) + ")"; + auto const locStr = std::string("(") + loc.file_name() + ":" + std::to_string(loc.line()) + ")"; bool bad = !test.expect(parsed.ter, "apply " + locStr + ": No ter result!"); bad = (jt.ter && parsed.ter && diff --git a/src/test/jtx/impl/Oracle.cpp b/src/test/jtx/impl/Oracle.cpp index cd96ef5c0d5..cbe5c0ce1e6 100644 --- a/src/test/jtx/impl/Oracle.cpp +++ b/src/test/jtx/impl/Oracle.cpp @@ -257,7 +257,7 @@ Oracle::set(UpdateArg const& arg) if (std::holds_alternative(*arg.lastUpdateTime)) { jv[jss::LastUpdateTime] = - to_string(kTestStartTime.count() + std::get(*arg.lastUpdateTime)); + std::to_string(kTestStartTime.count() + std::get(*arg.lastUpdateTime)); } else { @@ -266,7 +266,7 @@ Oracle::set(UpdateArg const& arg) } else { - jv[jss::LastUpdateTime] = to_string( + jv[jss::LastUpdateTime] = std::to_string( duration_cast(env_.current()->header().closeTime.time_since_epoch()).count() + kEpochOffset.count()); } diff --git a/src/test/jtx/utility.h b/src/test/jtx/utility.h index 5a37abd9209..4e5cf089397 100644 --- a/src/test/jtx/utility.h +++ b/src/test/jtx/utility.h @@ -84,4 +84,4 @@ fillSeq(json::Value& jv, ReadView const& view); */ json::Value cmdToJSONRPC(std::vector const& args, beast::Journal j, unsigned int apiVersion); -} // namespace xrpl::test::jtx +} diff --git a/src/test/ledger/View_test.cpp b/src/test/ledger/View_test.cpp index b62061d38d1..5f1c75bd9f0 100644 --- a/src/test/ledger/View_test.cpp +++ b/src/test/ledger/View_test.cpp @@ -1,5 +1,3 @@ - - #include #include #include @@ -21,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -42,27 +41,39 @@ #include #include +#include #include +#include #include #include #include #include +#include #include namespace xrpl::test { class View_test : public beast::unit_test::Suite { + // Every key used in this file, precomputed: kKeys[i] is the uint256 with + // numeric value i. Test ids are small integers, so a table indexed at + // run time keeps integer-to-key conversion in one place and lets every + // key be a compile-time constant. + static constexpr auto kKeys = [](std::index_sequence) { + return std::array{uint256{I}...}; + }(std::make_index_sequence<256>{}); + // Convert a small integer to a key static Keylet - k(std::uint64_t id) + k(std::size_t id) { - return Keylet{ltACCOUNT_ROOT, uint256(id)}; + XRPL_ASSERT(id < kKeys.size(), "xrpl::test::View_test::k : id in range"); + return Keylet{ltACCOUNT_ROOT, kKeys[id]}; } // Create SLE with key and payload static SLE::pointer - sle(std::uint64_t id, std::uint32_t seq = 1) + sle(std::size_t id, std::uint32_t seq = 1) { auto const le = std::make_shared(k(id)); le->setFieldU32(sfSequence, seq); @@ -90,8 +101,7 @@ class View_test : public beast::unit_test::Suite { openLedger.modify([](OpenView& view, beast::Journal) { // HACK! - std::optional next; - next.emplace(0); + std::optional next(uint256{}); for (;;) { next = view.succ(*next); @@ -107,8 +117,7 @@ class View_test : public beast::unit_test::Suite wipe(Ledger& ledger) { // HACK! - std::optional next; - next.emplace(0); + std::optional next{uint256{}}; for (;;) { next = ledger.succ(*next); @@ -120,7 +129,7 @@ class View_test : public beast::unit_test::Suite // Test succ correctness void - succ(ReadView const& v, std::uint32_t id, std::optional answer) + succ(ReadView const& v, std::size_t id, std::optional answer) { auto const next = v.succ(k(id).key); if (answer) @@ -397,17 +406,17 @@ class View_test : public beast::unit_test::Suite sles(ReadView const& ledger) { std::vector v; - v.reserve(32); for (auto const& sle : ledger.sles) v.push_back(sle->key()); return v; } + // Convert a list of small integers to a list of keys template static std::vector list(Args... args) { - return std::vector({uint256(args)...}); + return {k(args).key...}; } void @@ -427,7 +436,7 @@ class View_test : public beast::unit_test::Suite auto const ledger = std::make_shared(*genesis, env.app().getTimeKeeper().closeTime()); - auto setup = [&ledger](std::vector const& vec) { + auto setup = [&ledger](std::vector const& vec) { wipe(*ledger); for (auto x : vec) { @@ -439,20 +448,20 @@ class View_test : public beast::unit_test::Suite BEAST_EXPECT(sles(*ledger) == list(1, 2, 3)); auto e = ledger->stateMap().end(); auto b1 = ledger->stateMap().begin(); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(1)) == e); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(2)) == b1); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(1).key) == e); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(2).key) == b1); ++b1; - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(3)) == b1); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(3).key) == b1); ++b1; - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(4)) == b1); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(5)) == b1); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(4).key) == b1); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(5).key) == b1); b1 = ledger->stateMap().begin(); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(0)) == b1); + BEAST_EXPECT(ledger->stateMap().upperBound(k(0).key) == b1); ++b1; - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(1)) == b1); + BEAST_EXPECT(ledger->stateMap().upperBound(k(1).key) == b1); ++b1; - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(2)) == b1); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(3)) == e); + BEAST_EXPECT(ledger->stateMap().upperBound(k(2).key) == b1); + BEAST_EXPECT(ledger->stateMap().upperBound(k(3).key) == e); } { @@ -460,77 +469,77 @@ class View_test : public beast::unit_test::Suite BEAST_EXPECT(sles(*ledger) == list(2, 4, 6)); auto e = ledger->stateMap().end(); auto b1 = ledger->stateMap().begin(); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(1)) == e); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(2)) == e); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(3)) == b1); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(4)) == b1); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(1).key) == e); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(2).key) == e); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(3).key) == b1); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(4).key) == b1); ++b1; - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(5)) == b1); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(6)) == b1); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(5).key) == b1); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(6).key) == b1); ++b1; - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(7)) == b1); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(7).key) == b1); b1 = ledger->stateMap().begin(); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(1)) == b1); + BEAST_EXPECT(ledger->stateMap().upperBound(k(1).key) == b1); ++b1; - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(2)) == b1); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(3)) == b1); + BEAST_EXPECT(ledger->stateMap().upperBound(k(2).key) == b1); + BEAST_EXPECT(ledger->stateMap().upperBound(k(3).key) == b1); ++b1; - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(4)) == b1); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(5)) == b1); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(6)) == e); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(7)) == e); + BEAST_EXPECT(ledger->stateMap().upperBound(k(4).key) == b1); + BEAST_EXPECT(ledger->stateMap().upperBound(k(5).key) == b1); + BEAST_EXPECT(ledger->stateMap().upperBound(k(6).key) == e); + BEAST_EXPECT(ledger->stateMap().upperBound(k(7).key) == e); } { setup({2, 3, 5, 6, 10, 15}); BEAST_EXPECT(sles(*ledger) == list(2, 3, 5, 6, 10, 15)); auto e = ledger->stateMap().end(); auto b = ledger->stateMap().begin(); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(1)) == e); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(2)) == e); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(3)) == b); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(1).key) == e); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(2).key) == e); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(3).key) == b); ++b; - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(4)) == b); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(5)) == b); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(4).key) == b); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(5).key) == b); ++b; - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(6)) == b); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(6).key) == b); ++b; - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(7)) == b); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(8)) == b); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(9)) == b); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(10)) == b); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(7).key) == b); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(8).key) == b); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(9).key) == b); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(10).key) == b); ++b; - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(11)) == b); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(12)) == b); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(13)) == b); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(14)) == b); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(15)) == b); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(11).key) == b); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(12).key) == b); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(13).key) == b); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(14).key) == b); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(15).key) == b); ++b; - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(16)) == b); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(16).key) == b); b = ledger->stateMap().begin(); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(0)) == b); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(1)) == b); + BEAST_EXPECT(ledger->stateMap().upperBound(k(0).key) == b); + BEAST_EXPECT(ledger->stateMap().upperBound(k(1).key) == b); ++b; - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(2)) == b); + BEAST_EXPECT(ledger->stateMap().upperBound(k(2).key) == b); ++b; - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(3)) == b); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(4)) == b); + BEAST_EXPECT(ledger->stateMap().upperBound(k(3).key) == b); + BEAST_EXPECT(ledger->stateMap().upperBound(k(4).key) == b); ++b; - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(5)) == b); + BEAST_EXPECT(ledger->stateMap().upperBound(k(5).key) == b); ++b; - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(6)) == b); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(7)) == b); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(8)) == b); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(9)) == b); + BEAST_EXPECT(ledger->stateMap().upperBound(k(6).key) == b); + BEAST_EXPECT(ledger->stateMap().upperBound(k(7).key) == b); + BEAST_EXPECT(ledger->stateMap().upperBound(k(8).key) == b); + BEAST_EXPECT(ledger->stateMap().upperBound(k(9).key) == b); ++b; - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(10)) == b); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(11)) == b); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(12)) == b); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(13)) == b); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(14)) == b); + BEAST_EXPECT(ledger->stateMap().upperBound(k(10).key) == b); + BEAST_EXPECT(ledger->stateMap().upperBound(k(11).key) == b); + BEAST_EXPECT(ledger->stateMap().upperBound(k(12).key) == b); + BEAST_EXPECT(ledger->stateMap().upperBound(k(13).key) == b); + BEAST_EXPECT(ledger->stateMap().upperBound(k(14).key) == b); ++b; - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(15)) == e); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(16)) == e); + BEAST_EXPECT(ledger->stateMap().upperBound(k(15).key) == e); + BEAST_EXPECT(ledger->stateMap().upperBound(k(16).key) == e); } { // some full trees, some empty trees, etc @@ -580,42 +589,42 @@ class View_test : public beast::unit_test::Suite 100)); auto b = ledger->stateMap().begin(); auto e = ledger->stateMap().end(); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(0)) == e); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(1)) == b); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(5))->key() == uint256(4)); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(15))->key() == uint256(14)); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(16))->key() == uint256(15)); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(19))->key() == uint256(16)); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(20))->key() == uint256(16)); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(24))->key() == uint256(20)); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(31))->key() == uint256(30)); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(32))->key() == uint256(30)); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(40))->key() == uint256(39)); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(47))->key() == uint256(46)); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(48))->key() == uint256(47)); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(64))->key() == uint256(48)); - - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(90))->key() == uint256(66)); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(96))->key() == uint256(66)); - BEAST_EXPECT(ledger->stateMap().lowerBound(uint256(100))->key() == uint256(66)); - - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(0))->key() == uint256(1)); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(5))->key() == uint256(6)); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(15))->key() == uint256(16)); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(16))->key() == uint256(20)); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(18))->key() == uint256(20)); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(20))->key() == uint256(25)); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(31))->key() == uint256(32)); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(32))->key() == uint256(33)); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(47))->key() == uint256(48)); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(48))->key() == uint256(66)); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(53))->key() == uint256(66)); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(66))->key() == uint256(100)); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(70))->key() == uint256(100)); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(85))->key() == uint256(100)); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(98))->key() == uint256(100)); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(100)) == e); - BEAST_EXPECT(ledger->stateMap().upperBound(uint256(155)) == e); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(0).key) == e); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(1).key) == b); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(5).key)->key() == k(4).key); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(15).key)->key() == k(14).key); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(16).key)->key() == k(15).key); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(19).key)->key() == k(16).key); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(20).key)->key() == k(16).key); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(24).key)->key() == k(20).key); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(31).key)->key() == k(30).key); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(32).key)->key() == k(30).key); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(40).key)->key() == k(39).key); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(47).key)->key() == k(46).key); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(48).key)->key() == k(47).key); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(64).key)->key() == k(48).key); + + BEAST_EXPECT(ledger->stateMap().lowerBound(k(90).key)->key() == k(66).key); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(96).key)->key() == k(66).key); + BEAST_EXPECT(ledger->stateMap().lowerBound(k(100).key)->key() == k(66).key); + + BEAST_EXPECT(ledger->stateMap().upperBound(k(0).key)->key() == k(1).key); + BEAST_EXPECT(ledger->stateMap().upperBound(k(5).key)->key() == k(6).key); + BEAST_EXPECT(ledger->stateMap().upperBound(k(15).key)->key() == k(16).key); + BEAST_EXPECT(ledger->stateMap().upperBound(k(16).key)->key() == k(20).key); + BEAST_EXPECT(ledger->stateMap().upperBound(k(18).key)->key() == k(20).key); + BEAST_EXPECT(ledger->stateMap().upperBound(k(20).key)->key() == k(25).key); + BEAST_EXPECT(ledger->stateMap().upperBound(k(31).key)->key() == k(32).key); + BEAST_EXPECT(ledger->stateMap().upperBound(k(32).key)->key() == k(33).key); + BEAST_EXPECT(ledger->stateMap().upperBound(k(47).key)->key() == k(48).key); + BEAST_EXPECT(ledger->stateMap().upperBound(k(48).key)->key() == k(66).key); + BEAST_EXPECT(ledger->stateMap().upperBound(k(53).key)->key() == k(66).key); + BEAST_EXPECT(ledger->stateMap().upperBound(k(66).key)->key() == k(100).key); + BEAST_EXPECT(ledger->stateMap().upperBound(k(70).key)->key() == k(100).key); + BEAST_EXPECT(ledger->stateMap().upperBound(k(85).key)->key() == k(100).key); + BEAST_EXPECT(ledger->stateMap().upperBound(k(98).key)->key() == k(100).key); + BEAST_EXPECT(ledger->stateMap().upperBound(k(100).key) == e); + BEAST_EXPECT(ledger->stateMap().upperBound(k(155).key) == e); } } @@ -651,15 +660,15 @@ class View_test : public beast::unit_test::Suite view.rawInsert(sle(5)); BEAST_EXPECT(sles(view) == list(2, 3, 4, 5)); auto b = view.sles.begin(); - BEAST_EXPECT(view.sles.upperBound(uint256(1)) == b); + BEAST_EXPECT(view.sles.upperBound(k(1).key) == b); ++b; - BEAST_EXPECT(view.sles.upperBound(uint256(2)) == b); + BEAST_EXPECT(view.sles.upperBound(k(2).key) == b); ++b; - BEAST_EXPECT(view.sles.upperBound(uint256(3)) == b); + BEAST_EXPECT(view.sles.upperBound(k(3).key) == b); ++b; - BEAST_EXPECT(view.sles.upperBound(uint256(4)) == b); + BEAST_EXPECT(view.sles.upperBound(k(4).key) == b); ++b; - BEAST_EXPECT(view.sles.upperBound(uint256(5)) == b); + BEAST_EXPECT(view.sles.upperBound(k(5).key) == b); } { setup123(); @@ -670,14 +679,14 @@ class View_test : public beast::unit_test::Suite view.rawInsert(sle(5)); BEAST_EXPECT(sles(view) == list(3, 4, 5)); auto b = view.sles.begin(); - BEAST_EXPECT(view.sles.upperBound(uint256(1)) == b); - BEAST_EXPECT(view.sles.upperBound(uint256(2)) == b); + BEAST_EXPECT(view.sles.upperBound(k(1).key) == b); + BEAST_EXPECT(view.sles.upperBound(k(2).key) == b); ++b; - BEAST_EXPECT(view.sles.upperBound(uint256(3)) == b); + BEAST_EXPECT(view.sles.upperBound(k(3).key) == b); ++b; - BEAST_EXPECT(view.sles.upperBound(uint256(4)) == b); + BEAST_EXPECT(view.sles.upperBound(k(4).key) == b); ++b; - BEAST_EXPECT(view.sles.upperBound(uint256(5)) == b); + BEAST_EXPECT(view.sles.upperBound(k(5).key) == b); } { setup123(); @@ -689,13 +698,13 @@ class View_test : public beast::unit_test::Suite view.rawInsert(sle(5)); BEAST_EXPECT(sles(view) == list(4, 5)); auto b = view.sles.begin(); - BEAST_EXPECT(view.sles.upperBound(uint256(1)) == b); - BEAST_EXPECT(view.sles.upperBound(uint256(2)) == b); - BEAST_EXPECT(view.sles.upperBound(uint256(3)) == b); + BEAST_EXPECT(view.sles.upperBound(k(1).key) == b); + BEAST_EXPECT(view.sles.upperBound(k(2).key) == b); + BEAST_EXPECT(view.sles.upperBound(k(3).key) == b); ++b; - BEAST_EXPECT(view.sles.upperBound(uint256(4)) == b); + BEAST_EXPECT(view.sles.upperBound(k(4).key) == b); ++b; - BEAST_EXPECT(view.sles.upperBound(uint256(5)) == b); + BEAST_EXPECT(view.sles.upperBound(k(5).key) == b); } { setup123(); @@ -706,14 +715,14 @@ class View_test : public beast::unit_test::Suite BEAST_EXPECT(sles(view) == list(1, 2, 4, 5)); auto b = view.sles.begin(); ++b; - BEAST_EXPECT(view.sles.upperBound(uint256(1)) == b); + BEAST_EXPECT(view.sles.upperBound(k(1).key) == b); ++b; - BEAST_EXPECT(view.sles.upperBound(uint256(2)) == b); - BEAST_EXPECT(view.sles.upperBound(uint256(3)) == b); + BEAST_EXPECT(view.sles.upperBound(k(2).key) == b); + BEAST_EXPECT(view.sles.upperBound(k(3).key) == b); ++b; - BEAST_EXPECT(view.sles.upperBound(uint256(4)) == b); + BEAST_EXPECT(view.sles.upperBound(k(4).key) == b); ++b; - BEAST_EXPECT(view.sles.upperBound(uint256(5)) == b); + BEAST_EXPECT(view.sles.upperBound(k(5).key) == b); } { setup123(); @@ -729,12 +738,12 @@ class View_test : public beast::unit_test::Suite BEAST_EXPECT(sles(view) == list(1, 2)); auto b = view.sles.begin(); ++b; - BEAST_EXPECT(view.sles.upperBound(uint256(1)) == b); + BEAST_EXPECT(view.sles.upperBound(k(1).key) == b); ++b; - BEAST_EXPECT(view.sles.upperBound(uint256(2)) == b); - BEAST_EXPECT(view.sles.upperBound(uint256(3)) == b); - BEAST_EXPECT(view.sles.upperBound(uint256(4)) == b); - BEAST_EXPECT(view.sles.upperBound(uint256(5)) == b); + BEAST_EXPECT(view.sles.upperBound(k(2).key) == b); + BEAST_EXPECT(view.sles.upperBound(k(3).key) == b); + BEAST_EXPECT(view.sles.upperBound(k(4).key) == b); + BEAST_EXPECT(view.sles.upperBound(k(5).key) == b); view.rawInsert(sle(5)); view.rawInsert(sle(4)); @@ -742,15 +751,15 @@ class View_test : public beast::unit_test::Suite BEAST_EXPECT(sles(view) == list(1, 2, 3, 4, 5)); b = view.sles.begin(); ++b; - BEAST_EXPECT(view.sles.upperBound(uint256(1)) == b); + BEAST_EXPECT(view.sles.upperBound(k(1).key) == b); ++b; - BEAST_EXPECT(view.sles.upperBound(uint256(2)) == b); + BEAST_EXPECT(view.sles.upperBound(k(2).key) == b); ++b; - BEAST_EXPECT(view.sles.upperBound(uint256(3)) == b); + BEAST_EXPECT(view.sles.upperBound(k(3).key) == b); ++b; - BEAST_EXPECT(view.sles.upperBound(uint256(4)) == b); + BEAST_EXPECT(view.sles.upperBound(k(4).key) == b); ++b; - BEAST_EXPECT(view.sles.upperBound(uint256(5)) == b); + BEAST_EXPECT(view.sles.upperBound(k(5).key) == b); } } diff --git a/src/test/overlay/compression_test.cpp b/src/test/overlay/compression_test.cpp index 40dee96c75b..319189bd481 100644 --- a/src/test/overlay/compression_test.cpp +++ b/src/test/overlay/compression_test.cpp @@ -240,7 +240,7 @@ class compression_test : public beast::unit_test::Suite ledgerData->set_requestcookie(123456789); ledgerData->set_error(protocol::TMReplyError::reNO_LEDGER); ledgerData->mutable_nodes()->Reserve(n); - uint256 parentHash(0); + uint256 parentHash{}; NetClock::duration const resolution{10}; NetClock::time_point ct{resolution}; diff --git a/src/test/overlay/reduce_relay_test.cpp b/src/test/overlay/reduce_relay_test.cpp index ff3eb51a4dd..3249845e008 100644 --- a/src/test/overlay/reduce_relay_test.cpp +++ b/src/test/overlay/reduce_relay_test.cpp @@ -1357,19 +1357,18 @@ vp_base_squelch_max_selected_peers=2 network_.reset(); // update message count for the same peer/validator std::int16_t const nMessages = 5; - for (int i = 0; i < nMessages; i++) + // hash of the last message; the duplicate below re-sends it + auto const key = uint256{nMessages - 1}; + for (uint256 hash{}; hash <= key; ++hash) { - uint256 const key(i); network_.overlay().updateSlotAndSquelch( - key, network_.validator(0), 0, [&](PublicKey const&, PeerWPtr, std::uint32_t) { - }); + hash, network_.validator(0), 0, [&](PublicKey const&, PeerWPtr, std::uint32_t) {}); } auto peers = network_.overlay().getPeers(network_.validator(0)); // first message changes Slot state to Counting and is not counted, // hence '-1'. BEAST_EXPECT(std::get<1>(peers[0]) == (nMessages - 1)); // add duplicate - uint256 const key(nMessages - 1); network_.overlay().updateSlotAndSquelch( key, network_.validator(0), 0, [&](PublicKey const&, PeerWPtr, std::uint32_t) {}); // confirm the same number of messages @@ -1410,20 +1409,17 @@ vp_base_squelch_max_selected_peers=2 auto run = [&](int npeers) { handler.maxDuration = 0; reduce_relay::Slots slots(env_.app(), handler, env_.app().config()); - // 1st message from a new peer switches the slot - // to counting state and resets the counts of all peers + - // MAX_MESSAGE_THRESHOLD + 1 messages to reach the threshold - // and switch the slot's state to peer selection. + + // Each message needs a distinct hash so the slot's internal hash + // router accepts it; a counter is enough. + uint256 hash{}; + for (int m = 1; m <= reduce_relay::kMaxMessageThreshold + 2; m++) { for (int peer = 0; peer < npeers; peer++) { - // make unique message hash to make the - // slot's internal hash router accept the message - std::uint64_t const mid = (m * 1000) + peer; - uint256 const message{mid}; slots.updateSlotAndSquelch( - message, validator, peer, protocol::MessageType::mtVALIDATION); + ++hash, validator, peer, protocol::MessageType::mtVALIDATION); } } // make Slot's internal hash router expire all messages diff --git a/src/test/protocol/Issue_test.cpp b/src/test/protocol/Issue_test.cpp index a6a1fdd341b..90760286e34 100644 --- a/src/test/protocol/Issue_test.cpp +++ b/src/test/protocol/Issue_test.cpp @@ -32,9 +32,9 @@ class Issue_test : public beast::unit_test::Suite void testUnsigned() { - Unsigned const u1(1); - Unsigned const u2(2); - Unsigned const u3(3); + Unsigned const u1{1}; + Unsigned const u2{2}; + Unsigned const u3{3}; BEAST_EXPECT(u1 != u2); BEAST_EXPECT(u1 < u2); @@ -62,12 +62,12 @@ class Issue_test : public beast::unit_test::Suite void testIssue() { - Currency const c1(1); - AccountID const i1(1); - Currency const c2(2); - AccountID const i2(2); - Currency const c3(3); - AccountID const i3(3); + Currency const c1{1}; + AccountID const i1{1}; + Currency const c2{2}; + AccountID const i2{2}; + Currency const c3{3}; + AccountID const i3{3}; BEAST_EXPECT(Issue(c1, i1) != Issue(c2, i1)); BEAST_EXPECT(Issue(c1, i1) < Issue(c2, i1)); @@ -111,10 +111,10 @@ class Issue_test : public beast::unit_test::Suite void testIssueSet() { - Currency const c1(1); - AccountID const i1(1); - Currency const c2(2); - AccountID const i2(2); + Currency const c1{1}; + AccountID const i1{1}; + Currency const c2{2}; + AccountID const i2{2}; Issue const a1(c1, i1); Issue const a2(c2, i2); @@ -172,10 +172,10 @@ class Issue_test : public beast::unit_test::Suite void testIssueMap() { - Currency const c1(1); - AccountID const i1(1); - Currency const c2(2); - AccountID const i2(2); + Currency const c1{1}; + AccountID const i1{1}; + Currency const c2{2}; + AccountID const i2{2}; Issue const a1(c1, i1); Issue const a2(c2, i2); @@ -224,10 +224,10 @@ class Issue_test : public beast::unit_test::Suite void testIssueDomainSet() { - Currency const c1(1); - AccountID const i1(1); - Currency const c2(2); - AccountID const i2(2); + Currency const c1{1}; + AccountID const i1{1}; + Currency const c2{2}; + AccountID const i2{2}; Issue const a1(c1, i1); Issue const a2(c2, i2); uint256 const domain1{1}; @@ -261,10 +261,10 @@ class Issue_test : public beast::unit_test::Suite void testIssueDomainMap() { - Currency const c1(1); - AccountID const i1(1); - Currency const c2(2); - AccountID const i2(2); + Currency const c1{1}; + AccountID const i1{1}; + Currency const c2{2}; + AccountID const i2{2}; Issue const a1(c1, i1); Issue const a2(c2, i2); uint256 const domain1{1}; @@ -390,12 +390,11 @@ class Issue_test : public beast::unit_test::Suite void testBook() { - Currency const c1(1); - AccountID const i1(1); - Currency const c2(2); - AccountID const i2(2); - Currency const c3(3); - AccountID const i3(3); + Currency const c1{1}; + AccountID const i1{1}; + Currency const c2{2}; + AccountID const i2{2}; + Currency const c3{3}; Issue const a1(c1, i1); Issue const a2(c1, i2); @@ -553,10 +552,10 @@ class Issue_test : public beast::unit_test::Suite void testBookSet() { - Currency const c1(1); - AccountID const i1(1); - Currency const c2(2); - AccountID const i2(2); + Currency const c1{1}; + AccountID const i1{1}; + Currency const c2{2}; + AccountID const i2{2}; Issue const a1(c1, i1); Issue const a2(c2, i2); Book const b1(a1, a2, std::nullopt); @@ -684,10 +683,10 @@ class Issue_test : public beast::unit_test::Suite void testBookMap() { - Currency const c1(1); - AccountID const i1(1); - Currency const c2(2); - AccountID const i2(2); + Currency const c1{1}; + AccountID const i1{1}; + Currency const c2{2}; + AccountID const i2{2}; Issue const a1(c1, i1); Issue const a2(c2, i2); Book const b1(a1, a2, std::nullopt); diff --git a/src/test/protocol/Quality_test.cpp b/src/test/protocol/Quality_test.cpp index e7ffa9f3056..ff92a10618b 100644 --- a/src/test/protocol/Quality_test.cpp +++ b/src/test/protocol/Quality_test.cpp @@ -17,7 +17,7 @@ class Quality_test : public beast::unit_test::Suite // Create a raw, non-integral amount from mantissa and exponent STAmount static raw(std::uint64_t mantissa, int exponent) { - return STAmount(Issue{Currency(3), AccountID(3)}, mantissa, exponent); + return STAmount(Issue{Currency{3}, AccountID{3}}, mantissa, exponent); } template diff --git a/src/test/protocol/STAccount_test.cpp b/src/test/protocol/STAccount_test.cpp index b2d012783d5..d960dc9c19c 100644 --- a/src/test/protocol/STAccount_test.cpp +++ b/src/test/protocol/STAccount_test.cpp @@ -63,7 +63,7 @@ struct STAccount_test : public beast::unit_test::Suite STAccount const zeroAcct{sfAccount, AccountID{}}; BEAST_EXPECT(zeroAcct.getText() == "rrrrrrrrrrrrrrrrrrrrrhoLvTp"); BEAST_EXPECT(!zeroAcct.isDefault()); - BEAST_EXPECT(zeroAcct.value() == AccountID{0}); + BEAST_EXPECT(zeroAcct.value() == AccountID{}); BEAST_EXPECT(!zeroAcct.isEquivalent(defaultAcct)); BEAST_EXPECT(!zeroAcct.isEquivalent(sfAcct)); { diff --git a/src/test/protocol/STAmount_test.cpp b/src/test/protocol/STAmount_test.cpp index c3a681cf018..83819d7c581 100644 --- a/src/test/protocol/STAmount_test.cpp +++ b/src/test/protocol/STAmount_test.cpp @@ -191,7 +191,7 @@ class STAmount_test : public beast::unit_test::Suite { testcase("set value (iou)"); - Issue const usd(Currency(0x5553440000000000), AccountID(0x4985601)); + Issue const usd(toCurrency("USD"), AccountID{0x4985601}); testSetValue("1", usd); testSetValue("10", usd); @@ -747,7 +747,7 @@ class STAmount_test : public beast::unit_test::Suite { testcase("STAmount to XRPAmount conversions"); - Issue const usd{Currency(0x5553440000000000), AccountID(0x4985601)}; + Issue const usd{toCurrency("USD"), AccountID{0x4985601}}; Issue const xrp{xrpIssue()}; for (std::uint64_t drops = 100000000000000000; drops != 1; drops = drops / 10) @@ -779,7 +779,7 @@ class STAmount_test : public beast::unit_test::Suite { testcase("STAmount to IOUAmount conversions"); - Issue const usd{Currency(0x5553440000000000), AccountID(0x4985601)}; + Issue const usd{toCurrency("USD"), AccountID{0x4985601}}; Issue const xrp{xrpIssue()}; for (std::uint64_t dollars = 10000000000; dollars != 1; dollars = dollars / 10) @@ -867,8 +867,8 @@ class STAmount_test : public beast::unit_test::Suite { testcase("can add iou"); - Issue const usd{Currency(0x5553440000000000), AccountID(0x4985601)}; - Issue const eur{Currency(0x4555520000000000), AccountID(0x4985601)}; + Issue const usd{toCurrency("USD"), AccountID{0x4985601}}; + Issue const eur{toCurrency("EUR"), AccountID{0x4985601}}; // Adding two IOU amounts { @@ -925,8 +925,8 @@ class STAmount_test : public beast::unit_test::Suite { testcase("can add mpt"); - MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID(0x4985601))}}; - MPTIssue const mpt2{MPTIssue{makeMptID(2, AccountID(0x4985601))}}; + MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID{0x4985601})}}; + MPTIssue const mpt2{MPTIssue{makeMptID(2, AccountID{0x4985601})}}; // Adding zero { @@ -1001,7 +1001,7 @@ class STAmount_test : public beast::unit_test::Suite { testcase("MPT transfer rate rounding uses Number arithmetic"); - MPTIssue const asset{makeMptID(1, AccountID(0x4985601))}; + MPTIssue const asset{makeMptID(1, AccountID{0x4985601})}; Rate const transferRate{1'500'000'000}; STAmount const largeAmount{asset, UINT64_C(1'230'000'000'000'000'000)}; STAmount const scaledAmount{asset, UINT64_C(1'845'000'000'000'000'000)}; @@ -1063,7 +1063,7 @@ class STAmount_test : public beast::unit_test::Suite // kMinValue exercises the legacy value-scaling loop that normalizes // the mantissa before multiply. The MPTokensV2 Number path is // not taken here because the target asset is an IOU. - Issue const usd{Currency(0x5553440000000000), AccountID(0x4985601)}; + Issue const usd{toCurrency("USD"), AccountID{0x4985601}}; STAmount const iouVal{usd, 5}; STAmount const xrpVal{XRPAmount{7}}; // integral, mantissa < kMinValue @@ -1134,8 +1134,8 @@ class STAmount_test : public beast::unit_test::Suite testCanSubtractIOU() { testcase("can subtract iou"); - Issue const usd{Currency(0x5553440000000000), AccountID(0x4985601)}; - Issue const eur{Currency(0x4555520000000000), AccountID(0x4985601)}; + Issue const usd{toCurrency("USD"), AccountID{0x4985601}}; + Issue const eur{toCurrency("EUR"), AccountID{0x4985601}}; // Subtracting two IOU amounts { @@ -1171,8 +1171,8 @@ class STAmount_test : public beast::unit_test::Suite { testcase("can subtract mpt"); - MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID(0x4985601))}}; - MPTIssue const mpt2{MPTIssue{makeMptID(2, AccountID(0x4985601))}}; + MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID{0x4985601})}}; + MPTIssue const mpt2{MPTIssue{makeMptID(2, AccountID{0x4985601})}}; // Subtracting zero { @@ -1247,7 +1247,7 @@ class STAmount_test : public beast::unit_test::Suite { testcase("isZeroAtScale"); - Issue const usd{Currency(0x5553440000000000), AccountID(0x4985601)}; + Issue const usd{toCurrency("USD"), AccountID{0x4985601}}; // IOU: 10 IOU — mantissa = kMinValue (10^15), exponent = -14. // One ULP at this scale is 10^-14; half-ULP is 5*10^-15. @@ -1324,7 +1324,7 @@ class STAmount_test : public beast::unit_test::Suite // MPT is integral — same short-circuit behaviour as XRP. { - MPTIssue const mpt{makeMptID(1, AccountID(0x4985601))}; + MPTIssue const mpt{makeMptID(1, AccountID{0x4985601})}; STAmount const mptAmt{mpt, 1}; BEAST_EXPECT(!mptAmt.isZeroAtScale(0)); BEAST_EXPECT(!mptAmt.isZeroAtScale(-14)); diff --git a/src/test/protocol/STObject_test.cpp b/src/test/protocol/STObject_test.cpp index 8b7db632c54..67dcb44439a 100644 --- a/src/test/protocol/STObject_test.cpp +++ b/src/test/protocol/STObject_test.cpp @@ -151,13 +151,14 @@ class STObject_test : public beast::unit_test::Suite } { - std::vector uints; - uints.reserve(5); - for (int i = 0; i < uints.capacity(); ++i) - { - uints.emplace_back(i); - } - object1.setFieldV256(sfTestV256, STVector256(uints)); + object1.setFieldV256(sfTestV256, STVector256([]() { + std::vector uints(5); + + for (std::size_t i = 1; i != uints.size(); ++i) + uints[i] = uints[i - 1].next(); + + return uints; + }())); Serializer s; object1.add(s); @@ -421,15 +422,15 @@ class STObject_test : public beast::unit_test::Suite auto const& sf = sfIndexes; STObject st(sfGeneric); std::vector v; - v.emplace_back(1); - v.emplace_back(2); + v.emplace_back(uint256{1}); + v.emplace_back(uint256{2}); st[sf] = v; st[sf] = std::move(v); auto const& cst = st; BEAST_EXPECT(cst[sf].size() == 2); BEAST_EXPECT(cst[~sf]->size() == 2); // NOLINT(bugprone-unchecked-optional-access) - BEAST_EXPECT(cst[sf][0] == 1); - BEAST_EXPECT(cst[sf][1] == 2); + BEAST_EXPECT(cst[sf][0] == uint256{1}); + BEAST_EXPECT(cst[sf][1] == uint256{2}); static_assert(std::is_same_v const&>); } @@ -451,7 +452,7 @@ class STObject_test : public beast::unit_test::Suite BEAST_EXPECT(!cst[~sf2]); BEAST_EXPECT(cst[sf3].empty()); std::vector v; - v.emplace_back(1); + v.emplace_back(uint256{1}); st[sf1] = v; BEAST_EXPECT(cst[sf1].size() == 1); BEAST_EXPECT(cst[sf1][0] == uint256{1}); diff --git a/src/test/protocol/STParsedJSON_test.cpp b/src/test/protocol/STParsedJSON_test.cpp index c2030627c73..c02bcb17267 100644 --- a/src/test/protocol/STParsedJSON_test.cpp +++ b/src/test/protocol/STParsedJSON_test.cpp @@ -17,6 +17,8 @@ #include #include +#include + #include #include #include @@ -364,404 +366,49 @@ class STParsedJSON_test : public beast::unit_test::Suite } } + template void - testUInt128() + testHashField(Field const& field, T const& value) { - testcase("UInt128"); - { - json::Value j; - j[sfEmailHash] = "0123456789ABCDEF0123456789ABCDEF"; - STParsedJSONObject obj("Test", j); - BEAST_EXPECT(obj.object.has_value()); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(obj.object->isFieldPresent(sfEmailHash)); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(obj.object->getFieldH128(sfEmailHash).size() == 16); - std::array const expected = { - 0x01, - 0x23, - 0x45, - 0x67, - 0x89, - 0xAB, - 0xCD, - 0xEF, - 0x01, - 0x23, - 0x45, - 0x67, - 0x89, - 0xAB, - 0xCD, - 0xEF}; - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(obj.object->getFieldH128(sfEmailHash) == uint128::fromRaw(expected)); - } - - // Valid lowercase hex string for UInt128 - { - json::Value j; - j[sfEmailHash] = "0123456789abcdef0123456789abcdef"; - STParsedJSONObject obj("Test", j); - BEAST_EXPECT(obj.object.has_value()); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(obj.object->isFieldPresent(sfEmailHash)); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(obj.object->getFieldH128(sfEmailHash).size() == 16); - } - - // Empty string for UInt128 (should be valid, all zero) - { - json::Value j; - j[sfEmailHash] = ""; - STParsedJSONObject obj("Test", j); - BEAST_EXPECT(obj.object.has_value()); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(obj.object->isFieldPresent(sfEmailHash)); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - auto const& h128 = obj.object->getFieldH128(sfEmailHash); - BEAST_EXPECT(h128.size() == 16); - bool const allZero = std::ranges::all_of(h128, [](auto b) { return b == 0; }); - BEAST_EXPECT(allZero); - } - - // Odd-length hex string for UInt128 (should fail) - { - json::Value j; - j[sfEmailHash] = "0123456789ABCDEF0123456789ABCDE"; - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } - - // Non-hex string for UInt128 (should fail) - { - json::Value j; - j[sfEmailHash] = "nothexstring"; - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } - - // Hex string too short for UInt128 (should fail) - { - json::Value j; - j[sfEmailHash] = "01234567"; - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } - - // Hex string too long for UInt128 (should fail) - { - json::Value j; - j[sfEmailHash] = "0123456789ABCDEF0123456789ABCDEF00"; - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } - - // Array value for UInt128 (should fail) - { - json::Value j; - j[sfEmailHash] = json::Value(json::ValueType::Array); - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } - - // Object value for UInt128 (should fail) - { - json::Value j; - j[sfEmailHash] = json::Value(json::ValueType::Object); - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } + auto const parse = [&](json::Value const& v) { + json::Value j; + j[field] = v; + return STParsedJSONObject("Test", j).object; + }; + + auto const expectValue = [&](json::Value const& v, T const& expected) { + auto const obj = parse(v); + BEAST_EXPECT(obj && obj->isFieldPresent(field) && (*obj)[field] == expected); + }; + + auto const expectFail = [&](json::Value const& v) { BEAST_EXPECT(!parse(v)); }; + + std::string const hex = to_string(value); // upper-case, exactly 2 * T::size() chars + + expectValue(hex, value); + expectValue(boost::to_lower_copy(hex), value); + expectValue("", T{}); + expectFail(hex.substr(0, hex.size() - 1)); // odd length + expectFail("nothexstring"); + expectFail("01234567"); // too short + expectFail(hex + "00"); // too long + expectFail(json::Value(json::ValueType::Array)); + expectFail(json::Value(json::ValueType::Object)); } void - testUInt160() + testBaseUIntFields() { + testcase("UInt128"); + testHashField(sfEmailHash, uint128{"0123456789ABCDEF0123456789ABCDEF"}); testcase("UInt160"); - { - json::Value j; - j[sfTakerPaysCurrency] = "0123456789ABCDEF0123456789ABCDEF01234567"; - STParsedJSONObject obj("Test", j); - BEAST_EXPECT(obj.object.has_value()); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(obj.object->isFieldPresent(sfTakerPaysCurrency)); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(obj.object->getFieldH160(sfTakerPaysCurrency).size() == 20); - std::array const expected = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, - 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, - 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67}; - BEAST_EXPECT( - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - obj.object->getFieldH160(sfTakerPaysCurrency) == uint160::fromRaw(expected)); - } - // Valid lowercase hex string for UInt160 - { - json::Value j; - j[sfTakerPaysCurrency] = "0123456789abcdef0123456789abcdef01234567"; - STParsedJSONObject obj("Test", j); - BEAST_EXPECT(obj.object.has_value()); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(obj.object->isFieldPresent(sfTakerPaysCurrency)); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(obj.object->getFieldH160(sfTakerPaysCurrency).size() == 20); - } - - // Empty string for UInt160 (should be valid, all zero) - { - json::Value j; - j[sfTakerPaysCurrency] = ""; - STParsedJSONObject obj("Test", j); - BEAST_EXPECT(obj.object.has_value()); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(obj.object->isFieldPresent(sfTakerPaysCurrency)); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - auto const& h160 = obj.object->getFieldH160(sfTakerPaysCurrency); - BEAST_EXPECT(h160.size() == 20); - bool const allZero = std::ranges::all_of(h160, [](auto b) { return b == 0; }); - BEAST_EXPECT(allZero); - } - - // Non-hex string for UInt160 (should fail) - { - json::Value j; - j[sfTakerPaysCurrency] = "nothexstring"; - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } - - // Hex string too short for UInt160 (should fail) - { - json::Value j; - j[sfTakerPaysCurrency] = "01234567"; - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } - - // Hex string too long for UInt160 (should fail) - { - json::Value j; - j[sfTakerPaysCurrency] = "0123456789ABCDEF0123456789ABCDEF0123456789"; - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } - - // Array value for UInt160 (should fail) - { - json::Value j; - j[sfTakerPaysCurrency] = json::Value(json::ValueType::Array); - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } - - // Object value for UInt160 (should fail) - { - json::Value j; - j[sfTakerPaysCurrency] = json::Value(json::ValueType::Object); - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } - } - - void - testUInt192() - { + testHashField(sfTakerPaysCurrency, uint160{"0123456789ABCDEF0123456789ABCDEF01234567"}); testcase("UInt192"); - { - json::Value j; - j[sfMPTokenIssuanceID] = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"; - STParsedJSONObject obj("Test", j); - BEAST_EXPECT(obj.object.has_value()); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(obj.object->isFieldPresent(sfMPTokenIssuanceID)); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(obj.object->getFieldH192(sfMPTokenIssuanceID).size() == 24); - std::array const expected = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - BEAST_EXPECT( - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - obj.object->getFieldH192(sfMPTokenIssuanceID) == uint192::fromRaw(expected)); - } - - // Valid lowercase hex string for UInt192 - { - json::Value j; - j[sfMPTokenIssuanceID] = "ffffffffffffffffffffffffffffffffffffffffffffffff"; - STParsedJSONObject obj("Test", j); - BEAST_EXPECT(obj.object.has_value()); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(obj.object->isFieldPresent(sfMPTokenIssuanceID)); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(obj.object->getFieldH192(sfMPTokenIssuanceID).size() == 24); - } - - // Empty string for UInt192 (should be valid, all zero) - { - json::Value j; - j[sfMPTokenIssuanceID] = ""; - STParsedJSONObject obj("Test", j); - BEAST_EXPECT(obj.object.has_value()); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(obj.object->isFieldPresent(sfMPTokenIssuanceID)); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - auto const& h192 = obj.object->getFieldH192(sfMPTokenIssuanceID); - BEAST_EXPECT(h192.size() == 24); - bool const allZero = std::ranges::all_of(h192, [](auto b) { return b == 0; }); - BEAST_EXPECT(allZero); - } - - // Odd-length hex string for UInt192 (should fail) - { - json::Value j; - j[sfMPTokenIssuanceID] = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDE"; - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } - - // Non-hex string for UInt192 (should fail) - { - json::Value j; - j[sfMPTokenIssuanceID] = "nothexstring"; - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } - - // Hex string too short for UInt192 (should fail) - { - json::Value j; - j[sfMPTokenIssuanceID] = "01234567"; - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } - - // Hex string too long for UInt192 (should fail) - { - json::Value j; - j[sfMPTokenIssuanceID] = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF00"; - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } - - // Array value for UInt192 (should fail) - { - json::Value j; - j[sfMPTokenIssuanceID] = json::Value(json::ValueType::Array); - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } - - // Object value for UInt192 (should fail) - { - json::Value j; - j[sfMPTokenIssuanceID] = json::Value(json::ValueType::Object); - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } - } - - void - testUInt256() - { + testHashField(sfMPTokenIssuanceID, uint192{"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"}); testcase("UInt256"); - // Test with valid hex string for UInt256 - { - json::Value j; - j[sfLedgerHash] = - "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCD" - "EF"; - STParsedJSONObject obj("Test", j); - BEAST_EXPECT(obj.object.has_value()); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerHash)); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(obj.object->getFieldH256(sfLedgerHash).size() == 32); - std::array const expected = { - 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, - 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, - 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}; - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(obj.object->getFieldH256(sfLedgerHash) == uint256::fromRaw(expected)); - } - // Valid lowercase hex string for UInt256 - { - json::Value j; - j[sfLedgerHash] = - "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd" - "ef"; - STParsedJSONObject obj("Test", j); - BEAST_EXPECT(obj.object.has_value()); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerHash)); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(obj.object->getFieldH256(sfLedgerHash).size() == 32); - } - - // Empty string for UInt256 (should be valid, all zero) - { - json::Value j; - j[sfLedgerHash] = ""; - STParsedJSONObject obj("Test", j); - BEAST_EXPECT(obj.object.has_value()); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(obj.object->isFieldPresent(sfLedgerHash)); - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - auto const& h256 = obj.object->getFieldH256(sfLedgerHash); - BEAST_EXPECT(h256.size() == 32); - bool const allZero = std::ranges::all_of(h256, [](auto b) { return b == 0; }); - BEAST_EXPECT(allZero); - } - - // Odd-length hex string for UInt256 (should fail) - { - json::Value j; - j[sfLedgerHash] = - "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCD" - "E"; - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } - - // Non-hex string for UInt256 (should fail) - { - json::Value j; - j[sfLedgerHash] = "nothexstring"; - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } - - // Hex string too short for UInt256 (should fail) - { - json::Value j; - j[sfLedgerHash] = "01234567"; - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } - - // Hex string too long for UInt256 (should fail) - { - json::Value j; - j[sfLedgerHash] = - "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCD" - "EF00"; - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } - - // Array value for UInt256 (should fail) - { - json::Value j; - j[sfLedgerHash] = json::Value(json::ValueType::Array); - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } - - // Object value for UInt256 (should fail) - { - json::Value j; - j[sfLedgerHash] = json::Value(json::ValueType::Object); - STParsedJSONObject const obj("Test", j); - BEAST_EXPECT(!obj.object.has_value()); - } + testHashField( + sfLedgerHash, + uint256{"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"}); } void @@ -2590,10 +2237,7 @@ class STParsedJSON_test : public beast::unit_test::Suite testUInt16(); testUInt32(); testUInt64(); - testUInt128(); - testUInt160(); - testUInt192(); - testUInt256(); + testBaseUIntFields(); testInt32(); testBlob(); testVector256(); diff --git a/src/test/protocol/STTx_test.cpp b/src/test/protocol/STTx_test.cpp index f310274e562..3fc7138c9cb 100644 --- a/src/test/protocol/STTx_test.cpp +++ b/src/test/protocol/STTx_test.cpp @@ -1391,7 +1391,7 @@ class STTx_test : public beast::unit_test::Suite // Make an STObject that nests objects ten levels deep. There's // a minimum transaction size we must meet, so include a hash256. - uint256 const hash{42u}; + uint256 const hash{42}; auto inner = std::make_unique(recurse, sfTransactionMetaData); inner->setFieldH256(sfTransactionHash, hash); @@ -1453,7 +1453,7 @@ class STTx_test : public beast::unit_test::Suite // Make an STObject that nests ten levels deep alternating objects // and arrays. Include a hash256 to meet the minimum transaction // size. - uint256 const hash{42u}; + uint256 const hash{42}; STObject inner = {recurse, sfTransactionMetaData}; inner.setFieldH256(sfTransactionHash, hash); diff --git a/src/test/protocol/SecretKey_test.cpp b/src/test/protocol/SecretKey_test.cpp index 967c4417ebe..647c2e57ba6 100644 --- a/src/test/protocol/SecretKey_test.cpp +++ b/src/test/protocol/SecretKey_test.cpp @@ -42,11 +42,6 @@ class SecretKey_test : public beast::unit_test::Suite { testcase("secp256k1: canonicality"); - std::array const digestData{ - 0x34, 0xC1, 0x90, 0x28, 0xC8, 0x0D, 0x21, 0xF3, 0xF4, 0x8C, 0x93, - 0x54, 0x89, 0x5F, 0x8D, 0x5B, 0xF0, 0xD5, 0xEE, 0x7F, 0xF4, 0x57, - 0x64, 0x7C, 0xF6, 0x55, 0xF5, 0x53, 0x0A, 0x30, 0x22, 0xA7}; - std::array const pkData{ 0x02, 0x50, 0x96, 0xEB, 0x12, 0xD3, 0xE9, 0x24, 0x23, 0x4E, 0x71, 0x62, 0x36, 0x9C, 0x11, 0xD8, 0xBF, 0x87, 0x7E, 0xDA, 0x23, 0x87, @@ -73,27 +68,13 @@ class SecretKey_test : public beast::unit_test::Suite 0x89, 0x1C, 0x60, 0xBA, 0x63, 0x74, 0x44, 0xF7, 0x1A, 0x12, 0x9E, 0x47, 0x13, 0x5D, 0x36, 0xD9, 0x2A, 0xFD, 0x39, 0xB8, 0x56, 0x60, 0x1A, 0x01}; - auto const digest = uint256::fromVoid(digestData.data()); + constexpr uint256 digest{"34C19028C80D21F3F48C9354895F8D5BF0D5EE7FF457647CF655F5530A3022A7"}; PublicKey const pk{makeSlice(pkData)}; SecretKey const sk{makeSlice(skData)}; - { - auto const canonicality = ecdsaCanonicality(makeSlice(sig)); - BEAST_EXPECT(canonicality); - - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(*canonicality == ECDSACanonicality::FullyCanonical); - } - - { - auto const canonicality = ecdsaCanonicality(makeSlice(non)); - BEAST_EXPECT(canonicality); - - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - BEAST_EXPECT(*canonicality != ECDSACanonicality::FullyCanonical); - } - + BEAST_EXPECT(ecdsaCanonicality(makeSlice(sig)) == ECDSACanonicality::FullyCanonical); + BEAST_EXPECT(ecdsaCanonicality(makeSlice(non)) == ECDSACanonicality::Canonical); BEAST_EXPECT(verifyDigest(pk, digest, makeSlice(sig), false)); BEAST_EXPECT(verifyDigest(pk, digest, makeSlice(sig), true)); BEAST_EXPECT(verifyDigest(pk, digest, makeSlice(non), false)); diff --git a/src/test/rpc/BookChanges_test.cpp b/src/test/rpc/BookChanges_test.cpp index f0b4a4e1878..42ee569ff31 100644 --- a/src/test/rpc/BookChanges_test.cpp +++ b/src/test/rpc/BookChanges_test.cpp @@ -224,6 +224,8 @@ class BookChanges_test : public beast::unit_test::Suite env.app().getNodeFamily()); std::uint32_t seq = 0; + uint256 key{}; + for (auto const& [gets, pays] : crossings) { ++seq; @@ -257,7 +259,7 @@ class BookChanges_test : public beast::unit_test::Suite auto metaSerializer = std::make_shared(); metadata->add(*metaSerializer); - ledger->rawTxInsert(uint256{seq}, txSerializer, metaSerializer); + ledger->rawTxInsert(++key, txSerializer, metaSerializer); } ledger->setImmutable(); diff --git a/src/test/rpc/Feature_test.cpp b/src/test/rpc/Feature_test.cpp index 1e2504bf7f8..20e76ff8e81 100644 --- a/src/test/rpc/Feature_test.cpp +++ b/src/test/rpc/Feature_test.cpp @@ -121,7 +121,7 @@ class Feature_test : public beast::unit_test::Suite } // Test an arbitrary unknown feature - uint256 const zero{0}; + uint256 const zero{}; BEAST_EXPECT(featureToName(zero) == to_string(zero)); BEAST_EXPECT( featureToName(zero) == @@ -292,7 +292,8 @@ class Feature_test : public beast::unit_test::Suite for (auto it = result[jss::features].begin(); it != result[jss::features].end(); ++it) { uint256 id; - (void)id.parseHex(it.key().asString().c_str()); + if (!BEAST_EXPECT(id.parseHex(it.key().asString()))) + return; if (!BEAST_EXPECT((*it).isMember(jss::name))) return; bool const expectEnabled = env.app().getAmendmentTable().isEnabled(id); @@ -353,7 +354,8 @@ class Feature_test : public beast::unit_test::Suite for (auto it = jrr[jss::features].begin(); it != jrr[jss::features].end(); ++it) { uint256 id; - (void)id.parseHex(it.key().asString().c_str()); + if (!BEAST_EXPECT(id.parseHex(it.key().asString()))) + return; if (!BEAST_EXPECT((*it).isMember(jss::name))) return; bool const expectEnabled = env.app().getAmendmentTable().isEnabled(id); diff --git a/src/test/rpc/GetAggregatePrice_test.cpp b/src/test/rpc/GetAggregatePrice_test.cpp index 58c2e8b9962..3bd39148272 100644 --- a/src/test/rpc/GetAggregatePrice_test.cpp +++ b/src/test/rpc/GetAggregatePrice_test.cpp @@ -413,7 +413,7 @@ class GetAggregatePrice_test : public beast::unit_test::Suite // any ledger. sfPreviousTxnLgrSeq still points to a valid closed // ledger, so getLedgerBySeq succeeds but txRead returns null. auto const oracleKeylet = keylet::oracle(owner, oracle.documentID()); - uint256 const bogusTxnID{0xABCABCAB}; + auto const bogusTxnID = uint256{0xABCABCAB}; bool const modified = env.app().getOpenLedger().modify( [&oracleKeylet, &bogusTxnID](OpenView& view, beast::Journal) -> bool { auto const sle = view.read(oracleKeylet); diff --git a/src/test/rpc/GetCounts_test.cpp b/src/test/rpc/GetCounts_test.cpp index 3b1fc59af56..fe4967d82e5 100644 --- a/src/test/rpc/GetCounts_test.cpp +++ b/src/test/rpc/GetCounts_test.cpp @@ -9,6 +9,7 @@ #include #include +#include #include namespace xrpl { @@ -52,36 +53,59 @@ class GetCounts_test : public beast::unit_test::Suite // check counts, default params result = env.rpc("get_counts")[jss::result]; BEAST_EXPECT(result[jss::status] == "success"); - // compare with values reported by CountedObjects - auto const& objectCounts = CountedObjects::getInstance().getCounts(10); - for (auto const& it : objectCounts) + + // compare with values reported by the registry + auto const& counters = result[jss::counters]; + + for (auto const& c : gCountedObjects) { - BEAST_EXPECTS(result.isMember(it.first), it.first); - BEAST_EXPECTS(result[it.first].asInt() == it.second, it.first); + if (auto const count = c.count(); count >= 10) + { + BEAST_EXPECTS(counters.isMember(c.name()), c.name()); + BEAST_EXPECTS(counters[c.name()][jss::current].asUInt() == count, c.name()); + } } + BEAST_EXPECT(!result.isMember(jss::local_txs)); } - { // make request with min threshold 100 and verify - // that only STObject and NodeObject are reported + // that only counters at or above the threshold are reported result = env.rpc("get_counts", "100")[jss::result]; BEAST_EXPECT(result[jss::status] == "success"); - // compare with values reported by CountedObjects - auto const& objectCounts = CountedObjects::getInstance().getCounts(100); - for (auto const& it : objectCounts) + auto const& counters = result[jss::counters]; + + // every registry counter at/above threshold must be reported, with + // a matching current value + for (auto const& c : gCountedObjects) { - BEAST_EXPECTS(result.isMember(it.first), it.first); - BEAST_EXPECTS(result[it.first].asInt() == it.second, it.first); + if (auto const count = c.count(); count >= 100) + { + BEAST_EXPECTS(counters.isMember(c.name()), c.name()); + BEAST_EXPECTS(counters[c.name()][jss::current].asUInt() == count, c.name()); + } } - BEAST_EXPECT(!result.isMember("Transaction")); - BEAST_EXPECT(!result.isMember("STTx")); - BEAST_EXPECT(!result.isMember("STArray")); - BEAST_EXPECT(!result.isMember("HashRouterEntry")); - BEAST_EXPECT(!result.isMember("STLedgerEntry")); - } + // conversely, every reported entry must be a known counter that met + // the threshold, and its maximum must be consistent + for (auto const& name : counters.getMemberNames()) + { + auto const it = std::ranges::find_if( + gCountedObjects, [&](auto const& c) { return c.name() == name; }); + BEAST_EXPECTS(it != gCountedObjects.end(), name); + + auto const& entry = counters[name]; + BEAST_EXPECTS(entry[jss::current].asUInt() >= 100, name); + BEAST_EXPECTS(entry[jss::maximum].asUInt() >= entry[jss::current].asUInt(), name); + } + + BEAST_EXPECT(!counters.isMember("xrpl::Transaction")); + BEAST_EXPECT(!counters.isMember("xrpl::STTx")); + BEAST_EXPECT(!counters.isMember("xrpl::STArray")); + BEAST_EXPECT(!counters.isMember("xrpl::HashRouterEntry")); + BEAST_EXPECT(!counters.isMember("xrpl::STLedgerEntry")); + } { // local_txs field will exist when there are open Txs env(pay(alice, bob, alice["USD"](5))); diff --git a/src/test/rpc/LedgerEntry_test.cpp b/src/test/rpc/LedgerEntry_test.cpp index 24dde05ce13..b3a8bbd4980 100644 --- a/src/test/rpc/LedgerEntry_test.cpp +++ b/src/test/rpc/LedgerEntry_test.cpp @@ -670,23 +670,16 @@ class LedgerEntry_test : public beast::unit_test::Suite auto const sle = std::make_shared(keylet); // Create Amendments vector (enabled amendments) - std::vector enabledAmendments; - enabledAmendments.push_back( - uint256::fromVoid( - "42426C4D4F1009EE67080A9B7965B44656D7" - "714D104A72F9B4369F97ABF044EE")); - enabledAmendments.push_back( - uint256::fromVoid( - "4C97EBA926031A7CF7D7B36FDE3ED66DDA54" - "21192D63DE53FFB46E43B9DC8373")); - enabledAmendments.push_back( - uint256::fromVoid( - "03BDC0099C4E14163ADA272C1B6F6FABB448" - "CC3E51F522F978041E4B57D9158C")); - enabledAmendments.push_back( - uint256::fromVoid( - "35291ADD2D79EB6991343BDA0912269C817D" - "0F094B02226C1C14AD2858962ED4")); + std::vector enabledAmendments{ + uint256( + "42426C4D4F1009EE67080A9B7965B44656D7714D104A72F9B4369F97ABF044EE"), + uint256( + "4C97EBA926031A7CF7D7B36FDE3ED66DDA5421192D63DE53FFB46E43B9DC8373"), + uint256( + "03BDC0099C4E14163ADA272C1B6F6FABB448CC3E51F522F978041E4B57D9158C"), + uint256( + "35291ADD2D79EB6991343BDA0912269C817D0F094B02226C1C14AD2858962ED4") + }; sle->setFieldV256(sfAmendments, STVector256(enabledAmendments)); // Create Majorities array @@ -695,18 +688,16 @@ class LedgerEntry_test : public beast::unit_test::Suite auto majority1 = STObject::makeInnerObject(sfMajority); majority1.setFieldH256( sfAmendment, - uint256::fromVoid( - "7BB62DC13EC72B775091E9C71BF8CF97E122" - "647693B50C5E87A80DFD6FCFAC50")); + uint256( + "7BB62DC13EC72B775091E9C71BF8CF97E122647693B50C5E87A80DFD6FCFAC50")); majority1.setFieldU32(sfCloseTime, 779561310); majorities.pushBack(std::move(majority1)); auto majority2 = STObject::makeInnerObject(sfMajority); majority2.setFieldH256( sfAmendment, - uint256::fromVoid( - "755C971C29971C9F20C6F080F2ED96F87884" - "E40AD19554A5EBECDCEC8A1F77FE")); + uint256( + "755C971C29971C9F20C6F080F2ED96F87884E40AD19554A5EBECDCEC8A1F77FE")); majority2.setFieldU32(sfCloseTime, 779561310); majorities.pushBack(std::move(majority2)); @@ -1599,8 +1590,7 @@ class LedgerEntry_test : public beast::unit_test::Suite STArray disabledValidators; auto disabledValidator = STObject::makeInnerObject(sfDisabledValidator); auto pubKeyBlob = strUnHex( - "ED58F6770DB5DD77E59D28CB650EC3816E2FC95021BB56E720C9A1" - "2DA79C58A3AB"); + "ED58F6770DB5DD77E59D28CB650EC3816E2FC95021BB56E720C9A12DA79C58A3AB"); disabledValidator.setFieldVL(sfPublicKey, *pubKeyBlob); disabledValidator.setFieldU32(sfFirstLedgerSequence, 91371264); disabledValidators.pushBack(std::move(disabledValidator)); @@ -1608,10 +1598,8 @@ class LedgerEntry_test : public beast::unit_test::Suite sle->setFieldArray(sfDisabledValidators, disabledValidators); sle->setFieldH256( sfPreviousTxnID, - uint256::fromVoid( - "8D47FFE664BE6C335108DF689537625855A6" - "A95160CC6D351341B9" - "2624D9C5E3")); + uint256( + "8D47FFE664BE6C335108DF689537625855A6A95160CC6D351341B92624D9C5E3")); sle->setFieldU32(sfPreviousTxnLgrSeq, 91442944); view.rawInsert(sle); @@ -2536,7 +2524,7 @@ class LedgerEntry_test : public beast::unit_test::Suite jv[jss::result].isMember(jss::node) && jv[jss::result][jss::node].isMember("Hashes") && jv[jss::result][jss::node]["Hashes"].size() == expectedCount, - to_string(jv[jss::result][jss::node]["Hashes"].size())); + std::to_string(jv[jss::result][jss::node]["Hashes"].size())); } else { @@ -2621,7 +2609,7 @@ class LedgerEntry_test : public beast::unit_test::Suite // "hashes":[incorrect index hash] { json::Value params; - auto const badKey = strHex(expectedKey.key + uint256{1}); + auto const badKey = strHex(expectedKey.key.next()); params[jss::ledger_index] = jss::validated; params[jss::hashes] = badKey; auto const jv = env.rpc("json", "ledger_entry", to_string(params)); diff --git a/src/test/rpc/Subscribe_test.cpp b/src/test/rpc/Subscribe_test.cpp index 47c2245fa52..26497d071cf 100644 --- a/src/test/rpc/Subscribe_test.cpp +++ b/src/test/rpc/Subscribe_test.cpp @@ -1577,18 +1577,15 @@ class Subscribe_test : public beast::unit_test::Suite // strings MUST be distinct for the cap arithmetic to be exact; incrementing // guarantees distinctness without deriving `count` keypairs. static std::vector - makeAccountStrings(std::size_t count, std::uint32_t seed = 1) + makeAccountStrings(std::size_t count, AccountID start = AccountID{1}) { std::vector out; out.reserve(count); - // Start at `seed` so separate calls produce non-overlapping ranges, - // letting a test subscribe disjoint batches across requests. - AccountID id{static_cast(seed)}; + // Callers choose `start` beyond any previous batch so separate calls + // produce non-overlapping ranges, letting a test subscribe disjoint + // batches across requests. for (std::size_t i = 0; i < count; ++i) - { - out.push_back(toBase58(id)); - ++id; - } + out.push_back(toBase58(start++)); return out; } @@ -1747,8 +1744,8 @@ class Subscribe_test : public beast::unit_test::Suite // accounts_proposed (3, evaluated first, would subscribe) + // accounts (3): combined 6 exceeds the cap of 5, so the request is // rejected. The proposed branch must not have leaked its 3 entries. - json::Value req = accountsProposedRequest(makeAccountStrings(3, 1)); - for (auto const& a : makeAccountStrings(3, 100)) + json::Value req = accountsProposedRequest(makeAccountStrings(3)); + for (auto const& a : makeAccountStrings(3, AccountID{100})) req[jss::accounts].append(a); { auto const jr = wsc->invoke("subscribe", req)[jss::result]; @@ -1760,7 +1757,7 @@ class Subscribe_test : public beast::unit_test::Suite // connection's count is already 3 and this 3-account request would be // rejected (3 + 3 > 5). With no leak the count is 0 and it succeeds. { - auto const r = wsc->invoke("subscribe", accountsRequest(makeAccountStrings(3, 200))); + auto const r = wsc->invoke("subscribe", accountsRequest(makeAccountStrings(3, AccountID{200}))); BEAST_EXPECTS(r[jss::status] == "success", to_string(r)); } } @@ -1877,7 +1874,7 @@ class Subscribe_test : public beast::unit_test::Suite { auto wscBulk = makeWSClient(env.app().config()); auto const r = - wscBulk->invoke("subscribe", accountsRequest(makeAccountStrings(kBulk, 10))); + wscBulk->invoke("subscribe", accountsRequest(makeAccountStrings(kBulk, AccountID{10}))); BEAST_EXPECTS(r[jss::status] == "success", to_string(r)); // Destroying the client closes the WS connection, which destroys // the server-side InfoSub and posts the chunked async cleanup job. @@ -1931,7 +1928,7 @@ class Subscribe_test : public beast::unit_test::Suite // to a bulk set so its deferred cleanup is non-trivial and races with B. { auto wscA = makeWSClient(env.app().config()); - auto bulk = makeAccountStrings(2000, 10); + auto bulk = makeAccountStrings(2000, AccountID{10}); bulk.push_back(alice.human()); auto const r = wscA->invoke("subscribe", accountsRequest(bulk)); BEAST_EXPECTS(r[jss::status] == "success", to_string(r)); diff --git a/src/tests/libxrpl/basics/base_uint.cpp b/src/tests/libxrpl/basics/base_uint.cpp index 47838202052..6ab88528bae 100644 --- a/src/tests/libxrpl/basics/base_uint.cpp +++ b/src/tests/libxrpl/basics/base_uint.cpp @@ -9,12 +9,15 @@ #include #include +#include #include #include #include #include #include #include +#include +#include #include #include #include @@ -54,75 +57,67 @@ struct Nonhash struct BaseUintTest : public ::testing::Test { using BaseUInt96 = BaseUInt<96>; - static_assert(std::is_copy_constructible_v); - static_assert(std::is_copy_assignable_v); +private: + using HexPair = std::pair; + + template static void - testComparisons() + testComparisons(std::span cases) { - using HexPair = std::pair; + auto checkedLoad = [](xrpl::BaseUInt& value, std::string_view str) { + auto const success = value.parseHex(str); + EXPECT_TRUE(success) << str; + return success; + }; + for (auto const& [smallerText, largerText] : cases) { - static constexpr auto kTestArgs = std::to_array({ - {"0000000000000000", "0000000000000001"}, - {"0000000000000000", "ffffffffffffffff"}, - {"1234567812345678", "2345678923456789"}, - {"8000000000000000", "8000000000000001"}, - {"aaaaaaaaaaaaaaa9", "aaaaaaaaaaaaaaaa"}, - {"fffffffffffffffe", "ffffffffffffffff"}, - }); - - for (auto const& [smallerText, largerText] : kTestArgs) - { - xrpl::BaseUInt<64> const smaller{smallerText}, larger{largerText}; - // For code readability, we want to use general boolean - // expectations instead of specific EXPECT_LT etc. - EXPECT_TRUE(smaller < larger); - EXPECT_TRUE(smaller <= larger); - EXPECT_TRUE(smaller != larger); - EXPECT_FALSE(smaller == larger); - EXPECT_FALSE(smaller > larger); - EXPECT_FALSE(smaller >= larger); - EXPECT_FALSE(larger < smaller); - EXPECT_FALSE(larger <= smaller); - EXPECT_TRUE(larger != smaller); - EXPECT_FALSE(larger == smaller); - EXPECT_TRUE(larger > smaller); - EXPECT_TRUE(larger >= smaller); - EXPECT_TRUE(smaller == smaller); - EXPECT_TRUE(larger == larger); - } + xrpl::BaseUInt smaller, larger; + + if (!checkedLoad(smaller, smallerText) || !checkedLoad(larger, largerText)) + continue; + + // For code readability, we want to use general boolean + // expectations instead of specific EXPECT_LT etc. + EXPECT_TRUE(smaller < larger); + EXPECT_TRUE(smaller <= larger); + EXPECT_TRUE(smaller != larger); + EXPECT_FALSE(smaller == larger); + EXPECT_FALSE(smaller > larger); + EXPECT_FALSE(smaller >= larger); + EXPECT_FALSE(larger < smaller); + EXPECT_FALSE(larger <= smaller); + EXPECT_TRUE(larger != smaller); + EXPECT_FALSE(larger == smaller); + EXPECT_TRUE(larger > smaller); + EXPECT_TRUE(larger >= smaller); + EXPECT_TRUE(smaller == smaller); + EXPECT_TRUE(larger == larger); } + } - { - static constexpr auto kTestArgs = std::to_array({ - {"000000000000000000000000", "000000000000000000000001"}, - {"000000000000000000000000", "ffffffffffffffffffffffff"}, - {"0123456789ab0123456789ab", "123456789abc123456789abc"}, - {"555555555555555555555555", "55555555555a555555555555"}, - {"aaaaaaaaaaaaaaa9aaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaa"}, - {"fffffffffffffffffffffffe", "ffffffffffffffffffffffff"}, - }); - - for (auto const& [smallerText, largerText] : kTestArgs) - { - xrpl::BaseUInt<96> const smaller{smallerText}, larger{largerText}; - EXPECT_TRUE(smaller < larger); - EXPECT_TRUE(smaller <= larger); - EXPECT_TRUE(smaller != larger); - EXPECT_FALSE(smaller == larger); - EXPECT_FALSE(smaller > larger); - EXPECT_FALSE(smaller >= larger); - EXPECT_FALSE(larger < smaller); - EXPECT_FALSE(larger <= smaller); - EXPECT_TRUE(larger != smaller); - EXPECT_FALSE(larger == smaller); - EXPECT_TRUE(larger > smaller); - EXPECT_TRUE(larger >= smaller); - EXPECT_TRUE(smaller == smaller); - EXPECT_TRUE(larger == larger); - } - } +public: + static void + testComparisons() + { + testComparisons<64>(std::to_array({ + {"0000000000000000", "0000000000000001"}, + {"0000000000000000", "ffffffffffffffff"}, + {"1234567812345678", "2345678923456789"}, + {"8000000000000000", "8000000000000001"}, + {"aaaaaaaaaaaaaaa9", "aaaaaaaaaaaaaaaa"}, + {"fffffffffffffffe", "ffffffffffffffff"}, + })); + + testComparisons<96>(std::to_array({ + {"000000000000000000000000", "000000000000000000000001"}, + {"000000000000000000000000", "ffffffffffffffffffffffff"}, + {"0123456789ab0123456789ab", "123456789abc123456789abc"}, + {"555555555555555555555555", "55555555555a555555555555"}, + {"aaaaaaaaaaaaaaa9aaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaa"}, + {"fffffffffffffffffffffffe", "ffffffffffffffffffffffff"}, + })); } }; @@ -130,67 +125,24 @@ using BaseUintDeathTest = BaseUintTest; TEST_F(BaseUintDeathTest, from_raw_size_mismatch) { - // ENABLE_VOIDSTAR is a debug build, but does not crash on failed asserts. Rather than twist - // these tests into knots to make them work, just skip them. -#ifdef ENABLE_VOIDSTAR - GTEST_SKIP() << "ENABLE_VOIDSTAR is a debug build, but does not crash on failed asserts."; -#else - auto smallConstruct = [] { - // Container smaller than the base_uint (8 bytes vs 12 bytes for - // test96). Only the first 8 bytes are copied; the remaining 4 bytes - // stay zero. - Blob const tooSmall{1, 2, 3, 4, 5, 6, 7, 8}; - BaseUInt96 const result = BaseUInt96::fromRaw(tooSmall); - auto const resultText = to_string(result); - EXPECT_EQ(resultText, "010203040506070800000000") << resultText; - }; - EXPECT_DEBUG_DEATH(smallConstruct(), "input size match"); - - auto largeConstruct = [] { - // Container larger than the base_uint (16 bytes vs 12 bytes for - // test96). Only the first 12 bytes are copied; the extra bytes are - // ignored. - Blob const tooBig{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; - BaseUInt96 const result = BaseUInt96::fromRaw(tooBig); - auto const resultText = to_string(result); - EXPECT_EQ(resultText, "0102030405060708090A0B0C") << resultText; - }; - EXPECT_DEBUG_DEATH(largeConstruct(), "input size match"); - - auto smallCopy = [] { - // Container smaller than the base_uint (8 bytes vs 12 bytes for - // test96). Only the first 8 bytes are copied; the remaining 4 bytes - // stay zero. - Blob const tooSmall{1, 2, 3, 4, 5, 6, 7, 8}; - BaseUInt96 result{}; - --result; - { - auto const originalText = to_string(result); - EXPECT_EQ(originalText, "FFFFFFFFFFFFFFFFFFFFFFFF") << originalText; - } - result = tooSmall; - auto const resultText = to_string(result); - EXPECT_EQ(resultText, "010203040506070800000000") << resultText; - }; - EXPECT_DEBUG_DEATH(smallCopy(), "input size match"); - - auto const largeCopy = [] { - // Container larger than the base_uint (16 bytes vs 12 bytes for - // test96). Only the first 12 bytes are copied; the extra bytes are - // ignored. - Blob const tooBig{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; - BaseUInt96 result{}; - --result; + // High-bit bytes throughout so that a sign-extension mistake on a + // char-typed source would be visible. + static constexpr std::array kBytes = { + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B}; + + for (std::size_t i = 0; i <= kBytes.size(); ++i) + { + auto const input = std::span{kBytes}.first(i); + auto const val = BaseUInt96::fromRaw(input); + + EXPECT_EQ(val.has_value(), i == BaseUInt96::size()) << i; + + if (val) { - auto const originalText = to_string(result); - EXPECT_EQ(originalText, "FFFFFFFFFFFFFFFFFFFFFFFF") << originalText; + EXPECT_TRUE(std::ranges::equal(*val, input)); + EXPECT_EQ(BaseUInt96::fromRaw(std::string{input.begin(), input.end()}), val); } - result = tooBig; - auto const resultText = to_string(result); - EXPECT_EQ(resultText, "0102030405060708090A0B0C") << resultText; - }; - EXPECT_DEBUG_DEATH(largeCopy(), "input size match"); -#endif + } } TEST_F(BaseUintTest, base_uint) @@ -203,12 +155,10 @@ TEST_F(BaseUintTest, base_uint) // used to verify set insertion (hashing required) std::unordered_set> uset; - Blob const raw{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; - EXPECT_EQ(BaseUInt96::kBytes, raw.size()); + BaseUInt96 const ascending{ + std::to_array({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})}; - BaseUInt96 ascending = BaseUInt96::fromRaw(raw); uset.insert(ascending); - EXPECT_EQ(raw.size(), ascending.size()); EXPECT_EQ(to_string(ascending), "0102030405060708090A0B0C"); EXPECT_EQ(toShortString(ascending), "01020304..."); EXPECT_EQ(*ascending.data(), 1); @@ -225,9 +175,9 @@ TEST_F(BaseUintTest, base_uint) // back into another base_uint (rehashed) for comparison with the original Nonhash<96> hasher{}; hash_append(hasher, ascending); - BaseUInt96 const rehashed = - BaseUInt96::fromRaw(std::vector(hasher.data.begin(), hasher.data.end())); - EXPECT_EQ(rehashed, ascending); + + // Exercises the fixed-extent constructor from a std::array. + EXPECT_EQ(BaseUInt96{hasher.data}, ascending); BaseUInt96 complement{~ascending}; uset.insert(complement); @@ -271,14 +221,11 @@ TEST_F(BaseUintTest, base_uint) BaseUInt96 const bracedZero{}; EXPECT_EQ(bracedZero, zero) << to_string(bracedZero); - - BaseUInt96 const zeroFromUInt{0u}; - EXPECT_EQ(zeroFromUInt, zero) << to_string(zeroFromUInt); } BaseUInt96 counter{zero}; counter++; - EXPECT_EQ(counter, BaseUInt96(1)); + EXPECT_EQ(counter, zero.next()); counter--; EXPECT_EQ(counter, beast::kZero); EXPECT_EQ(counter, zero); @@ -341,65 +288,57 @@ TEST_F(BaseUintTest, base_uint) EXPECT_EQ(to_string(parsed), s1); } - // Constexpr constructors + // Compile-time construction + static_assert(BaseUInt96{}.signum() == 0); + static_assert(BaseUInt96{"0"}.signum() == 0); + static_assert(BaseUInt96{"000000000000000000000000"}.signum() == 0); + static_assert(BaseUInt96{"000000000000000000000001"}.signum() == 1); + static_assert(BaseUInt96{"800000000000000000000000"}.signum() == 1); + + // The string_view constructor is consteval, so malformed input is a + // compile-time error rather than an exception. The runtime parser + // rejects the same inputs and leaves the object untouched. { - static_assert(BaseUInt96{}.signum() == 0); - static_assert(BaseUInt96("0").signum() == 0); - static_assert(BaseUInt96("000000000000000000000000").signum() == 0); - static_assert(BaseUInt96("000000000000000000000001").signum() == 1); - static_assert(BaseUInt96("800000000000000000000000").signum() == 1); - - // Using the constexpr constructor in a non-constexpr context - // with an error in the parsing throws an exception. - { - // Invalid length for string. The vector keeps this out of a constant - // expression, so the constructor throws instead of failing to compile. - auto tooShort = [] { - std::vector const str(23, '7'); - std::string_view const sView(str.data(), str.size()); - [[maybe_unused]] BaseUInt96 const t96(sView); - }; - EXPECT_THAT( - tooShort, - ::testing::ThrowsMessage("invalid length for hex string")); - } - { - // Invalid character in string. - auto badCharacter = [] { - std::vector str(23, '7'); - str.push_back('G'); - std::string_view const sView(str.data(), str.size()); - [[maybe_unused]] BaseUInt96 const t96(sView); - }; - EXPECT_THAT( - badCharacter, ::testing::ThrowsMessage("invalid hex character")); - } + std::string const tooShort(23, '7'); + BaseUInt96 t96; + EXPECT_FALSE(t96.parseHex(tooShort)); + EXPECT_EQ(t96, BaseUInt96{}); + } + { + std::string badCharacter(23, '7'); + badCharacter.push_back('G'); + BaseUInt96 t96; + EXPECT_FALSE(t96.parseHex(badCharacter)); + EXPECT_EQ(t96, BaseUInt96{}); + } - // Verify that constexpr base_uints interpret a string the same + { + // Verify that consteval construction interprets a string the same // way parseHex() does. struct StrBaseUInt { - char const* const str; - BaseUInt96 tst; + std::string_view str; + BaseUInt96 value; - constexpr StrBaseUInt(char const* s) : str(s), tst(s) + consteval StrBaseUInt(char const* s) : str(s), value(str) { } }; - constexpr auto kTestCases = std::to_array({ + + constexpr StrBaseUInt kTestCases[] = { "000000000000000000000000", "000000000000000000000001", "fedcba9876543210ABCDEF91", "19FEDCBA0123456789abcdef", "800000000000000000000000", "fFfFfFfFfFfFfFfFfFfFfFfF", - }); + }; - for (StrBaseUInt const& expectedByte : kTestCases) + for (StrBaseUInt const& testCase : kTestCases) { BaseUInt96 t96; - EXPECT_TRUE(t96.parseHex(expectedByte.str)); - EXPECT_EQ(t96, expectedByte.tst); + EXPECT_TRUE(t96.parseHex(testCase.str)) << testCase.str; + EXPECT_EQ(t96, testCase.value); } } } diff --git a/src/tests/libxrpl/consensus/Validations.cpp b/src/tests/libxrpl/consensus/Validations.cpp index 56964f56a64..486802e0a0d 100644 --- a/src/tests/libxrpl/consensus/Validations.cpp +++ b/src/tests/libxrpl/consensus/Validations.cpp @@ -857,7 +857,7 @@ TEST(ValidationsTest, acquire_validated_ledger) // Validation is available EXPECT_TRUE(harness.vals().numTrustedForLedger(ID{2}) == 1); // but ledger based data is not - EXPECT_TRUE(harness.vals().getNodesAfter(kGenesisLedger, ID{0}) == 0); + EXPECT_TRUE(harness.vals().getNodesAfter(kGenesisLedger, ID{}) == 0); // Initial preferred branch falls back to the ledger we are trying to // acquire EXPECT_TRUE(harness.vals().getPreferred(kGenesisLedger) == std::make_pair(Seq{2}, ID{2})); @@ -870,7 +870,7 @@ TEST(ValidationsTest, acquire_validated_ledger) // Create the ledger Ledger const ledgerAB = h["ab"]; // Now it should be available - EXPECT_TRUE(harness.vals().getNodesAfter(kGenesisLedger, ID{0}) == 1); + EXPECT_TRUE(harness.vals().getNodesAfter(kGenesisLedger, ID{}) == 1); // Create a validation that is not available harness.clock().advance(5s); diff --git a/src/tests/libxrpl/csf/Peer.h b/src/tests/libxrpl/csf/Peer.h index d4b6f42bdb9..f7554c5f9ab 100644 --- a/src/tests/libxrpl/csf/Peer.h +++ b/src/tests/libxrpl/csf/Peer.h @@ -946,7 +946,7 @@ struct Peer // In the future, consider taking peer dominant ledger if no validations // yet Ledger::ID bestLCL = validations.getPreferred(lastClosedLedger, earliestAllowedSeq()); - if (bestLCL == Ledger::ID{0}) + if (bestLCL == Ledger::ID{}) bestLCL = lastClosedLedger.id(); issue(StartRound{.bestLedger = bestLCL, .prevLedger = lastClosedLedger}); diff --git a/src/tests/libxrpl/csf/Validation.h b/src/tests/libxrpl/csf/Validation.h index 4325f965119..75192561ee2 100644 --- a/src/tests/libxrpl/csf/Validation.h +++ b/src/tests/libxrpl/csf/Validation.h @@ -30,7 +30,7 @@ using PeerKey = std::pair; */ class Validation { - Ledger::ID ledgerID_{0}; + Ledger::ID ledgerID_{}; Ledger::Seq seq_{0}; NetClock::time_point signTime_; diff --git a/src/tests/libxrpl/csf/ledgers.h b/src/tests/libxrpl/csf/ledgers.h index ca4e44d5a66..e527b177998 100644 --- a/src/tests/libxrpl/csf/ledgers.h +++ b/src/tests/libxrpl/csf/ledgers.h @@ -88,7 +88,7 @@ class Ledger /** * Parent ledger id */ - ID parentID{0}; + ID parentID{}; /** * Parent ledger close time diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/AMMTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/AMMTests.cpp index 0bb80c41e66..d998d76d66a 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/AMMTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/AMMTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(AMMTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const accountValue = canonical_ACCOUNT(); auto const tradingFeeValue = canonical_UINT16(); @@ -135,7 +135,7 @@ TEST(AMMTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(AMMTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const accountValue = canonical_ACCOUNT(); auto const tradingFeeValue = canonical_UINT16(); @@ -292,7 +292,7 @@ TEST(AMMTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(AMMTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -311,7 +311,7 @@ TEST(AMMTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(AMMTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -328,7 +328,7 @@ TEST(AMMTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(AMMTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const accountValue = canonical_ACCOUNT(); auto const lPTokenBalanceValue = canonical_AMOUNT(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/AccountRootTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/AccountRootTests.cpp index 17c80899f99..22064eb9386 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/AccountRootTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/AccountRootTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(AccountRootTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const accountValue = canonical_ACCOUNT(); auto const sequenceValue = canonical_UINT32(); @@ -293,7 +293,7 @@ TEST(AccountRootTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(AccountRootTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const accountValue = canonical_ACCOUNT(); auto const sequenceValue = canonical_UINT32(); @@ -687,7 +687,7 @@ TEST(AccountRootTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(AccountRootTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -706,7 +706,7 @@ TEST(AccountRootTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(AccountRootTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -723,7 +723,7 @@ TEST(AccountRootTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(AccountRootTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const accountValue = canonical_ACCOUNT(); auto const sequenceValue = canonical_UINT32(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/AmendmentsTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/AmendmentsTests.cpp index 17da755a3d6..5dcd4c19c66 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/AmendmentsTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/AmendmentsTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(AmendmentsTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const amendmentsValue = canonical_VECTOR256(); auto const majoritiesValue = canonical_ARRAY(); @@ -85,7 +85,7 @@ TEST(AmendmentsTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(AmendmentsTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const amendmentsValue = canonical_VECTOR256(); auto const majoritiesValue = canonical_ARRAY(); @@ -167,7 +167,7 @@ TEST(AmendmentsTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(AmendmentsTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -186,7 +186,7 @@ TEST(AmendmentsTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(AmendmentsTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -203,7 +203,7 @@ TEST(AmendmentsTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(AmendmentsTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; AmendmentsBuilder builder{ diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/BridgeTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/BridgeTests.cpp index 1d4cfc00b9a..5a91bfe7f48 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/BridgeTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/BridgeTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(BridgeTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const accountValue = canonical_ACCOUNT(); auto const signatureRewardValue = canonical_AMOUNT(); @@ -127,7 +127,7 @@ TEST(BridgeTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(BridgeTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const accountValue = canonical_ACCOUNT(); auto const signatureRewardValue = canonical_AMOUNT(); @@ -272,7 +272,7 @@ TEST(BridgeTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(BridgeTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -291,7 +291,7 @@ TEST(BridgeTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(BridgeTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -308,7 +308,7 @@ TEST(BridgeTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(BridgeTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const accountValue = canonical_ACCOUNT(); auto const signatureRewardValue = canonical_AMOUNT(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/CheckTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/CheckTests.cpp index f14b4fb4e49..132f795f196 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/CheckTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/CheckTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(CheckTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const accountValue = canonical_ACCOUNT(); auto const destinationValue = canonical_ACCOUNT(); @@ -149,7 +149,7 @@ TEST(CheckTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(CheckTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const accountValue = canonical_ACCOUNT(); auto const destinationValue = canonical_ACCOUNT(); @@ -327,7 +327,7 @@ TEST(CheckTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(CheckTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -346,7 +346,7 @@ TEST(CheckTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(CheckTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -363,7 +363,7 @@ TEST(CheckTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(CheckTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const accountValue = canonical_ACCOUNT(); auto const destinationValue = canonical_ACCOUNT(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/CredentialTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/CredentialTests.cpp index 4fbf6d2a26f..1d3796afe3f 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/CredentialTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/CredentialTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(CredentialTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const subjectValue = canonical_ACCOUNT(); auto const issuerValue = canonical_ACCOUNT(); @@ -123,7 +123,7 @@ TEST(CredentialTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(CredentialTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const subjectValue = canonical_ACCOUNT(); auto const issuerValue = canonical_ACCOUNT(); @@ -262,7 +262,7 @@ TEST(CredentialTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(CredentialTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -281,7 +281,7 @@ TEST(CredentialTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(CredentialTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -298,7 +298,7 @@ TEST(CredentialTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(CredentialTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const subjectValue = canonical_ACCOUNT(); auto const issuerValue = canonical_ACCOUNT(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/DIDTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/DIDTests.cpp index 29d49a47f29..0dfcbf6a666 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/DIDTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/DIDTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(DIDTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const accountValue = canonical_ACCOUNT(); auto const dIDDocumentValue = canonical_VL(); @@ -107,7 +107,7 @@ TEST(DIDTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(DIDTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const accountValue = canonical_ACCOUNT(); auto const dIDDocumentValue = canonical_VL(); @@ -222,7 +222,7 @@ TEST(DIDTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(DIDTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -241,7 +241,7 @@ TEST(DIDTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(DIDTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -258,7 +258,7 @@ TEST(DIDTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(DIDTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const accountValue = canonical_ACCOUNT(); auto const ownerNodeValue = canonical_UINT64(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/DelegateTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/DelegateTests.cpp index 24c7c97d7f0..034c0724269 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/DelegateTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/DelegateTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(DelegateTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const accountValue = canonical_ACCOUNT(); auto const authorizeValue = canonical_ACCOUNT(); @@ -103,7 +103,7 @@ TEST(DelegateTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(DelegateTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const accountValue = canonical_ACCOUNT(); auto const authorizeValue = canonical_ACCOUNT(); @@ -212,7 +212,7 @@ TEST(DelegateTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(DelegateTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -231,7 +231,7 @@ TEST(DelegateTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(DelegateTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -248,7 +248,7 @@ TEST(DelegateTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(DelegateTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const accountValue = canonical_ACCOUNT(); auto const authorizeValue = canonical_ACCOUNT(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/DepositPreauthTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/DepositPreauthTests.cpp index 9e6144d5b9c..d456d8005f6 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/DepositPreauthTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/DepositPreauthTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(DepositPreauthTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const accountValue = canonical_ACCOUNT(); auto const authorizeValue = canonical_ACCOUNT(); @@ -97,7 +97,7 @@ TEST(DepositPreauthTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(DepositPreauthTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const accountValue = canonical_ACCOUNT(); auto const authorizeValue = canonical_ACCOUNT(); @@ -197,7 +197,7 @@ TEST(DepositPreauthTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(DepositPreauthTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -216,7 +216,7 @@ TEST(DepositPreauthTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(DepositPreauthTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -233,7 +233,7 @@ TEST(DepositPreauthTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(DepositPreauthTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const accountValue = canonical_ACCOUNT(); auto const ownerNodeValue = canonical_UINT64(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/DirectoryNodeTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/DirectoryNodeTests.cpp index af617c2634b..d6d51c495f4 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/DirectoryNodeTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/DirectoryNodeTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(DirectoryNodeTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const ownerValue = canonical_ACCOUNT(); auto const takerPaysCurrencyValue = canonical_UINT160(); @@ -201,7 +201,7 @@ TEST(DirectoryNodeTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(DirectoryNodeTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const ownerValue = canonical_ACCOUNT(); auto const takerPaysCurrencyValue = canonical_UINT160(); @@ -457,7 +457,7 @@ TEST(DirectoryNodeTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(DirectoryNodeTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -476,7 +476,7 @@ TEST(DirectoryNodeTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(DirectoryNodeTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -493,7 +493,7 @@ TEST(DirectoryNodeTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(DirectoryNodeTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const indexesValue = canonical_VECTOR256(); auto const rootIndexValue = canonical_UINT256(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/EscrowTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/EscrowTests.cpp index ad5f88fd2fd..8a501a8419e 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/EscrowTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/EscrowTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(EscrowTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const accountValue = canonical_ACCOUNT(); auto const sequenceValue = canonical_UINT32(); @@ -203,7 +203,7 @@ TEST(EscrowTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(EscrowTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const accountValue = canonical_ACCOUNT(); auto const sequenceValue = canonical_UINT32(); @@ -462,7 +462,7 @@ TEST(EscrowTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(EscrowTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -481,7 +481,7 @@ TEST(EscrowTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(EscrowTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -498,7 +498,7 @@ TEST(EscrowTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(EscrowTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const accountValue = canonical_ACCOUNT(); auto const destinationValue = canonical_ACCOUNT(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/FeeSettingsTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/FeeSettingsTests.cpp index 92bf25398c9..a9defd8a4a2 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/FeeSettingsTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/FeeSettingsTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(FeeSettingsTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const baseFeeValue = canonical_UINT64(); auto const referenceFeeUnitsValue = canonical_UINT32(); @@ -165,7 +165,7 @@ TEST(FeeSettingsTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(FeeSettingsTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const baseFeeValue = canonical_UINT64(); auto const referenceFeeUnitsValue = canonical_UINT32(); @@ -367,7 +367,7 @@ TEST(FeeSettingsTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(FeeSettingsTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -386,7 +386,7 @@ TEST(FeeSettingsTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(FeeSettingsTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -403,7 +403,7 @@ TEST(FeeSettingsTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(FeeSettingsTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; FeeSettingsBuilder builder{ diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/LedgerHashesTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/LedgerHashesTests.cpp index 8a7ae387490..c2808f27d9d 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/LedgerHashesTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/LedgerHashesTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(LedgerHashesTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const firstLedgerSequenceValue = canonical_UINT32(); auto const lastLedgerSequenceValue = canonical_UINT32(); @@ -73,7 +73,7 @@ TEST(LedgerHashesTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(LedgerHashesTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const firstLedgerSequenceValue = canonical_UINT32(); auto const lastLedgerSequenceValue = canonical_UINT32(); @@ -137,7 +137,7 @@ TEST(LedgerHashesTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(LedgerHashesTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -156,7 +156,7 @@ TEST(LedgerHashesTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(LedgerHashesTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -173,7 +173,7 @@ TEST(LedgerHashesTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(LedgerHashesTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const hashesValue = canonical_VECTOR256(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/LoanBrokerTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/LoanBrokerTests.cpp index fba8c539d2c..6dee08d5217 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/LoanBrokerTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/LoanBrokerTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(LoanBrokerTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const previousTxnIDValue = canonical_UINT256(); auto const previousTxnLgrSeqValue = canonical_UINT32(); @@ -197,7 +197,7 @@ TEST(LoanBrokerTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(LoanBrokerTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const previousTxnIDValue = canonical_UINT256(); auto const previousTxnLgrSeqValue = canonical_UINT32(); @@ -447,7 +447,7 @@ TEST(LoanBrokerTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(LoanBrokerTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -466,7 +466,7 @@ TEST(LoanBrokerTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(LoanBrokerTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -483,7 +483,7 @@ TEST(LoanBrokerTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(LoanBrokerTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const previousTxnIDValue = canonical_UINT256(); auto const previousTxnLgrSeqValue = canonical_UINT32(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/LoanTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/LoanTests.cpp index 845a8337b99..8c3ad8f0b68 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/LoanTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/LoanTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(LoanTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const previousTxnIDValue = canonical_UINT256(); auto const previousTxnLgrSeqValue = canonical_UINT32(); @@ -295,7 +295,7 @@ TEST(LoanTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(LoanTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const previousTxnIDValue = canonical_UINT256(); auto const previousTxnLgrSeqValue = canonical_UINT32(); @@ -692,7 +692,7 @@ TEST(LoanTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(LoanTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -711,7 +711,7 @@ TEST(LoanTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(LoanTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -728,7 +728,7 @@ TEST(LoanTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(LoanTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const previousTxnIDValue = canonical_UINT256(); auto const previousTxnLgrSeqValue = canonical_UINT32(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenIssuanceTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenIssuanceTests.cpp index e8c1b645d84..9db972ac1c2 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenIssuanceTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenIssuanceTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(MPTokenIssuanceTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const issuerValue = canonical_ACCOUNT(); auto const sequenceValue = canonical_UINT32(); @@ -223,7 +223,7 @@ TEST(MPTokenIssuanceTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(MPTokenIssuanceTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const issuerValue = canonical_ACCOUNT(); auto const sequenceValue = canonical_UINT32(); @@ -512,7 +512,7 @@ TEST(MPTokenIssuanceTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(MPTokenIssuanceTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -531,7 +531,7 @@ TEST(MPTokenIssuanceTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(MPTokenIssuanceTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -548,7 +548,7 @@ TEST(MPTokenIssuanceTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(MPTokenIssuanceTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const issuerValue = canonical_ACCOUNT(); auto const sequenceValue = canonical_UINT32(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenTests.cpp index 41eff1cce77..1f796f34cf3 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(MPTokenTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const accountValue = canonical_ACCOUNT(); auto const mPTokenIssuanceIDValue = canonical_UINT192(); @@ -185,7 +185,7 @@ TEST(MPTokenTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(MPTokenTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const accountValue = canonical_ACCOUNT(); auto const mPTokenIssuanceIDValue = canonical_UINT192(); @@ -417,7 +417,7 @@ TEST(MPTokenTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(MPTokenTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -436,7 +436,7 @@ TEST(MPTokenTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(MPTokenTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -453,7 +453,7 @@ TEST(MPTokenTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(MPTokenTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const accountValue = canonical_ACCOUNT(); auto const mPTokenIssuanceIDValue = canonical_UINT192(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/NFTokenOfferTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/NFTokenOfferTests.cpp index 9d039b2c38d..f36bb86dba9 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/NFTokenOfferTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/NFTokenOfferTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(NFTokenOfferTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const ownerValue = canonical_ACCOUNT(); auto const nFTokenIDValue = canonical_UINT256(); @@ -121,7 +121,7 @@ TEST(NFTokenOfferTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(NFTokenOfferTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const ownerValue = canonical_ACCOUNT(); auto const nFTokenIDValue = canonical_UINT256(); @@ -257,7 +257,7 @@ TEST(NFTokenOfferTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(NFTokenOfferTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -276,7 +276,7 @@ TEST(NFTokenOfferTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(NFTokenOfferTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -293,7 +293,7 @@ TEST(NFTokenOfferTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(NFTokenOfferTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const ownerValue = canonical_ACCOUNT(); auto const nFTokenIDValue = canonical_UINT256(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/NFTokenPageTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/NFTokenPageTests.cpp index 59090501691..eba0d38fdfd 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/NFTokenPageTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/NFTokenPageTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(NFTokenPageTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const previousPageMinValue = canonical_UINT256(); auto const nextPageMinValue = canonical_UINT256(); @@ -89,7 +89,7 @@ TEST(NFTokenPageTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(NFTokenPageTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const previousPageMinValue = canonical_UINT256(); auto const nextPageMinValue = canonical_UINT256(); @@ -177,7 +177,7 @@ TEST(NFTokenPageTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(NFTokenPageTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -196,7 +196,7 @@ TEST(NFTokenPageTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(NFTokenPageTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -213,7 +213,7 @@ TEST(NFTokenPageTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(NFTokenPageTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const nFTokensValue = canonical_ARRAY(); auto const previousTxnIDValue = canonical_UINT256(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/NegativeUNLTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/NegativeUNLTests.cpp index 48ff060c113..206400ff3c2 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/NegativeUNLTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/NegativeUNLTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(NegativeUNLTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const disabledValidatorsValue = canonical_ARRAY(); auto const validatorToDisableValue = canonical_VL(); @@ -95,7 +95,7 @@ TEST(NegativeUNLTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(NegativeUNLTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const disabledValidatorsValue = canonical_ARRAY(); auto const validatorToDisableValue = canonical_VL(); @@ -192,7 +192,7 @@ TEST(NegativeUNLTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(NegativeUNLTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -211,7 +211,7 @@ TEST(NegativeUNLTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(NegativeUNLTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -228,7 +228,7 @@ TEST(NegativeUNLTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(NegativeUNLTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; NegativeUNLBuilder builder{ diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/OfferTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/OfferTests.cpp index 5cb1bbd804a..8b95296dc25 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/OfferTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/OfferTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(OfferTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const accountValue = canonical_ACCOUNT(); auto const sequenceValue = canonical_UINT32(); @@ -147,7 +147,7 @@ TEST(OfferTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(OfferTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const accountValue = canonical_ACCOUNT(); auto const sequenceValue = canonical_UINT32(); @@ -322,7 +322,7 @@ TEST(OfferTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(OfferTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -341,7 +341,7 @@ TEST(OfferTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(OfferTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -358,7 +358,7 @@ TEST(OfferTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(OfferTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const accountValue = canonical_ACCOUNT(); auto const sequenceValue = canonical_UINT32(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/OracleTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/OracleTests.cpp index 837fd576bf0..e40b43f2f00 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/OracleTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/OracleTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(OracleTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const ownerValue = canonical_ACCOUNT(); auto const oracleDocumentIDValue = canonical_UINT32(); @@ -129,7 +129,7 @@ TEST(OracleTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(OracleTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const ownerValue = canonical_ACCOUNT(); auto const oracleDocumentIDValue = canonical_UINT32(); @@ -277,7 +277,7 @@ TEST(OracleTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(OracleTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -296,7 +296,7 @@ TEST(OracleTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(OracleTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -313,7 +313,7 @@ TEST(OracleTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(OracleTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const ownerValue = canonical_ACCOUNT(); auto const providerValue = canonical_VL(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/PayChannelTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/PayChannelTests.cpp index 7c312523698..9eece79847d 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/PayChannelTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/PayChannelTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(PayChannelTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const accountValue = canonical_ACCOUNT(); auto const destinationValue = canonical_ACCOUNT(); @@ -177,7 +177,7 @@ TEST(PayChannelTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(PayChannelTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const accountValue = canonical_ACCOUNT(); auto const destinationValue = canonical_ACCOUNT(); @@ -397,7 +397,7 @@ TEST(PayChannelTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(PayChannelTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -416,7 +416,7 @@ TEST(PayChannelTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(PayChannelTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -433,7 +433,7 @@ TEST(PayChannelTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(PayChannelTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const accountValue = canonical_ACCOUNT(); auto const destinationValue = canonical_ACCOUNT(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/PermissionedDomainTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/PermissionedDomainTests.cpp index c448d064cf0..a00191e3858 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/PermissionedDomainTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/PermissionedDomainTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(PermissionedDomainTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const ownerValue = canonical_ACCOUNT(); auto const sequenceValue = canonical_UINT32(); @@ -93,7 +93,7 @@ TEST(PermissionedDomainTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(PermissionedDomainTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const ownerValue = canonical_ACCOUNT(); auto const sequenceValue = canonical_UINT32(); @@ -187,7 +187,7 @@ TEST(PermissionedDomainTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(PermissionedDomainTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -206,7 +206,7 @@ TEST(PermissionedDomainTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(PermissionedDomainTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/RippleStateTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/RippleStateTests.cpp index de0769793b3..c9ec8e8d700 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/RippleStateTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/RippleStateTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(RippleStateTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const balanceValue = canonical_AMOUNT(); auto const lowLimitValue = canonical_AMOUNT(); @@ -165,7 +165,7 @@ TEST(RippleStateTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(RippleStateTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const balanceValue = canonical_AMOUNT(); auto const lowLimitValue = canonical_AMOUNT(); @@ -367,7 +367,7 @@ TEST(RippleStateTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(RippleStateTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -386,7 +386,7 @@ TEST(RippleStateTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(RippleStateTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -403,7 +403,7 @@ TEST(RippleStateTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(RippleStateTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const balanceValue = canonical_AMOUNT(); auto const lowLimitValue = canonical_AMOUNT(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/SignerListTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/SignerListTests.cpp index 5ec2c4e10db..d74a9036bac 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/SignerListTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/SignerListTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(SignerListTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const ownerValue = canonical_ACCOUNT(); auto const ownerNodeValue = canonical_UINT64(); @@ -103,7 +103,7 @@ TEST(SignerListTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(SignerListTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const ownerValue = canonical_ACCOUNT(); auto const ownerNodeValue = canonical_UINT64(); @@ -212,7 +212,7 @@ TEST(SignerListTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(SignerListTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -231,7 +231,7 @@ TEST(SignerListTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(SignerListTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -248,7 +248,7 @@ TEST(SignerListTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(SignerListTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const ownerNodeValue = canonical_UINT64(); auto const signerQuorumValue = canonical_UINT32(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/SponsorshipTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/SponsorshipTests.cpp index e1d9ff15b98..2d9806e5a18 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/SponsorshipTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/SponsorshipTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(SponsorshipTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const previousTxnIDValue = canonical_UINT256(); auto const previousTxnLgrSeqValue = canonical_UINT32(); @@ -123,7 +123,7 @@ TEST(SponsorshipTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(SponsorshipTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const previousTxnIDValue = canonical_UINT256(); auto const previousTxnLgrSeqValue = canonical_UINT32(); @@ -262,7 +262,7 @@ TEST(SponsorshipTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(SponsorshipTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -281,7 +281,7 @@ TEST(SponsorshipTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(SponsorshipTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -298,7 +298,7 @@ TEST(SponsorshipTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(SponsorshipTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const previousTxnIDValue = canonical_UINT256(); auto const previousTxnLgrSeqValue = canonical_UINT32(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/TicketTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/TicketTests.cpp index 1ac00288f06..b828b083939 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/TicketTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/TicketTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(TicketTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const accountValue = canonical_ACCOUNT(); auto const ownerNodeValue = canonical_UINT64(); @@ -85,7 +85,7 @@ TEST(TicketTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(TicketTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const accountValue = canonical_ACCOUNT(); auto const ownerNodeValue = canonical_UINT64(); @@ -167,7 +167,7 @@ TEST(TicketTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(TicketTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -189,7 +189,7 @@ TEST(TicketTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(TicketTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type CheckBuilder wrongBuilder{ diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/VaultTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/VaultTests.cpp index 26dde555636..0e41aac8336 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/VaultTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/VaultTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(VaultTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const previousTxnIDValue = canonical_UINT256(); auto const previousTxnLgrSeqValue = canonical_UINT32(); @@ -217,7 +217,7 @@ TEST(VaultTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(VaultTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const previousTxnIDValue = canonical_UINT256(); auto const previousTxnLgrSeqValue = canonical_UINT32(); @@ -497,7 +497,7 @@ TEST(VaultTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(VaultTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -516,7 +516,7 @@ TEST(VaultTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(VaultTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ @@ -533,7 +533,7 @@ TEST(VaultTests, BuilderThrowsOnWrongEntryType) // 5) Build with only required fields and verify optional fields return nullopt. TEST(VaultTests, OptionalFieldsReturnNullopt) { - uint256 const index{3u}; + uint256 const index{3}; auto const previousTxnIDValue = canonical_UINT256(); auto const previousTxnLgrSeqValue = canonical_UINT32(); diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/XChainOwnedClaimIDTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/XChainOwnedClaimIDTests.cpp index 4b6979b86a1..30cf893863c 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/XChainOwnedClaimIDTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/XChainOwnedClaimIDTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(XChainOwnedClaimIDTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const accountValue = canonical_ACCOUNT(); auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); @@ -117,7 +117,7 @@ TEST(XChainOwnedClaimIDTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(XChainOwnedClaimIDTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const accountValue = canonical_ACCOUNT(); auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); @@ -247,7 +247,7 @@ TEST(XChainOwnedClaimIDTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(XChainOwnedClaimIDTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -266,7 +266,7 @@ TEST(XChainOwnedClaimIDTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(XChainOwnedClaimIDTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimIDTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimIDTests.cpp index 0e4e1fb9772..127426a0e39 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimIDTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/XChainOwnedCreateAccountClaimIDTests.cpp @@ -18,7 +18,7 @@ namespace xrpl::ledger_entries { // builder's STObject and the wrapper's SLE. TEST(XChainOwnedCreateAccountClaimIDTests, BuilderSettersRoundTrip) { - uint256 const index{1u}; + uint256 const index{1}; auto const accountValue = canonical_ACCOUNT(); auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); @@ -101,7 +101,7 @@ TEST(XChainOwnedCreateAccountClaimIDTests, BuilderSettersRoundTrip) // from that SLE, build a new wrapper, and verify all fields (and validate()). TEST(XChainOwnedCreateAccountClaimIDTests, BuilderFromSleRoundTrip) { - uint256 const index{2u}; + uint256 const index{2}; auto const accountValue = canonical_ACCOUNT(); auto const xChainBridgeValue = canonical_XCHAIN_BRIDGE(); @@ -207,7 +207,7 @@ TEST(XChainOwnedCreateAccountClaimIDTests, BuilderFromSleRoundTrip) // 3) Verify wrapper throws when constructed from wrong ledger entry type. TEST(XChainOwnedCreateAccountClaimIDTests, WrapperThrowsOnWrongEntryType) { - uint256 const index{3u}; + uint256 const index{3}; // Build a valid ledger entry of a different type // Ticket requires: Account, OwnerNode, TicketSequence, PreviousTxnID, PreviousTxnLgrSeq @@ -226,7 +226,7 @@ TEST(XChainOwnedCreateAccountClaimIDTests, WrapperThrowsOnWrongEntryType) // 4) Verify builder throws when constructed from wrong ledger entry type. TEST(XChainOwnedCreateAccountClaimIDTests, BuilderThrowsOnWrongEntryType) { - uint256 const index{4u}; + uint256 const index{4}; // Build a valid ledger entry of a different type TicketBuilder wrongBuilder{ diff --git a/src/tests/libxrpl/shamap/SHAMap.cpp b/src/tests/libxrpl/shamap/SHAMap.cpp index f67c2993006..84ee5ad6b44 100644 --- a/src/tests/libxrpl/shamap/SHAMap.cpp +++ b/src/tests/libxrpl/shamap/SHAMap.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -92,6 +93,39 @@ operator!=(SHAMapItem const& a, SHAMapItem const& b) return a.key() != b.key(); } +// Key construction helpers. Keys in these tests are described by their nibbles (nibble 0 is the +// most significant), so build them by filling every byte and then overwriting the nibbles that +// matter, rather than by assembling and parsing a hex string. + +/** A key whose every byte is `b`. */ +static uint256 +filledKey(unsigned char b) +{ + uint256 k; + std::fill_n(k.begin(), k.size(), b); + return k; +} + +/** `k` with nibble `i` (0 = most significant) set to `v`; every other nibble unchanged. */ +static uint256 +withNibble(uint256 k, unsigned int i, unsigned int v) +{ + auto& byte = k.begin()[i / 2]; + byte = (i % 2 == 0) ? static_cast((byte & 0x0f) | (v << 4)) + : static_cast((byte & 0xf0) | v); + return k; +} + +/** `k` with its leading nibbles replaced by `nibbles`, in order. */ +static uint256 +withPrefix(uint256 k, std::initializer_list nibbles) +{ + unsigned int i = 0; + for (auto const v : nibbles) + k = withNibble(k, i++, v); + return k; +} + struct SHAMapBackingMode { bool backed; @@ -285,14 +319,13 @@ class SHAMapTraversal : public ::testing::Test static std::vector deepFanOutKeys() { + // Nibbles 0-4 are "abcde", the 6th nibble varies by branch, and every nibble after it + // is '7'. + auto const base = withPrefix(filledKey(0x77), {0xa, 0xb, 0xc, 0xd, 0xe}); + std::vector keys; for (unsigned int branch = 0; branch < SHAMap::kBranchFactor; ++branch) - { - // Vary the 6th nibble, keeping the first five identical. - auto text = std::string("abcde") + "0123456789abcdef"[branch]; - text.append(64 - text.size(), '7'); - keys.emplace_back(std::string_view{text}); - } + keys.push_back(withNibble(base, 5, branch)); return keys; } @@ -303,12 +336,11 @@ class SHAMapTraversal : public ::testing::Test static std::vector deepFanOutKeysAtLeafDepth() { + auto const base = filledKey(0xaa); + std::vector keys; for (unsigned int branch = 0; branch < SHAMap::kBranchFactor; ++branch) - { - auto text = std::string(63, 'a') + "0123456789abcdef"[branch]; - keys.emplace_back(std::string_view{text}); - } + keys.push_back(withNibble(base, SHAMap::kLeafDepth - 1u, branch)); return keys; } @@ -391,8 +423,7 @@ TEST_F(SHAMapTraversal, bounds_agree_with_iteration_for_absent_keys) // Probe keys that are not in the map, so the traversal starts mid-tree rather than at a leaf. for (unsigned char const c : {0x00, 0x40, 0x80, 0xc0, 0xff}) { - uint256 probe; - std::fill_n(probe.begin(), probe.size(), c); + uint256 const probe = filledKey(c); auto const expectedUpper = std::ranges::upper_bound(keys, probe); auto const upper = map.upperBound(probe); @@ -421,13 +452,12 @@ TEST_F(SHAMapTraversal, bounds_agree_with_iteration_for_absent_keys) // Probe keys that land on a leaf and require the leaf-pop-and-resume case. Keys share // "abcde" as a prefix and vary the 6th nibble, so a probe that shares the full prefix - // but differs in the padding hits a leaf from either side: '0' padded with '0' lands below - // the first key, and 'f' padded with 'f' lands above the last. - for (char const nibble : {'0', 'f'}) + // but differs in the padding hits a leaf from either side: nibble 0 padded with 0 lands + // below the first key, and nibble f padded with f lands above the last. + for (unsigned int const nibble : {0x0u, 0xfu}) { - auto text = std::string("abcde") + nibble; - text.append(64 - text.size(), nibble); - uint256 const probe{std::string_view{text}}; + auto const fill = static_cast(nibble * 0x11); + uint256 const probe = withPrefix(filledKey(fill), {0xa, 0xb, 0xc, 0xd, 0xe, nibble}); auto const expectedUpper = std::ranges::upper_bound(keys, probe); auto const upper = map.upperBound(probe); @@ -467,8 +497,7 @@ TEST_F(SHAMapTraversal, bounds_on_empty_map_return_end) EXPECT_EQ(map.upperBound(uint256{}), map.end()); EXPECT_EQ(map.lowerBound(uint256{}), map.end()); - uint256 probe; - std::fill_n(probe.begin(), probe.size(), std::uint8_t{0xff}); + uint256 const probe = filledKey(0xff); EXPECT_EQ(map.upperBound(probe), map.end()); EXPECT_EQ(map.lowerBound(probe), map.end()); } @@ -543,7 +572,7 @@ TEST_F(SHAMapTraversal, iteration_survives_a_collapsed_inner_node) // One key in a separate subtree, diverging from the fan-out group at the very first nibble, so // it survives untouched while the fan-out group below is collapsed. - auto const sentinel = uint256{std::string_view{std::string(64, '0')}}; + auto const sentinel = uint256{}; auto fanOutKeys = deepFanOutKeysAtLeafDepth(); fillMap(map, fanOutKeys); @@ -653,13 +682,16 @@ TEST_F(SHAMapTraversal, bounds_agree_with_iteration_for_absent_keys_at_leaf_dept // branch, right up to the one just above kLeafDepth. for (unsigned int const divergeAt : {0u, 31u, 61u, 62u}) { - // '9' sorts below the shared 'a' prefix and 'b' above it, so the probe lands under or - // over the whole key block -- driving belowHelper's First and Last descents respectively. - for (char const nibble : {'9', 'b'}) + // The shared 'a' prefix up to (not including) divergeAt, zeros after it. + uint256 prefix = filledKey(0x00); + for (unsigned int i = 0; i < divergeAt; ++i) + prefix = withNibble(prefix, i, 0xa); + + // 9 sorts below the shared 'a' prefix and b above it, so the probe lands under or over + // the whole key block -- driving belowHelper's First and Last descents respectively. + for (unsigned int const nibble : {0x9u, 0xbu}) { - auto text = std::string(divergeAt, 'a') + nibble; - text.append(64 - text.size(), '0'); - uint256 const probe{std::string_view{text}}; + uint256 const probe = withNibble(prefix, divergeAt, nibble); auto const expectedUpper = std::ranges::upper_bound(keys, probe); auto const upper = map.upperBound(probe); @@ -740,13 +772,12 @@ TEST_F(SHAMapPathProof, verify_proof_path) uint256 rootHash; std::vector goodPath; - static constexpr unsigned char kFirstKey = 1; static constexpr unsigned char kKeyCount = 100; - static constexpr unsigned char kLastKey = kKeyCount - 1; + static constexpr auto kFirstKey = uint256{1}; + static constexpr auto kLastKey = uint256{kKeyCount - 1}; - for (unsigned char c = kFirstKey; c < kKeyCount; ++c) + for (auto k = kFirstKey; k <= kLastKey; ++k) { - uint256 k(c); map.addItem(SHAMapNodeType::TnAccountState, makeShamapitem(k, Slice{k.data(), k.size()})); map.invariants(); @@ -760,16 +791,15 @@ TEST_F(SHAMapPathProof, verify_proof_path) auto& proofPath = *path; EXPECT_TRUE(map.verifyProofPath(root, k, proofPath)); - if (c == kFirstKey) + if (k == kFirstKey) { // extra node proofPath.insert(proofPath.begin(), proofPath.front()); EXPECT_FALSE(map.verifyProofPath(root, k, proofPath)); // wrong key - uint256 const wrongKey(c + 1); - EXPECT_FALSE(map.getProofPath(wrongKey)); + EXPECT_FALSE(map.getProofPath(k.next())); } - if (c == kLastKey) + if (k == kLastKey) { key = k; rootHash = root; @@ -811,8 +841,9 @@ TEST_F(SHAMapPathProof, legitimate_deep_path_is_sixty_five_elements) SHAMap map{SHAMapType::FREE, f}; map.setUnbacked(); - auto const kA = uint256{std::string_view{std::string(63, 'a') + "1"}}; - auto const kB = uint256{std::string_view{std::string(63, 'a') + "2"}}; + // 63 nibbles of 'a', then 1 and 2 respectively. + auto const kA = withNibble(filledKey(0xaa), SHAMap::kLeafDepth - 1u, 1); + auto const kB = withNibble(filledKey(0xaa), SHAMap::kLeafDepth - 1u, 2); for (auto const& k : {kA, kB}) { diff --git a/src/xrpld/app/consensus/RCLValidations.cpp b/src/xrpld/app/consensus/RCLValidations.cpp index c587a04cf01..11174555b63 100644 --- a/src/xrpld/app/consensus/RCLValidations.cpp +++ b/src/xrpld/app/consensus/RCLValidations.cpp @@ -27,7 +27,7 @@ namespace xrpl { RCLValidatedLedger::RCLValidatedLedger(MakeGenesis) - : ledgerID_{0}, ledgerSeq_{0}, j_{beast::Journal::getNullSink()} + : ledgerSeq_{0}, j_{beast::Journal::getNullSink()} { } @@ -84,7 +84,7 @@ RCLValidatedLedger::operator[](Seq const& s) const -> ID << " from ledger hash=" << ledgerID_ << " seq=" << ledgerSeq_ << " (available: " << minSeq() << "-" << seq() << ")"; // Default ID that is less than all others - return ID{0}; + return {}; } // Return the sequence number of the earliest possible mismatching ancestor diff --git a/src/xrpld/app/ledger/OrderBookDBImpl.cpp b/src/xrpld/app/ledger/OrderBookDBImpl.cpp index 1e474ef9492..3b40704e014 100644 --- a/src/xrpld/app/ledger/OrderBookDBImpl.cpp +++ b/src/xrpld/app/ledger/OrderBookDBImpl.cpp @@ -133,10 +133,9 @@ OrderBookDBImpl::update(std::shared_ptr const& ledger) if (sle->isFieldPresent(sfTakerPaysCurrency)) { - Issue issue; - issue.currency = sle->getFieldH160(sfTakerPaysCurrency); - issue.account = sle->getFieldH160(sfTakerPaysIssuer); - book.in = issue; + book.in = Issue{ + Currency{sle->getFieldH160(sfTakerPaysCurrency)}, + AccountID{sle->getFieldH160(sfTakerPaysIssuer)}}; } else { @@ -145,12 +144,12 @@ OrderBookDBImpl::update(std::shared_ptr const& ledger) "OrderBookDB::update, must be TakerPaysMPT"); book.in = sle->getFieldH192(sfTakerPaysMPT); } + if (sle->isFieldPresent(sfTakerGetsCurrency)) { - Issue issue; - issue.currency = sle->getFieldH160(sfTakerGetsCurrency); - issue.account = sle->getFieldH160(sfTakerGetsIssuer); - book.out = issue; + book.out = Issue{ + Currency{sle->getFieldH160(sfTakerGetsCurrency)}, + AccountID{sle->getFieldH160(sfTakerGetsIssuer)}}; } else { diff --git a/src/xrpld/app/ledger/detail/LedgerReplayMsgHandler.cpp b/src/xrpld/app/ledger/detail/LedgerReplayMsgHandler.cpp index 6ed4a296acd..7fee962f665 100644 --- a/src/xrpld/app/ledger/detail/LedgerReplayMsgHandler.cpp +++ b/src/xrpld/app/ledger/detail/LedgerReplayMsgHandler.cpp @@ -44,24 +44,25 @@ LedgerReplayMsgHandler::processProofPathRequest( protocol::TMProofPathRequest& packet = *msg; protocol::TMProofPathResponse reply; - if (!packet.has_key() || !packet.has_ledgerhash() || !packet.has_type() || - packet.ledgerhash().size() != uint256::size() || packet.key().size() != uint256::size() || + auto const key = uint256::fromRaw(packet.key()); + auto const ledgerHash = uint256::fromRaw(packet.ledgerhash()); + + if (!key || !ledgerHash || !packet.has_type() || !protocol::TMLedgerMapType_IsValid(packet.type())) { JLOG(journal_.debug()) << "getProofPath: Invalid request"; reply.set_error(protocol::TMReplyError::reBAD_REQUEST); return reply; } + reply.set_key(packet.key()); reply.set_ledgerhash(packet.ledgerhash()); reply.set_type(packet.type()); - uint256 const key = uint256::fromRaw(packet.key()); - uint256 const ledgerHash = uint256::fromRaw(packet.ledgerhash()); - auto ledger = app_.getLedgerMaster().getLedgerByHash(ledgerHash); + auto ledger = app_.getLedgerMaster().getLedgerByHash(*ledgerHash); if (!ledger) { - JLOG(journal_.debug()) << "getProofPath: Don't have ledger " << ledgerHash; + JLOG(journal_.debug()) << "getProofPath: Don't have ledger " << *ledgerHash; reply.set_error(protocol::TMReplyError::reNO_LEDGER); return reply; } @@ -70,9 +71,9 @@ LedgerReplayMsgHandler::processProofPathRequest( switch (packet.type()) { case protocol::lmACCOUNT_STATE: - return ledger->stateMap().getProofPath(key); + return ledger->stateMap().getProofPath(*key); case protocol::lmTRANSACTION: - return ledger->txMap().getProofPath(key); + return ledger->txMap().getProofPath(*key); default: // should not be here // because already tested with TMLedgerMapType_IsValid() @@ -82,8 +83,8 @@ LedgerReplayMsgHandler::processProofPathRequest( if (!path) { - JLOG(journal_.debug()) << "getProofPath: Don't have the node " << key << " of ledger " - << ledgerHash; + JLOG(journal_.debug()) << "getProofPath: Don't have the node " << *key << " of ledger " + << *ledgerHash; reply.set_error(protocol::TMReplyError::reNO_NODE); return reply; } @@ -96,7 +97,7 @@ LedgerReplayMsgHandler::processProofPathRequest( for (auto const& b : *path) reply.add_path(b.data(), b.size()); - JLOG(journal_.debug()) << "getProofPath for the node " << key << " of ledger " << ledgerHash + JLOG(journal_.debug()) << "getProofPath for the node " << *key << " of ledger " << *ledgerHash << " path length " << path->size(); return reply; } @@ -106,14 +107,18 @@ LedgerReplayMsgHandler::processProofPathResponse( std::shared_ptr const& msg) { protocol::TMProofPathResponse const& reply = *msg; + if (reply.has_error()) { JLOG(journal_.debug()) << "ProofPathResponse: peer reported error"; return ReplayMsgStatus::BadData; } - if (!reply.has_key() || !reply.has_ledgerhash() || !reply.has_type() || - !reply.has_ledgerheader() || reply.path_size() == 0 || - reply.ledgerhash().size() != uint256::size() || reply.key().size() != uint256::size()) + + auto const replyHash = uint256::fromRaw(reply.ledgerhash()); + auto const key = uint256::fromRaw(reply.key()); + + if (!replyHash || !key || !reply.has_type() || !reply.has_ledgerheader() || + reply.path_size() == 0) { JLOG(journal_.debug()) << "ProofPathResponse: malformed (missing or wrong-size fields)"; return ReplayMsgStatus::Malformed; @@ -136,30 +141,27 @@ LedgerReplayMsgHandler::processProofPathResponse( JLOG(journal_.debug()) << "ProofPathResponse: malformed header (" << e.what() << ")"; return ReplayMsgStatus::Malformed; } - uint256 const replyHash = uint256::fromRaw(reply.ledgerhash()); - if (calculateLedgerHash(info) != replyHash) + + if (calculateLedgerHash(info) != *replyHash) { JLOG(journal_.debug()) << "ProofPathResponse: malformed (hash mismatch)"; return ReplayMsgStatus::Malformed; } - info.hash = replyHash; + info.hash = *replyHash; - uint256 const key = uint256::fromRaw(reply.key()); - if (key != keylet::skip().key) + if (*key != keylet::skip().key) { - JLOG(journal_.debug()) << "ProofPathResponse: malformed (unexpected key " << key << ")"; + JLOG(journal_.debug()) << "ProofPathResponse: malformed (unexpected key " << *key << ")"; return ReplayMsgStatus::Malformed; } // verify the skip list std::vector path; path.reserve(reply.path_size()); - for (int i = 0; i < reply.path_size(); ++i) - { - path.emplace_back(reply.path(i).begin(), reply.path(i).end()); - } + for (auto const& node : reply.path()) + path.emplace_back(node.begin(), node.end()); - if (!SHAMap::verifyProofPath(info.accountHash, key, path)) + if (!SHAMap::verifyProofPath(info.accountHash, *key, path)) { JLOG(journal_.debug()) << "ProofPathResponse: malformed (proof path verify failed)"; return ReplayMsgStatus::Malformed; @@ -176,6 +178,7 @@ LedgerReplayMsgHandler::processProofPathResponse( JLOG(journal_.debug()) << "ProofPathResponse: malformed SHAMap node (" << e.what() << ")"; return ReplayMsgStatus::Malformed; } + if (!node || !node->isLeaf()) { JLOG(journal_.debug()) << "ProofPathResponse: malformed (not a leaf node)"; @@ -199,7 +202,9 @@ LedgerReplayMsgHandler::processReplayDeltaRequest( protocol::TMReplayDeltaRequest const& packet = *msg; protocol::TMReplayDeltaResponse reply; - if (!packet.has_ledgerhash() || packet.ledgerhash().size() != uint256::size()) + auto const ledgerHash = uint256::fromRaw(packet.ledgerhash()); + + if (!ledgerHash) { JLOG(journal_.debug()) << "getReplayDelta: Invalid request"; reply.set_error(protocol::TMReplyError::reBAD_REQUEST); @@ -207,11 +212,10 @@ LedgerReplayMsgHandler::processReplayDeltaRequest( } reply.set_ledgerhash(packet.ledgerhash()); - uint256 const ledgerHash = uint256::fromRaw(packet.ledgerhash()); - auto ledger = app_.getLedgerMaster().getLedgerByHash(ledgerHash); + auto ledger = app_.getLedgerMaster().getLedgerByHash(*ledgerHash); if (!ledger || !ledger->isImmutable()) { - JLOG(journal_.debug()) << "getReplayDelta: Don't have ledger " << ledgerHash; + JLOG(journal_.debug()) << "getReplayDelta: Don't have ledger " << *ledgerHash; reply.set_error(protocol::TMReplyError::reNO_LEDGER); return reply; } @@ -226,7 +230,7 @@ LedgerReplayMsgHandler::processReplayDeltaRequest( reply.add_transaction(txNode->data(), txNode->size()); }); - JLOG(journal_.debug()) << "getReplayDelta for ledger " << ledgerHash << " txMap hash " + JLOG(journal_.debug()) << "getReplayDelta for ledger " << *ledgerHash << " txMap hash " << txMap.getHash().asUInt256(); return reply; } @@ -241,14 +245,17 @@ LedgerReplayMsgHandler::processReplayDeltaResponse( JLOG(journal_.debug()) << "ReplayDeltaResponse: peer reported error"; return ReplayMsgStatus::BadData; } - if (!reply.has_ledgerheader() || !reply.has_ledgerhash() || - reply.ledgerhash().size() != uint256::size()) + + auto const replyHash = uint256::fromRaw(reply.ledgerhash()); + + if (!reply.has_ledgerheader() || !replyHash) { JLOG(journal_.debug()) << "ReplayDeltaResponse: malformed (missing or wrong-size fields)"; return ReplayMsgStatus::Malformed; } LedgerHeader info; + try { info = deserializeHeader(makeSlice(reply.ledgerheader())); @@ -258,13 +265,14 @@ LedgerReplayMsgHandler::processReplayDeltaResponse( JLOG(journal_.debug()) << "ReplayDeltaResponse: malformed header (" << e.what() << ")"; return ReplayMsgStatus::Malformed; } - uint256 const replyHash = uint256::fromRaw(reply.ledgerhash()); + if (calculateLedgerHash(info) != replyHash) { JLOG(journal_.debug()) << "ReplayDeltaResponse: malformed (hash mismatch)"; return ReplayMsgStatus::Malformed; } - info.hash = replyHash; + + info.hash = *replyHash; auto numTxns = reply.transaction_size(); std::map> orderedTxns; diff --git a/src/xrpld/app/main/Application.cpp b/src/xrpld/app/main/Application.cpp index 775471854df..b643b14a085 100644 --- a/src/xrpld/app/main/Application.cpp +++ b/src/xrpld/app/main/Application.cpp @@ -472,8 +472,6 @@ class ApplicationImp : public Application, public BasicApp getIoContext()) , grpcServer_(std::make_unique(*this)) { - initAccountIdCache(config_->getValueFor(SizedItem::AccountIdCacheSize)); - add(resourceManager_.get()); // diff --git a/src/xrpld/app/misc/FeeVoteImpl.cpp b/src/xrpld/app/misc/FeeVoteImpl.cpp index f1cb944a527..ef7fc66733f 100644 --- a/src/xrpld/app/misc/FeeVoteImpl.cpp +++ b/src/xrpld/app/misc/FeeVoteImpl.cpp @@ -226,27 +226,22 @@ FeeVoteImpl::doVoting( auto doVote = [](std::shared_ptr const& val, detail::VotableValue& value, auto const& valueField) { - if (auto const field = val->at(~valueField)) + using XRPType = XRPAmount::value_type; + + if (auto const field = val->at(~valueField); field && std::in_range(*field)) { - using XRPType = XRPAmount::value_type; - auto const vote = *field; - if (vote <= std::numeric_limits::max() && - isLegalAmountSigned(XRPAmount{unsafeCast(vote)})) + if (auto const amount = XRPAmount{checkedCast(*field)}; + isLegalAmountSigned(amount)) { - value.addVote(XRPAmount{unsafeCast(vote)}); - } - else - { - // Invalid amounts will be treated as if they're - // not provided. Don't throw because this value is - // provided by an external entity. - value.noVote(); + value.addVote(amount); + return; } } - else - { - value.noVote(); - } + + // Invalid or missing amounts are treated as if they're not + // provided. Don't throw because this value is provided by an + // external entity. + value.noVote(); }; for (auto const& val : set) diff --git a/src/xrpld/app/misc/NegativeUNLVote.cpp b/src/xrpld/app/misc/NegativeUNLVote.cpp index 2ed440fba85..1f20ff42c39 100644 --- a/src/xrpld/app/misc/NegativeUNLVote.cpp +++ b/src/xrpld/app/misc/NegativeUNLVote.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include namespace xrpl { @@ -142,16 +143,14 @@ NegativeUNLVote::choose(uint256 const& randomPadData, std::vector const& { XRPL_ASSERT(!candidates.empty(), "xrpl::NegativeUNLVote::choose : non-empty input"); static_assert(NodeID::kBytes <= uint256::kBytes); - NodeID const randomPad = NodeID::fromVoid(randomPadData.data()); - NodeID txNodeID = candidates[0]; - for (int j = 1; j < candidates.size(); ++j) - { - if ((candidates[j] ^ randomPad) < (txNodeID ^ randomPad)) - { - txNodeID = candidates[j]; - } - } - return txNodeID; + + NodeID const randomPad{ + std::span{randomPadData}.first()}; + + return *std::min_element( + candidates.begin(), candidates.end(), [&randomPad](NodeID const& a, NodeID const& b) { + return (a ^ randomPad) < (b ^ randomPad); + }); } std::optional> diff --git a/src/xrpld/app/misc/detail/AmendmentTable.cpp b/src/xrpld/app/misc/detail/AmendmentTable.cpp index 4af556d3e77..10544f2db4a 100644 --- a/src/xrpld/app/misc/detail/AmendmentTable.cpp +++ b/src/xrpld/app/misc/detail/AmendmentTable.cpp @@ -69,7 +69,7 @@ parseSection(Section const& section) uint256 id; - if (!id.parseHex(match[1])) + if (!id.parseHex(match[1].str())) { Throw( "Invalid amendment ID '" + match[1] + "' in [" + section.name() + "]"); diff --git a/src/xrpld/core/Config.h b/src/xrpld/core/Config.h index 2dea8f35972..45ade62344b 100644 --- a/src/xrpld/core/Config.h +++ b/src/xrpld/core/Config.h @@ -40,7 +40,6 @@ enum class SizedItem : std::size_t { OpenFinalLimit, BurstSize, RamSizeGb, - AccountIdCacheSize, }; /** diff --git a/src/xrpld/core/detail/Config.cpp b/src/xrpld/core/detail/Config.cpp index 3ff62c9b641..532585c1d95 100644 --- a/src/xrpld/core/detail/Config.cpp +++ b/src/xrpld/core/detail/Config.cpp @@ -111,7 +111,7 @@ namespace xrpl { // clang-format off // The configurable node sizes are "tiny", "small", "medium", "large", "huge" -inline constexpr std::array>, 13> +inline constexpr std::array>, 12> kSizedItems {{ // FIXME: We should document each of these items, explaining exactly @@ -130,8 +130,7 @@ kSizedItems {SizedItem::LgrDbCache, {{ 4, 8, 16, 32, 128 }}}, {SizedItem::OpenFinalLimit, {{ 8, 16, 32, 64, 128 }}}, {SizedItem::BurstSize, {{ 4, 8, 16, 32, 48 }}}, - {SizedItem::RamSizeGb, {{ 6, 8, 12, 24, 0 }}}, - {SizedItem::AccountIdCacheSize, {{ 20047, 50053, 77081, 150061, 300007 }}} + {SizedItem::RamSizeGb, {{ 6, 8, 12, 24, 0 }}} }}; // clang-format on diff --git a/src/xrpld/overlay/detail/PeerImp.cpp b/src/xrpld/overlay/detail/PeerImp.cpp index 0dc9792f2e3..9e37f363121 100644 --- a/src/xrpld/overlay/detail/PeerImp.cpp +++ b/src/xrpld/overlay/detail/PeerImp.cpp @@ -209,11 +209,7 @@ PeerImp::run() auto parseLedgerHash = [](std::string_view value) -> std::optional { if (uint256 ret; ret.parseHex(value)) return ret; - - if (auto const s = base64Decode(value); s.size() == uint256::size()) - return uint256::fromRaw(s); - - return std::nullopt; + return uint256::fromRaw(base64Decode(value)); }; std::optional closed; @@ -1526,57 +1522,59 @@ PeerImp::onMessage(std::shared_ptr const& m) } // Queue a job to process the request. - std::weak_ptr const weak = shared_from_this(); - app_.getJobQueue().addJob(JtLedgerReq, "RcvGetLedger", [weak, m, itype]() { - auto peer = weak.lock(); - if (!peer) - return; + app_.getJobQueue().addJob( + JtLedgerReq, "RcvGetLedger", [weak = weak_from_this(), m, itype]() { + auto peer = weak.lock(); + if (!peer) + return; - std::vector nodeIDs; - bool tooManyNodeIds = false; - if (itype != protocol::liBASE) - { - nodeIDs.reserve(std::min(m->nodeids_size(), tuning::kSoftMaxReplyNodes)); - for (auto const& nodeId : m->nodeids()) + std::vector nodeIDs; + bool tooManyNodeIds = false; + if (itype != protocol::liBASE) { - if (nodeIDs.size() >= tuning::kSoftMaxReplyNodes) + nodeIDs.reserve(std::min(m->nodeids_size(), tuning::kSoftMaxReplyNodes)); + for (auto const& nodeId : m->nodeids()) { - // The peer requested too many node IDs. Continue processing the received node - // IDs up to the limit. If the request is legitimate then at least they will get - // a response and won't have to resend these nodes in their next request. - tooManyNodeIds = true; - break; - } - auto parsed = deserializeSHAMapNodeID(nodeId); - if (!parsed) - { - peer->charge(resource::kFeeInvalidData, "TMGetLedger: Invalid node ID"); - return; + if (nodeIDs.size() >= tuning::kSoftMaxReplyNodes) + { + // The peer requested too many node IDs. Continue processing the received + // node IDs up to the limit. If the request is legitimate then at least they + // will get a response and won't have to resend these nodes in their next + // request. + tooManyNodeIds = true; + break; + } + auto parsed = deserializeSHAMapNodeID(nodeId); + if (!parsed) + { + peer->charge(resource::kFeeInvalidData, "TMGetLedger: Invalid node ID"); + return; + } + nodeIDs.push_back(std::move(*parsed)); } - nodeIDs.push_back(std::move(*parsed)); } - } - - // These are two distinct infractions and are charged independently: requesting too many - // node IDs is charged even for a relay response, while the base "get ledger request" charge - // below is skipped for relay responses. - if (tooManyNodeIds) - { - peer->charge(resource::kFeeModerateBurdenPeer, "TMGetLedger: too many node IDs"); - // Truncate the request to what was actually parsed and charged for, so that if this - // request ends up being relayed to another peer, we don't forward the oversized list. - m->mutable_nodeids()->DeleteSubrange( - static_cast(nodeIDs.size()), - m->nodeids_size() - static_cast(nodeIDs.size())); - } - if (!m->has_requestcookie()) - { - peer->charge(resource::kFeeModerateBurdenPeer, "TMGetLedger: get ledger request"); - } + // These are two distinct infractions and are charged independently: requesting too many + // node IDs is charged even for a relay response, while the base "get ledger request" + // charge below is skipped for relay responses. + if (tooManyNodeIds) + { + peer->charge(resource::kFeeModerateBurdenPeer, "TMGetLedger: too many node IDs"); + + // Truncate the request to what was actually parsed and charged for, so that if this + // request ends up being relayed to another peer, we don't forward the oversized + // list. + m->mutable_nodeids()->DeleteSubrange( + static_cast(nodeIDs.size()), + m->nodeids_size() - static_cast(nodeIDs.size())); + } + if (!m->has_requestcookie()) + { + peer->charge(resource::kFeeModerateBurdenPeer, "TMGetLedger: get ledger request"); + } - peer->processLedgerRequest(m, std::move(nodeIDs)); - }); + peer->processLedgerRequest(m, std::move(nodeIDs)); + }); } void @@ -1702,7 +1700,9 @@ PeerImp::onMessage(std::shared_ptr const& m) }; // Verify ledger hash - if (!stringIsUInt256Sized(m->ledgerhash())) + auto const ledgerHash = uint256::fromRaw(m->ledgerhash()); + + if (!ledgerHash) { badData("Invalid ledger hash"); return; @@ -1759,6 +1759,15 @@ PeerImp::onMessage(std::shared_ptr const& m) { if (auto peer = overlay_.findPeerByShortID(m->requestcookie())) { + auto relayError = [&](std::string what) { + what += " while relaying ledger data for "; + what += to_string(*ledgerHash); + what += " to peer "; + what += std::to_string(peer->id()); + + badData(what); + }; + m->clear_requestcookie(); // If the original requester doesn't support the new depth-based format, rewrite any @@ -1770,45 +1779,33 @@ PeerImp::onMessage(std::shared_ptr const& m) peer->supportsFeature(ProtocolFeature::LedgerNodeDepth); enum class MessageType { Unknown, Base, Legacy, Depth }; MessageType messageType = MessageType::Unknown; - for (int i = 0; i < m->nodes_size(); ++i) + for (auto& ledgerNode : *m->mutable_nodes()) { - auto* ledgerNode = m->mutable_nodes(i); - // All nodes should have non-empty data. The field is required so we don't need to // check for presence first. - if (ledgerNode->nodedata().empty()) + if (ledgerNode.nodedata().empty()) { - badData( - "Received node with empty data while relaying ledger data for " + - to_string(uint256::fromRaw(m->ledgerhash())) + " to peer " + - std::to_string(peer->id())); + relayError("Received node with empty data"); return; } MessageType msgType = MessageType::Unknown; if (m->type() == protocol::liBASE) { - if (ledgerNode->has_nodeid() || ledgerNode->has_id() || ledgerNode->has_depth()) + if (ledgerNode.has_nodeid() || ledgerNode.has_id() || ledgerNode.has_depth()) { - badData( - "Received liBASE message with node reference while relaying ledger " - "data for " + - to_string(uint256::fromRaw(m->ledgerhash())) + " to peer " + - std::to_string(peer->id())); + relayError("Received liBASE message with node reference"); return; } msgType = MessageType::Base; } else { - msgType = ledgerNode->has_nodeid() ? MessageType::Legacy : MessageType::Depth; + msgType = ledgerNode.has_nodeid() ? MessageType::Legacy : MessageType::Depth; } if (messageType != MessageType::Unknown && messageType != msgType) { - badData( - "Received mixed mode message while relaying ledger data for " + - to_string(uint256::fromRaw(m->ledgerhash())) + " to peer " + - std::to_string(peer->id())); + relayError("Received mixed mode message"); return; } messageType = msgType; @@ -1819,49 +1816,40 @@ PeerImp::onMessage(std::shared_ptr const& m) SOMETIMES( !peerSupportsNodeDepth, "xrpl::PeerImp : relaying depth-format ledger data to pre-2.3 peer"); - switch (ledgerNode->reference_case()) + switch (ledgerNode.reference_case()) { case protocol::TMLedgerNode::kId: { // We can directly copy the `id` field, because it uses the same wire format // as the legacy `nodeid` field. REACHABLE("xrpl::PeerImp : relay downgrade id to nodeid"); - ledgerNode->set_nodeid(ledgerNode->id()); - ledgerNode->clear_id(); + ledgerNode.set_nodeid(ledgerNode.id()); + ledgerNode.clear_id(); break; } case protocol::TMLedgerNode::kDepth: { // We need to regenerate the node ID from the node data and depth. - auto treeNode = getTreeNode(ledgerNode->nodedata()); + auto treeNode = getTreeNode(ledgerNode.nodedata()); if (!treeNode) { - badData( - "Unable to get tree node while relaying ledger data for " + - to_string(uint256::fromRaw(m->ledgerhash())) + " to peer " + - std::to_string(peer->id())); + relayError("Unable to get tree node"); return; } - auto const nodeID = getSHAMapNodeID(*ledgerNode, *treeNode); + auto const nodeID = getSHAMapNodeID(ledgerNode, *treeNode); if (!nodeID) { - badData( - "Unable to get node ID while relaying ledger data for " + - to_string(uint256::fromRaw(m->ledgerhash())) + " to peer " + - std::to_string(peer->id())); + relayError("Unable to get node ID"); return; } REACHABLE("xrpl::PeerImp : relay downgrade depth to nodeid"); - ledgerNode->set_nodeid(nodeID->getRawString()); - ledgerNode->clear_depth(); + ledgerNode.set_nodeid(nodeID->getRawString()); + ledgerNode.clear_depth(); break; } default: { SOMETIMES(true, "xrpl::PeerImp : relay node has empty reference"); - badData( - "Empty node reference while relaying ledger data for " + - to_string(uint256::fromRaw(m->ledgerhash())) + " to peer " + - std::to_string(peer->id())); + relayError("Empty node reference"); return; } } @@ -1876,23 +1864,19 @@ PeerImp::onMessage(std::shared_ptr const& m) return; } - uint256 const ledgerHash = uint256::fromRaw(m->ledgerhash()); - // Otherwise check if received data for a candidate transaction set if (m->type() == protocol::liTS_CANDIDATE) { - std::weak_ptr const weak{shared_from_this()}; - app_.getJobQueue().addJob(JtTxnData, "RcvPeerData", [weak, ledgerHash, m]() { - if (auto peer = weak.lock()) - { - peer->app_.getInboundTransactions().gotData(ledgerHash, peer, m); - } - }); + app_.getJobQueue().addJob( + JtTxnData, "RcvPeerData", [weak = weak_from_this(), hash = *ledgerHash, m]() { + if (auto peer = weak.lock()) + peer->app_.getInboundTransactions().gotData(hash, peer, m); + }); return; } // Consume the message - app_.getInboundLedgers().gotLedgerData(ledgerHash, shared_from_this(), m); + app_.getInboundLedgers().gotLedgerData(*ledgerHash, shared_from_this(), m); } void @@ -1912,7 +1896,10 @@ PeerImp::onMessage(std::shared_ptr const& m) return; } - if (!stringIsUInt256Sized(set.currenttxhash()) || !stringIsUInt256Sized(set.previousledger())) + auto const proposeHash = uint256::fromRaw(set.currenttxhash()); + auto const prevLedger = uint256::fromRaw(set.previousledger()); + + if (!proposeHash || !prevLedger) { JLOG(pJournal_.warn()) << "Proposal: malformed"; fee_.update(resource::kFeeMalformedRequest, "bad hashes"); @@ -1938,13 +1925,10 @@ PeerImp::onMessage(std::shared_ptr const& m) return; } - uint256 const proposeHash = uint256::fromRaw(set.currenttxhash()); - uint256 const prevLedger = uint256::fromRaw(set.previousledger()); - NetClock::time_point const closeTime{NetClock::duration{set.closetime()}}; uint256 const suppression = proposalUniqueId( - proposeHash, prevLedger, set.proposeseq(), closeTime, publicKey.slice(), sig); + *proposeHash, *prevLedger, set.proposeseq(), closeTime, publicKey.slice(), sig); if (auto [added, relayed] = app_.getHashRouter().addSuppressionPeerWithStatus(suppression, id_); !added) @@ -1985,16 +1969,17 @@ PeerImp::onMessage(std::shared_ptr const& m) sig, suppression, RCLCxPeerPos::Proposal{ - prevLedger, + *prevLedger, set.proposeseq(), - proposeHash, + *proposeHash, closeTime, app_.getTimeKeeper().closeTime(), calcNodeID(app_.getValidatorManifests().getMasterKey(publicKey))}); - std::weak_ptr const weak = shared_from_this(); app_.getJobQueue().addJob( - isTrusted ? JtProposalT : JtProposalUt, "checkPropose", [weak, isTrusted, m, proposal]() { + isTrusted ? JtProposalT : JtProposalUt, + "checkPropose", + [weak = weak_from_this(), isTrusted, m, proposal]() { if (auto peer = weak.lock()) peer->checkPropose(isTrusted, m, proposal); }); @@ -2045,42 +2030,30 @@ PeerImp::onMessage(std::shared_ptr const& m) } { - uint256 closedLedgerHash{}; - bool const peerChangedLedgers{m->has_ledgerhash() && stringIsUInt256Sized(m->ledgerhash())}; + auto const closed = m->has_ledgerhash() ? uint256::fromRaw(m->ledgerhash()) : std::nullopt; + auto const previous = + m->has_ledgerhashprevious() ? uint256::fromRaw(m->ledgerhashprevious()) : std::nullopt; { // Operations on closedLedgerHash_ and previousLedgerHash_ must be // guarded by recentLock_. std::scoped_lock const sl(recentLock_); - if (peerChangedLedgers) - { - closedLedgerHash_ = m->ledgerhash(); - closedLedgerHash = closedLedgerHash_; - addLedger(closedLedgerHash, sl); - } - else - { - closedLedgerHash_.zero(); - } - if (m->has_ledgerhashprevious() && stringIsUInt256Sized(m->ledgerhashprevious())) - { - previousLedgerHash_ = m->ledgerhashprevious(); - addLedger(previousLedgerHash_, sl); - } - else - { - previousLedgerHash_.zero(); - } - } - if (peerChangedLedgers) - { - JLOG(pJournal_.debug()) << "LCL is " << closedLedgerHash; + closedLedgerHash_ = closed.value_or(beast::kZero); + + if (closed) + addLedger(*closed, sl); + + previousLedgerHash_ = previous.value_or(beast::kZero); + + if (previous) + addLedger(*previous, sl); } + + if (closed) + JLOG(pJournal_.debug()) << "LCL is " << *closed; else - { JLOG(pJournal_.debug()) << "Status: No ledger"; - } } if (m->has_firstseq() && m->has_lastseq()) @@ -2216,14 +2189,14 @@ PeerImp::checkTracking(std::uint32_t seq1, std::uint32_t seq2) void PeerImp::onMessage(std::shared_ptr const& m) { - if (!stringIsUInt256Sized(m->hash())) + auto const hash = uint256::fromRaw(m->hash()); + + if (!hash) { fee_.update(resource::kFeeMalformedRequest, "bad hash"); return; } - uint256 const hash = uint256::fromRaw(m->hash()); - if (m->status() == protocol::tsHAVE) { std::scoped_lock const sl(recentLock_); @@ -2234,7 +2207,7 @@ PeerImp::onMessage(std::shared_ptr const& m) return; } - recentTxSets_.push_back(hash); + recentTxSets_.push_back(*hash); } } @@ -2624,6 +2597,7 @@ PeerImp::onMessage(std::shared_ptr const& m) return; } } + // Reject oversized requests before touching the NodeStore. // The legitimate upper bound (InboundLedger::getNeededHashes()) // is 8 hashes; anything beyond kHardMaxReplyNodes is non-conforming. @@ -2680,48 +2654,43 @@ PeerImp::onMessage(std::shared_ptr const& m) bool pLDo = true; bool progress = false; - for (int i = 0; i < packet.objects_size(); ++i) + for (auto const& obj : packet.objects()) { - protocol::TMIndexedObject const& obj = packet.objects(i); + auto const hash = uint256::fromRaw(obj.hash()); + + if (!hash) + continue; - if (obj.has_hash() && stringIsUInt256Sized(obj.hash())) + if (obj.has_ledgerseq() && obj.ledgerseq() != pLSeq) { - if (obj.has_ledgerseq()) + if (pLDo && (pLSeq != 0)) { - if (obj.ledgerseq() != pLSeq) - { - if (pLDo && (pLSeq != 0)) - { - JLOG(pJournal_.debug()) << "GetObj: Full fetch pack for " << pLSeq; - } - pLSeq = obj.ledgerseq(); - pLDo = !app_.getLedgerMaster().haveLedger(pLSeq); - - if (!pLDo) - { - JLOG(pJournal_.debug()) << "GetObj: Late fetch pack for " << pLSeq; - } - else - { - progress = true; - } - } + JLOG(pJournal_.debug()) << "GetObj: Full fetch pack for " << pLSeq; } - if (pLDo) - { - uint256 const hash = uint256::fromRaw(obj.hash()); + pLSeq = obj.ledgerseq(); + pLDo = !app_.getLedgerMaster().haveLedger(pLSeq); - app_.getLedgerMaster().addFetchPack( - hash, std::make_shared(obj.data().begin(), obj.data().end())); + if (!pLDo) + { + JLOG(pJournal_.debug()) << "GetObj: Late fetch pack for " << pLSeq; + } + else + { + progress = true; } } + + if (pLDo) + app_.getLedgerMaster().addFetchPack( + *hash, std::make_shared(obj.data().begin(), obj.data().end())); } if (pLDo && (pLSeq != 0)) { JLOG(pJournal_.debug()) << "GetObj: Partial fetch pack for " << pLSeq; } + if (packet.type() == protocol::TMGetObjectByHash::otFETCH_PACK) app_.getLedgerMaster().gotFetchPack(progress, pLSeq); } @@ -2755,19 +2724,20 @@ PeerImp::processGetObjectByHash(std::shared_ptr con for (int i = 0; i < iterLimit; ++i) { auto const& obj = packet.objects(i); - if (!obj.has_hash() || !stringIsUInt256Sized(obj.hash())) + + auto const hash = uint256::fromRaw(obj.hash()); + if (!hash) continue; - uint256 const hash = uint256::fromRaw(obj.hash()); // VFALCO TODO Move this someplace more sensible so we don't // need to inject the NodeStore interfaces. std::uint32_t const seq{obj.has_ledgerseq() ? obj.ledgerseq() : 0}; - auto const nodeObject = app_.getNodeStore().fetchNodeObject(hash, seq); + auto const nodeObject = app_.getNodeStore().fetchNodeObject(*hash, seq); if (!nodeObject) continue; protocol::TMIndexedObject& newObj = *reply.add_objects(); - newObj.set_hash(hash.begin(), hash.size()); + newObj.set_hash(hash->begin(), hash->size()); auto const& data = nodeObject->getData(); newObj.set_data(data.data(), data.size()); if (obj.has_nodeid()) @@ -2816,34 +2786,35 @@ PeerImp::handleHaveTransactions(std::shared_ptr co JLOG(pJournal_.trace()) << "received TMHaveTransactions " << m->hashes_size(); - for (std::uint32_t i = 0; i < m->hashes_size(); i++) + for (auto const& raw : m->hashes()) { - if (!stringIsUInt256Sized(m->hashes(i))) + auto const hash = uint256::fromRaw(raw); + + if (!hash) { JLOG(pJournal_.error()) << "TMHaveTransactions with invalid hash size"; fee_.update(resource::kFeeMalformedRequest, "hash size"); return; } - uint256 hash = uint256::fromRaw(m->hashes(i)); + auto txn = app_.getMasterTransaction().fetchFromCache(*hash); - auto txn = app_.getMasterTransaction().fetchFromCache(hash); - - JLOG(pJournal_.trace()) << "checking transaction " << (bool)txn; + JLOG(pJournal_.trace()) << "checking transaction " << *hash << ": " + << (txn ? " cached" : " not cached"); if (!txn) { JLOG(pJournal_.debug()) << "adding transaction to request"; auto obj = tmBH.add_objects(); - obj->set_hash(hash.data(), hash.size()); + obj->set_hash(hash->data(), hash->size()); } else { // Erase only if a peer has seen this tx. If the peer has not // seen this tx then the tx could not has been queued for this // peer. - removeTxQueue(hash); + removeTxQueue(*hash); } } @@ -2954,7 +2925,9 @@ PeerImp::doFetchPack(std::shared_ptr const& packet) return; } - if (!stringIsUInt256Sized(packet->ledgerhash())) + auto const hash = uint256::fromRaw(packet->ledgerhash()); + + if (!hash) { JLOG(pJournal_.warn()) << "FetchPack hash size malformed"; fee_.update(resource::kFeeMalformedRequest, "hash size"); @@ -2963,14 +2936,12 @@ PeerImp::doFetchPack(std::shared_ptr const& packet) fee_.fee = resource::kFeeHeavyBurdenPeer; - uint256 const hash = uint256::fromRaw(packet->ledgerhash()); - - std::weak_ptr const weak = shared_from_this(); - auto elapsed = UptimeClock::now(); - auto const pap = &app_; - app_.getJobQueue().addJob(JtPack, "MakeFetchPack", [pap, weak, packet, hash, elapsed]() { - pap->getLedgerMaster().makeFetchPack(weak, packet, hash, elapsed); - }); + app_.getJobQueue().addJob( + JtPack, + "MakeFetchPack", + [weak = weak_from_this(), app = &app_, packet, hash = *hash, elapsed = UptimeClock::now()] { + app->getLedgerMaster().makeFetchPack(weak, packet, hash, elapsed); + }); } void @@ -2988,24 +2959,21 @@ PeerImp::doTransactions(std::shared_ptr const& pack return; } - for (std::uint32_t i = 0; i < packet->objects_size(); ++i) + for (auto const& obj : packet->objects()) { - auto const& obj = packet->objects(i); + auto const hash = uint256::fromRaw(obj.hash()); - if (!stringIsUInt256Sized(obj.hash())) + if (!hash) { fee_.update(resource::kFeeMalformedRequest, "hash size"); return; } - uint256 hash = uint256::fromRaw(obj.hash()); - - auto txn = app_.getMasterTransaction().fetchFromCache(hash); + auto txn = app_.getMasterTransaction().fetchFromCache(*hash); if (!txn) { - JLOG(pJournal_.error()) - << "doTransactions, transaction not found " << Slice(hash.data(), hash.size()); + JLOG(pJournal_.error()) << "doTransactions, transaction not found " << *hash; fee_.update(resource::kFeeMalformedRequest, "tx not found"); return; } @@ -3356,25 +3324,27 @@ PeerImp::getLedger(std::shared_ptr const& m) if (m->has_ledgerhash()) { // Attempt to find ledger by hash - uint256 const ledgerHash = uint256::fromRaw(m->ledgerhash()); - ledger = app_.getLedgerMaster().getLedgerByHash(ledgerHash); - if (!ledger) + if (auto const ledgerHash = uint256::fromRaw(m->ledgerhash())) { - JLOG(pJournal_.trace()) << "getLedger: Don't have ledger with hash " << ledgerHash; - - if (m->has_querytype() && !m->has_requestcookie()) + ledger = app_.getLedgerMaster().getLedgerByHash(*ledgerHash); + if (!ledger) { - // Attempt to relay the request to a peer - if (auto const peer = getPeerWithLedger( - overlay_, ledgerHash, m->has_ledgerseq() ? m->ledgerseq() : 0, this)) + JLOG(pJournal_.trace()) << "getLedger: Don't have ledger with hash " << *ledgerHash; + + if (m->has_querytype() && !m->has_requestcookie()) { - m->set_requestcookie(id()); - peer->send(std::make_shared(*m, protocol::mtGET_LEDGER)); - JLOG(pJournal_.debug()) << "getLedger: Request relayed to peer"; - return ledger; - } + // Attempt to relay the request to a peer + if (auto const peer = getPeerWithLedger( + overlay_, *ledgerHash, m->has_ledgerseq() ? m->ledgerseq() : 0, this)) + { + m->set_requestcookie(id()); + peer->send(std::make_shared(*m, protocol::mtGET_LEDGER)); + JLOG(pJournal_.debug()) << "getLedger: Request relayed to peer"; + return ledger; + } - JLOG(pJournal_.trace()) << "getLedger: Failed to find peer to relay request"; + JLOG(pJournal_.trace()) << "getLedger: Failed to find peer to relay request"; + } } } } @@ -3435,14 +3405,22 @@ PeerImp::getTxSet(std::shared_ptr const& m) const { JLOG(pJournal_.trace()) << "getTxSet: TX set"; - uint256 const txSetHash = uint256::fromRaw(m->ledgerhash()); - std::shared_ptr shaMap{app_.getInboundTransactions().getSet(txSetHash, false)}; + // Presence and length validated in onMessage(TMGetLedger) for liTS_CANDIDATE. + auto const txSetHash = uint256::fromRaw(m->ledgerhash()); + + XRPL_ASSERT(txSetHash, "xrpl::PeerImp::getTxSet : tx set hash validated upstream"); + + if (!txSetHash) + return {}; + + auto shaMap = app_.getInboundTransactions().getSet(*txSetHash, false); + if (!shaMap) { if (m->has_querytype() && !m->has_requestcookie()) { // Attempt to relay the request to a peer - if (auto const peer = getPeerWithTree(overlay_, txSetHash, this)) + if (auto const peer = getPeerWithTree(overlay_, *txSetHash, this)) { m->set_requestcookie(id()); peer->send(std::make_shared(*m, protocol::mtGET_LEDGER)); diff --git a/src/xrpld/overlay/detail/PeerImp.h b/src/xrpld/overlay/detail/PeerImp.h index e87c12279eb..3a3a95a6613 100644 --- a/src/xrpld/overlay/detail/PeerImp.h +++ b/src/xrpld/overlay/detail/PeerImp.h @@ -776,7 +776,7 @@ PeerImp::PeerImp( : Child(overlay) , app_(app) , id_(id) - , fingerprint_(getFingerprint(slot->remoteEndpoint(), publicKey, to_string(id_))) + , fingerprint_(getFingerprint(slot->remoteEndpoint(), publicKey, std::to_string(id_))) , prefix_(makePrefix(fingerprint_)) , sink_(app_.getJournal("Peer"), prefix_) , pSink_(app_.getJournal("Protocol"), prefix_) diff --git a/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp b/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp index 19fe2949249..7bf5d789987 100644 --- a/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp +++ b/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp @@ -190,7 +190,7 @@ ledgerFromSpecifier( switch (ledgerCase) { case LedgerCase::kHash: { - if (auto hash = uint256::fromVoidChecked(specifier.hash())) + if (auto hash = uint256::fromRaw(specifier.hash())) { return getLedger(ledger, *hash, context); } diff --git a/src/xrpld/rpc/detail/TransactionSign.cpp b/src/xrpld/rpc/detail/TransactionSign.cpp index 6ea879aa2c7..6f7e36593cc 100644 --- a/src/xrpld/rpc/detail/TransactionSign.cpp +++ b/src/xrpld/rpc/detail/TransactionSign.cpp @@ -555,7 +555,7 @@ transactionPreProcessImpl( { auto const networkId = app.getNetworkIDService().getNetworkID(); if (networkId > 1024) - txJson[jss::NetworkID] = to_string(networkId); + txJson[jss::NetworkID] = std::to_string(networkId); } } diff --git a/src/xrpld/rpc/handlers/admin/status/GetCounts.cpp b/src/xrpld/rpc/handlers/admin/status/GetCounts.cpp index 421f23d2374..52ca7dfcf1e 100644 --- a/src/xrpld/rpc/handlers/admin/status/GetCounts.cpp +++ b/src/xrpld/rpc/handlers/admin/status/GetCounts.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -11,9 +12,11 @@ #include #include +#include #include #include #include +#include namespace xrpl { @@ -43,16 +46,26 @@ textTime( } json::Value -getCountsJson(Application& app, int minObjectCount) +getCountsJson(Application& app, std::uint32_t minObjectCount) { - auto objectCounts = CountedObjects::getInstance().getCounts(minObjectCount); + json::Value ret = json::ValueType::Object; - json::Value ret(json::ValueType::Object); + ret[jss::counters] = [minObjectCount] { + json::Value ctrs = json::ValueType::Object; - for (auto const& [k, v] : objectCounts) - { - ret[k] = v; - } + for (auto const& c : gCountedObjects) + { + if (auto const count = c.count(); count >= minObjectCount) + { + json::Value obj(json::ValueType::Object); + obj[jss::current] = count; + obj[jss::maximum] = std::max(count, c.max()); + ctrs[c.name()] = std::move(obj); + } + } + + return ctrs; + }(); if (app.config().useTxTables()) { @@ -113,7 +126,7 @@ getCountsJson(Application& app, int minObjectCount) json::Value doGetCounts(rpc::JsonContext& context) { - int minCount = 10; + std::uint32_t minCount = 10; if (context.params.isMember(jss::min_count)) minCount = context.params[jss::min_count].asUInt(); diff --git a/src/xrpld/rpc/handlers/admin/status/GetCounts.h b/src/xrpld/rpc/handlers/admin/status/GetCounts.h index 15cfa32a297..4e12d6ef38c 100644 --- a/src/xrpld/rpc/handlers/admin/status/GetCounts.h +++ b/src/xrpld/rpc/handlers/admin/status/GetCounts.h @@ -4,9 +4,11 @@ #include +#include + namespace xrpl { json::Value -getCountsJson(Application& app, int minObjectCount); +getCountsJson(Application& app, std::uint32_t minObjectCount); } // namespace xrpl diff --git a/src/xrpld/rpc/handlers/ledger/LedgerData.cpp b/src/xrpld/rpc/handlers/ledger/LedgerData.cpp index 697d8e52b82..bac22d5c0c1 100644 --- a/src/xrpld/rpc/handlers/ledger/LedgerData.cpp +++ b/src/xrpld/rpc/handlers/ledger/LedgerData.cpp @@ -153,7 +153,7 @@ doLedgerDataGrpc(rpc::GRPCContext& con } uint256 startKey; - if (auto key = uint256::fromVoidChecked(request.marker())) + if (auto key = uint256::fromRaw(request.marker())) { startKey = *key; } @@ -166,7 +166,7 @@ doLedgerDataGrpc(rpc::GRPCContext& con auto e = ledger->sles.end(); if (!request.end_marker().empty()) { - auto const key = uint256::fromVoidChecked(request.end_marker()); + auto const key = uint256::fromRaw(request.end_marker()); if (!key) return {response, {grpc::StatusCode::INVALID_ARGUMENT, "end marker malformed"}}; diff --git a/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp b/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp index 5271720b34f..1d4364145e8 100644 --- a/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp +++ b/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp @@ -1030,7 +1030,7 @@ doLedgerEntryGrpc(rpc::GRPCContext& c return {response, errorStatus}; } - auto const key = uint256::fromVoidChecked(request.key()); + auto const key = uint256::fromRaw(request.key()); if (!key) { grpc::Status const errorStatus{grpc::StatusCode::INVALID_ARGUMENT, "index malformed"}; diff --git a/src/xrpld/rpc/handlers/transaction/Simulate.cpp b/src/xrpld/rpc/handlers/transaction/Simulate.cpp index 61cdddafdef..478b9c7adb8 100644 --- a/src/xrpld/rpc/handlers/transaction/Simulate.cpp +++ b/src/xrpld/rpc/handlers/transaction/Simulate.cpp @@ -159,7 +159,7 @@ autofillTx(json::Value& txJson, rpc::JsonContext& context) { auto const networkId = context.app.getNetworkIDService().getNetworkID(); if (networkId > 1024) - txJson[jss::NetworkID] = to_string(networkId); + txJson[jss::NetworkID] = std::to_string(networkId); } if (!txJson.isMember(jss::Fee))