mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-28 23:15:52 +00:00
Compare commits
8 Commits
a1q123456/
...
vlntb/lock
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab0a72ad93 | ||
|
|
83ae5e3050 | ||
|
|
56f5189a2b | ||
|
|
da694c8304 | ||
|
|
d0f836581b | ||
|
|
984c70955a | ||
|
|
3effb54e49 | ||
|
|
316f9535e3 |
@@ -21,7 +21,6 @@
|
||||
#define RIPPLE_BASICS_SHAMAP_HASH_H_INCLUDED
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/partitioned_unordered_map.h>
|
||||
|
||||
#include <ostream>
|
||||
|
||||
|
||||
@@ -170,9 +170,6 @@ public:
|
||||
bool
|
||||
retrieve(key_type const& key, T& data);
|
||||
|
||||
mutex_type&
|
||||
peekMutex();
|
||||
|
||||
std::vector<key_type>
|
||||
getKeys() const;
|
||||
|
||||
@@ -193,11 +190,14 @@ public:
|
||||
|
||||
private:
|
||||
SharedPointerType
|
||||
initialFetch(key_type const& key, std::lock_guard<mutex_type> const& l);
|
||||
initialFetch(key_type const& key);
|
||||
|
||||
void
|
||||
collect_metrics();
|
||||
|
||||
Mutex&
|
||||
lockPartition(key_type const& key) const;
|
||||
|
||||
private:
|
||||
struct Stats
|
||||
{
|
||||
@@ -300,8 +300,8 @@ private:
|
||||
[[maybe_unused]] clock_type::time_point const& now,
|
||||
typename KeyValueCacheType::map_type& partition,
|
||||
SweptPointersVector& stuffToSweep,
|
||||
std::atomic<int>& allRemovals,
|
||||
std::lock_guard<std::recursive_mutex> const&);
|
||||
std::atomic<int>& allRemoval,
|
||||
Mutex& partitionLock);
|
||||
|
||||
[[nodiscard]] std::thread
|
||||
sweepHelper(
|
||||
@@ -310,14 +310,12 @@ private:
|
||||
typename KeyOnlyCacheType::map_type& partition,
|
||||
SweptPointersVector&,
|
||||
std::atomic<int>& allRemovals,
|
||||
std::lock_guard<std::recursive_mutex> const&);
|
||||
Mutex& partitionLock);
|
||||
|
||||
beast::Journal m_journal;
|
||||
clock_type& m_clock;
|
||||
Stats m_stats;
|
||||
|
||||
mutex_type mutable m_mutex;
|
||||
|
||||
// Used for logging
|
||||
std::string m_name;
|
||||
|
||||
@@ -328,10 +326,11 @@ private:
|
||||
clock_type::duration const m_target_age;
|
||||
|
||||
// Number of items cached
|
||||
int m_cache_count;
|
||||
std::atomic<int> m_cache_count;
|
||||
cache_type m_cache; // Hold strong reference to recent objects
|
||||
std::uint64_t m_hits;
|
||||
std::uint64_t m_misses;
|
||||
std::atomic<std::uint64_t> m_hits;
|
||||
std::atomic<std::uint64_t> m_misses;
|
||||
mutable std::vector<mutex_type> partitionLocks_;
|
||||
};
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <xrpl/basics/IntrusivePointer.ipp>
|
||||
#include <xrpl/basics/TaggedCache.h>
|
||||
#include <xrpl/beast/core/CurrentThreadName.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -60,6 +61,7 @@ inline TaggedCache<
|
||||
, m_hits(0)
|
||||
, m_misses(0)
|
||||
{
|
||||
partitionLocks_ = std::vector<mutex_type>(m_cache.partitions());
|
||||
}
|
||||
|
||||
template <
|
||||
@@ -105,8 +107,13 @@ TaggedCache<
|
||||
KeyEqual,
|
||||
Mutex>::size() const
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
return m_cache.size();
|
||||
std::size_t totalSize = 0;
|
||||
for (size_t i = 0; i < partitionLocks_.size(); ++i)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(partitionLocks_[i]);
|
||||
totalSize += m_cache.map()[i].size();
|
||||
}
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
template <
|
||||
@@ -129,8 +136,7 @@ TaggedCache<
|
||||
KeyEqual,
|
||||
Mutex>::getCacheSize() const
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
return m_cache_count;
|
||||
return m_cache_count.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
template <
|
||||
@@ -153,8 +159,7 @@ TaggedCache<
|
||||
KeyEqual,
|
||||
Mutex>::getTrackSize() const
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
return m_cache.size();
|
||||
return size();
|
||||
}
|
||||
|
||||
template <
|
||||
@@ -177,9 +182,10 @@ TaggedCache<
|
||||
KeyEqual,
|
||||
Mutex>::getHitRate()
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
auto const total = static_cast<float>(m_hits + m_misses);
|
||||
return m_hits * (100.0f / std::max(1.0f, total));
|
||||
auto hits = m_hits.load(std::memory_order_relaxed);
|
||||
auto misses = m_misses.load(std::memory_order_relaxed);
|
||||
float total = float(hits + misses);
|
||||
return hits * (100.0f / std::max(1.0f, total));
|
||||
}
|
||||
|
||||
template <
|
||||
@@ -202,9 +208,12 @@ TaggedCache<
|
||||
KeyEqual,
|
||||
Mutex>::clear()
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
for (auto& mutex : partitionLocks_)
|
||||
mutex.lock();
|
||||
m_cache.clear();
|
||||
m_cache_count = 0;
|
||||
for (auto& mutex : partitionLocks_)
|
||||
mutex.unlock();
|
||||
m_cache_count.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
template <
|
||||
@@ -227,11 +236,14 @@ TaggedCache<
|
||||
KeyEqual,
|
||||
Mutex>::reset()
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
for (auto& mutex : partitionLocks_)
|
||||
mutex.lock();
|
||||
m_cache.clear();
|
||||
m_cache_count = 0;
|
||||
m_hits = 0;
|
||||
m_misses = 0;
|
||||
for (auto& mutex : partitionLocks_)
|
||||
mutex.unlock();
|
||||
m_cache_count.store(0, std::memory_order_relaxed);
|
||||
m_hits.store(0, std::memory_order_relaxed);
|
||||
m_misses.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
template <
|
||||
@@ -255,7 +267,7 @@ TaggedCache<
|
||||
KeyEqual,
|
||||
Mutex>::touch_if_exists(KeyComparable const& key)
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
std::lock_guard<Mutex> lock(lockPartition(key));
|
||||
auto const iter(m_cache.find(key));
|
||||
if (iter == m_cache.end())
|
||||
{
|
||||
@@ -297,8 +309,6 @@ TaggedCache<
|
||||
|
||||
auto const start = std::chrono::steady_clock::now();
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
if (m_target_size == 0 ||
|
||||
(static_cast<int>(m_cache.size()) <= m_target_size))
|
||||
{
|
||||
@@ -330,12 +340,13 @@ TaggedCache<
|
||||
m_cache.map()[p],
|
||||
allStuffToSweep[p],
|
||||
allRemovals,
|
||||
lock));
|
||||
partitionLocks_[p]));
|
||||
}
|
||||
for (std::thread& worker : workers)
|
||||
worker.join();
|
||||
|
||||
m_cache_count -= allRemovals;
|
||||
int removals = allRemovals.load(std::memory_order_relaxed);
|
||||
m_cache_count.fetch_sub(removals, std::memory_order_relaxed);
|
||||
}
|
||||
// At this point allStuffToSweep will go out of scope outside the lock
|
||||
// and decrement the reference count on each strong pointer.
|
||||
@@ -369,7 +380,8 @@ TaggedCache<
|
||||
{
|
||||
// Remove from cache, if !valid, remove from map too. Returns true if
|
||||
// removed from cache
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
std::lock_guard<Mutex> lock(lockPartition(key));
|
||||
|
||||
auto cit = m_cache.find(key);
|
||||
|
||||
@@ -382,7 +394,7 @@ TaggedCache<
|
||||
|
||||
if (entry.isCached())
|
||||
{
|
||||
--m_cache_count;
|
||||
m_cache_count.fetch_sub(1, std::memory_order_relaxed);
|
||||
entry.ptr.convertToWeak();
|
||||
ret = true;
|
||||
}
|
||||
@@ -420,17 +432,16 @@ TaggedCache<
|
||||
{
|
||||
// Return canonical value, store if needed, refresh in cache
|
||||
// Return values: true=we had the data already
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
std::lock_guard<Mutex> lock(lockPartition(key));
|
||||
auto cit = m_cache.find(key);
|
||||
|
||||
if (cit == m_cache.end())
|
||||
{
|
||||
m_cache.emplace(
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple(key),
|
||||
std::forward_as_tuple(m_clock.now(), data));
|
||||
++m_cache_count;
|
||||
m_cache_count.fetch_add(1, std::memory_order_relaxed);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -479,12 +490,12 @@ TaggedCache<
|
||||
data = cachedData;
|
||||
}
|
||||
|
||||
++m_cache_count;
|
||||
m_cache_count.fetch_add(1, std::memory_order_relaxed);
|
||||
return true;
|
||||
}
|
||||
|
||||
entry.ptr = data;
|
||||
++m_cache_count;
|
||||
m_cache_count.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -560,10 +571,11 @@ TaggedCache<
|
||||
KeyEqual,
|
||||
Mutex>::fetch(key_type const& key)
|
||||
{
|
||||
std::lock_guard<mutex_type> l(m_mutex);
|
||||
auto ret = initialFetch(key, l);
|
||||
std::lock_guard<Mutex> lock(lockPartition(key));
|
||||
|
||||
auto ret = initialFetch(key);
|
||||
if (!ret)
|
||||
++m_misses;
|
||||
m_misses.fetch_add(1, std::memory_order_relaxed);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -627,8 +639,8 @@ TaggedCache<
|
||||
Mutex>::insert(key_type const& key)
|
||||
-> std::enable_if_t<IsKeyCache, ReturnType>
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
clock_type::time_point const now(m_clock.now());
|
||||
std::lock_guard<Mutex> lock(lockPartition(key));
|
||||
auto [it, inserted] = m_cache.emplace(
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple(key),
|
||||
@@ -668,29 +680,6 @@ TaggedCache<
|
||||
return true;
|
||||
}
|
||||
|
||||
template <
|
||||
class Key,
|
||||
class T,
|
||||
bool IsKeyCache,
|
||||
class SharedWeakUnionPointer,
|
||||
class SharedPointerType,
|
||||
class Hash,
|
||||
class KeyEqual,
|
||||
class Mutex>
|
||||
inline auto
|
||||
TaggedCache<
|
||||
Key,
|
||||
T,
|
||||
IsKeyCache,
|
||||
SharedWeakUnionPointer,
|
||||
SharedPointerType,
|
||||
Hash,
|
||||
KeyEqual,
|
||||
Mutex>::peekMutex() -> mutex_type&
|
||||
{
|
||||
return m_mutex;
|
||||
}
|
||||
|
||||
template <
|
||||
class Key,
|
||||
class T,
|
||||
@@ -714,10 +703,13 @@ TaggedCache<
|
||||
std::vector<key_type> v;
|
||||
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
v.reserve(m_cache.size());
|
||||
for (auto const& _ : m_cache)
|
||||
v.push_back(_.first);
|
||||
for (std::size_t i = 0; i < partitionLocks_.size(); ++i)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(partitionLocks_[i]);
|
||||
for (auto const& entry : m_cache.map()[i])
|
||||
v.push_back(entry.first);
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
@@ -743,11 +735,12 @@ TaggedCache<
|
||||
KeyEqual,
|
||||
Mutex>::rate() const
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
auto const tot = m_hits + m_misses;
|
||||
auto hits = m_hits.load(std::memory_order_relaxed);
|
||||
auto misses = m_misses.load(std::memory_order_relaxed);
|
||||
auto const tot = hits + misses;
|
||||
if (tot == 0)
|
||||
return 0;
|
||||
return double(m_hits) / tot;
|
||||
return 0.0;
|
||||
return double(hits) / tot;
|
||||
}
|
||||
|
||||
template <
|
||||
@@ -771,18 +764,16 @@ TaggedCache<
|
||||
KeyEqual,
|
||||
Mutex>::fetch(key_type const& digest, Handler const& h)
|
||||
{
|
||||
{
|
||||
std::lock_guard l(m_mutex);
|
||||
if (auto ret = initialFetch(digest, l))
|
||||
return ret;
|
||||
}
|
||||
std::lock_guard<Mutex> lock(lockPartition(digest));
|
||||
|
||||
if (auto ret = initialFetch(digest))
|
||||
return ret;
|
||||
|
||||
auto sle = h();
|
||||
if (!sle)
|
||||
return {};
|
||||
|
||||
std::lock_guard l(m_mutex);
|
||||
++m_misses;
|
||||
m_misses.fetch_add(1, std::memory_order_relaxed);
|
||||
auto const [it, inserted] =
|
||||
m_cache.emplace(digest, Entry(m_clock.now(), std::move(sle)));
|
||||
if (!inserted)
|
||||
@@ -809,9 +800,10 @@ TaggedCache<
|
||||
SharedPointerType,
|
||||
Hash,
|
||||
KeyEqual,
|
||||
Mutex>::
|
||||
initialFetch(key_type const& key, std::lock_guard<mutex_type> const& l)
|
||||
Mutex>::initialFetch(key_type const& key)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(lockPartition(key));
|
||||
|
||||
auto cit = m_cache.find(key);
|
||||
if (cit == m_cache.end())
|
||||
return {};
|
||||
@@ -819,7 +811,7 @@ TaggedCache<
|
||||
Entry& entry = cit->second;
|
||||
if (entry.isCached())
|
||||
{
|
||||
++m_hits;
|
||||
m_hits.fetch_add(1, std::memory_order_relaxed);
|
||||
entry.touch(m_clock.now());
|
||||
return entry.ptr.getStrong();
|
||||
}
|
||||
@@ -827,12 +819,13 @@ TaggedCache<
|
||||
if (entry.isCached())
|
||||
{
|
||||
// independent of cache size, so not counted as a hit
|
||||
++m_cache_count;
|
||||
m_cache_count.fetch_add(1, std::memory_order_relaxed);
|
||||
entry.touch(m_clock.now());
|
||||
return entry.ptr.getStrong();
|
||||
}
|
||||
|
||||
m_cache.erase(cit);
|
||||
m_cache.erase(cit); // TODO: if this erase happens on fetch, what is left
|
||||
// for a sweep?
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -861,10 +854,11 @@ TaggedCache<
|
||||
{
|
||||
beast::insight::Gauge::value_type hit_rate(0);
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
auto const total(m_hits + m_misses);
|
||||
auto const hits = m_hits.load(std::memory_order_relaxed);
|
||||
auto const misses = m_misses.load(std::memory_order_relaxed);
|
||||
auto const total = hits + misses;
|
||||
if (total != 0)
|
||||
hit_rate = (m_hits * 100) / total;
|
||||
hit_rate = (hits * 100) / total;
|
||||
}
|
||||
m_stats.hit_rate.set(hit_rate);
|
||||
}
|
||||
@@ -895,12 +889,16 @@ TaggedCache<
|
||||
typename KeyValueCacheType::map_type& partition,
|
||||
SweptPointersVector& stuffToSweep,
|
||||
std::atomic<int>& allRemovals,
|
||||
std::lock_guard<std::recursive_mutex> const&)
|
||||
Mutex& partitionLock)
|
||||
{
|
||||
return std::thread([&, this]() {
|
||||
beast::setCurrentThreadName("sweep-1");
|
||||
|
||||
int cacheRemovals = 0;
|
||||
int mapRemovals = 0;
|
||||
|
||||
std::lock_guard<Mutex> lock(partitionLock);
|
||||
|
||||
// Keep references to all the stuff we sweep
|
||||
// so that we can destroy them outside the lock.
|
||||
stuffToSweep.reserve(partition.size());
|
||||
@@ -984,12 +982,16 @@ TaggedCache<
|
||||
typename KeyOnlyCacheType::map_type& partition,
|
||||
SweptPointersVector&,
|
||||
std::atomic<int>& allRemovals,
|
||||
std::lock_guard<std::recursive_mutex> const&)
|
||||
Mutex& partitionLock)
|
||||
{
|
||||
return std::thread([&, this]() {
|
||||
beast::setCurrentThreadName("sweep-2");
|
||||
|
||||
int cacheRemovals = 0;
|
||||
int mapRemovals = 0;
|
||||
|
||||
std::lock_guard<Mutex> lock(partitionLock);
|
||||
|
||||
// Keep references to all the stuff we sweep
|
||||
// so that we can destroy them outside the lock.
|
||||
{
|
||||
@@ -1024,6 +1026,29 @@ TaggedCache<
|
||||
});
|
||||
}
|
||||
|
||||
template <
|
||||
class Key,
|
||||
class T,
|
||||
bool IsKeyCache,
|
||||
class SharedWeakUnionPointer,
|
||||
class SharedPointerType,
|
||||
class Hash,
|
||||
class KeyEqual,
|
||||
class Mutex>
|
||||
inline Mutex&
|
||||
TaggedCache<
|
||||
Key,
|
||||
T,
|
||||
IsKeyCache,
|
||||
SharedWeakUnionPointer,
|
||||
SharedPointerType,
|
||||
Hash,
|
||||
KeyEqual,
|
||||
Mutex>::lockPartition(key_type const& key) const
|
||||
{
|
||||
return partitionLocks_[m_cache.partition_index(key)];
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -277,6 +277,12 @@ public:
|
||||
return map_;
|
||||
}
|
||||
|
||||
partition_map_type const&
|
||||
map() const
|
||||
{
|
||||
return map_;
|
||||
}
|
||||
|
||||
iterator
|
||||
begin()
|
||||
{
|
||||
@@ -321,6 +327,12 @@ public:
|
||||
return cend();
|
||||
}
|
||||
|
||||
std::size_t
|
||||
partition_index(key_type const& key) const
|
||||
{
|
||||
return partitioner(key);
|
||||
}
|
||||
|
||||
private:
|
||||
template <class T>
|
||||
void
|
||||
@@ -380,7 +392,7 @@ public:
|
||||
clear()
|
||||
{
|
||||
for (auto& p : map_)
|
||||
p.clear();
|
||||
p.clear(); // TODO make sure that it is locked inside
|
||||
}
|
||||
|
||||
iterator
|
||||
@@ -406,7 +418,7 @@ public:
|
||||
{
|
||||
std::size_t ret = 0;
|
||||
for (auto& p : map_)
|
||||
ret += p.size();
|
||||
ret += p.size(); // TODO make sure that it is locked inside
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
#include <xrpl/basics/ByteUtilities.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/partitioned_unordered_map.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
74
src/test/protocol/ApiVersion_test.cpp
Normal file
74
src/test/protocol/ApiVersion_test.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/XRPLF/rippled/
|
||||
Copyright (c) 2023 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/protocol/ApiVersion.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
struct ApiVersion_test : beast::unit_test::suite
|
||||
{
|
||||
void
|
||||
run() override
|
||||
{
|
||||
{
|
||||
testcase("API versions invariants");
|
||||
|
||||
static_assert(
|
||||
RPC::apiMinimumSupportedVersion <=
|
||||
RPC::apiMaximumSupportedVersion);
|
||||
static_assert(
|
||||
RPC::apiMinimumSupportedVersion <= RPC::apiMaximumValidVersion);
|
||||
static_assert(
|
||||
RPC::apiMaximumSupportedVersion <= RPC::apiMaximumValidVersion);
|
||||
static_assert(RPC::apiBetaVersion <= RPC::apiMaximumValidVersion);
|
||||
|
||||
BEAST_EXPECT(true);
|
||||
}
|
||||
|
||||
{
|
||||
// Update when we change versions
|
||||
testcase("API versions");
|
||||
|
||||
static_assert(RPC::apiMinimumSupportedVersion >= 1);
|
||||
static_assert(RPC::apiMinimumSupportedVersion < 2);
|
||||
static_assert(RPC::apiMaximumSupportedVersion >= 2);
|
||||
static_assert(RPC::apiMaximumSupportedVersion < 3);
|
||||
static_assert(RPC::apiMaximumValidVersion >= 3);
|
||||
static_assert(RPC::apiMaximumValidVersion < 4);
|
||||
static_assert(RPC::apiBetaVersion >= 3);
|
||||
static_assert(RPC::apiBetaVersion < 4);
|
||||
|
||||
BEAST_EXPECT(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(ApiVersion, protocol, ripple);
|
||||
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
118
src/test/protocol/BuildInfo_test.cpp
Normal file
118
src/test/protocol/BuildInfo_test.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2020 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
#include <xrpl/protocol/BuildInfo.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class BuildInfo_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
testEncodeSoftwareVersion()
|
||||
{
|
||||
testcase("EncodeSoftwareVersion");
|
||||
|
||||
auto encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.3-b7");
|
||||
|
||||
// the first two bytes identify the particular implementation, 0x183B
|
||||
BEAST_EXPECT(
|
||||
(encodedVersion & 0xFFFF'0000'0000'0000LLU) ==
|
||||
0x183B'0000'0000'0000LLU);
|
||||
|
||||
// the next three bytes: major version, minor version, patch version,
|
||||
// 0x010203
|
||||
BEAST_EXPECT(
|
||||
(encodedVersion & 0x0000'FFFF'FF00'0000LLU) ==
|
||||
0x0000'0102'0300'0000LLU);
|
||||
|
||||
// the next two bits:
|
||||
{
|
||||
// 01 if a beta
|
||||
BEAST_EXPECT(
|
||||
(encodedVersion & 0x0000'0000'00C0'0000LLU) >> 22 == 0b01);
|
||||
// 10 if an RC
|
||||
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.4-rc7");
|
||||
BEAST_EXPECT(
|
||||
(encodedVersion & 0x0000'0000'00C0'0000LLU) >> 22 == 0b10);
|
||||
// 11 if neither an RC nor a beta
|
||||
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.5");
|
||||
BEAST_EXPECT(
|
||||
(encodedVersion & 0x0000'0000'00C0'0000LLU) >> 22 == 0b11);
|
||||
}
|
||||
|
||||
// the next six bits: rc/beta number (1-63)
|
||||
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.6-b63");
|
||||
BEAST_EXPECT((encodedVersion & 0x0000'0000'003F'0000LLU) >> 16 == 63);
|
||||
|
||||
// the last two bytes are zeros
|
||||
BEAST_EXPECT((encodedVersion & 0x0000'0000'0000'FFFFLLU) == 0);
|
||||
|
||||
// Test some version strings with wrong formats:
|
||||
// no rc/beta number
|
||||
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.3-b");
|
||||
BEAST_EXPECT((encodedVersion & 0x0000'0000'00FF'0000LLU) == 0);
|
||||
// rc/beta number out of range
|
||||
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.3-b64");
|
||||
BEAST_EXPECT((encodedVersion & 0x0000'0000'00FF'0000LLU) == 0);
|
||||
|
||||
// Check that the rc/beta number of a release is 0:
|
||||
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.6");
|
||||
BEAST_EXPECT((encodedVersion & 0x0000'0000'003F'0000LLU) == 0);
|
||||
}
|
||||
|
||||
void
|
||||
testIsRippledVersion()
|
||||
{
|
||||
testcase("IsRippledVersion");
|
||||
auto vFF = 0xFFFF'FFFF'FFFF'FFFFLLU;
|
||||
BEAST_EXPECT(!BuildInfo::isRippledVersion(vFF));
|
||||
auto vRippled = 0x183B'0000'0000'0000LLU;
|
||||
BEAST_EXPECT(BuildInfo::isRippledVersion(vRippled));
|
||||
}
|
||||
|
||||
void
|
||||
testIsNewerVersion()
|
||||
{
|
||||
testcase("IsNewerVersion");
|
||||
auto vFF = 0xFFFF'FFFF'FFFF'FFFFLLU;
|
||||
BEAST_EXPECT(!BuildInfo::isNewerVersion(vFF));
|
||||
|
||||
auto v159 = BuildInfo::encodeSoftwareVersion("1.5.9");
|
||||
BEAST_EXPECT(!BuildInfo::isNewerVersion(v159));
|
||||
|
||||
auto vCurrent = BuildInfo::getEncodedVersion();
|
||||
BEAST_EXPECT(!BuildInfo::isNewerVersion(vCurrent));
|
||||
|
||||
auto vMax = BuildInfo::encodeSoftwareVersion("255.255.255");
|
||||
BEAST_EXPECT(BuildInfo::isNewerVersion(vMax));
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testEncodeSoftwareVersion();
|
||||
testIsRippledVersion();
|
||||
testIsNewerVersion();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(BuildInfo, protocol, ripple);
|
||||
} // namespace ripple
|
||||
198
src/test/protocol/Hooks_test.cpp
Normal file
198
src/test/protocol/Hooks_test.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2017 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <test/jtx.h>
|
||||
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class Hooks_test : public beast::unit_test::suite
|
||||
{
|
||||
/**
|
||||
* This unit test was requested here:
|
||||
* https://github.com/ripple/rippled/pull/4089#issuecomment-1050274539
|
||||
* These are tests that exercise facilities that are reserved for when Hooks
|
||||
* is merged in the future.
|
||||
**/
|
||||
|
||||
void
|
||||
testHookFields()
|
||||
{
|
||||
testcase("Test Hooks fields");
|
||||
|
||||
using namespace test::jtx;
|
||||
|
||||
std::vector<std::reference_wrapper<SField const>> fields_to_test = {
|
||||
sfHookResult,
|
||||
sfHookStateChangeCount,
|
||||
sfHookEmitCount,
|
||||
sfHookExecutionIndex,
|
||||
sfHookApiVersion,
|
||||
sfHookStateCount,
|
||||
sfEmitGeneration,
|
||||
sfHookOn,
|
||||
sfHookInstructionCount,
|
||||
sfEmitBurden,
|
||||
sfHookReturnCode,
|
||||
sfReferenceCount,
|
||||
sfEmitParentTxnID,
|
||||
sfEmitNonce,
|
||||
sfEmitHookHash,
|
||||
sfHookStateKey,
|
||||
sfHookHash,
|
||||
sfHookNamespace,
|
||||
sfHookSetTxnID,
|
||||
sfHookStateData,
|
||||
sfHookReturnString,
|
||||
sfHookParameterName,
|
||||
sfHookParameterValue,
|
||||
sfEmitCallback,
|
||||
sfHookAccount,
|
||||
sfEmittedTxn,
|
||||
sfHook,
|
||||
sfHookDefinition,
|
||||
sfHookParameter,
|
||||
sfHookGrant,
|
||||
sfEmitDetails,
|
||||
sfHookExecutions,
|
||||
sfHookExecution,
|
||||
sfHookParameters,
|
||||
sfHooks,
|
||||
sfHookGrants};
|
||||
|
||||
for (auto const& rf : fields_to_test)
|
||||
{
|
||||
SField const& f = rf.get();
|
||||
|
||||
STObject dummy{sfGeneric};
|
||||
|
||||
BEAST_EXPECT(!dummy.isFieldPresent(f));
|
||||
|
||||
switch (f.fieldType)
|
||||
{
|
||||
case STI_UINT8: {
|
||||
dummy.setFieldU8(f, 0);
|
||||
BEAST_EXPECT(dummy.getFieldU8(f) == 0);
|
||||
|
||||
dummy.setFieldU8(f, 255);
|
||||
BEAST_EXPECT(dummy.getFieldU8(f) == 255);
|
||||
|
||||
BEAST_EXPECT(dummy.isFieldPresent(f));
|
||||
break;
|
||||
}
|
||||
|
||||
case STI_UINT16: {
|
||||
dummy.setFieldU16(f, 0);
|
||||
BEAST_EXPECT(dummy.getFieldU16(f) == 0);
|
||||
|
||||
dummy.setFieldU16(f, 0xFFFFU);
|
||||
BEAST_EXPECT(dummy.getFieldU16(f) == 0xFFFFU);
|
||||
|
||||
BEAST_EXPECT(dummy.isFieldPresent(f));
|
||||
break;
|
||||
}
|
||||
|
||||
case STI_UINT32: {
|
||||
dummy.setFieldU32(f, 0);
|
||||
BEAST_EXPECT(dummy.getFieldU32(f) == 0);
|
||||
|
||||
dummy.setFieldU32(f, 0xFFFFFFFFU);
|
||||
BEAST_EXPECT(dummy.getFieldU32(f) == 0xFFFFFFFFU);
|
||||
|
||||
BEAST_EXPECT(dummy.isFieldPresent(f));
|
||||
break;
|
||||
}
|
||||
|
||||
case STI_UINT64: {
|
||||
dummy.setFieldU64(f, 0);
|
||||
BEAST_EXPECT(dummy.getFieldU64(f) == 0);
|
||||
|
||||
dummy.setFieldU64(f, 0xFFFFFFFFFFFFFFFFU);
|
||||
BEAST_EXPECT(dummy.getFieldU64(f) == 0xFFFFFFFFFFFFFFFFU);
|
||||
|
||||
BEAST_EXPECT(dummy.isFieldPresent(f));
|
||||
break;
|
||||
}
|
||||
|
||||
case STI_UINT256: {
|
||||
uint256 u = uint256::fromVoid(
|
||||
"DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBE"
|
||||
"EFDEADBEEF");
|
||||
dummy.setFieldH256(f, u);
|
||||
BEAST_EXPECT(dummy.getFieldH256(f) == u);
|
||||
BEAST_EXPECT(dummy.isFieldPresent(f));
|
||||
break;
|
||||
}
|
||||
|
||||
case STI_VL: {
|
||||
std::vector<uint8_t> v{1, 2, 3};
|
||||
dummy.setFieldVL(f, v);
|
||||
BEAST_EXPECT(dummy.getFieldVL(f) == v);
|
||||
BEAST_EXPECT(dummy.isFieldPresent(f));
|
||||
break;
|
||||
}
|
||||
|
||||
case STI_ACCOUNT: {
|
||||
AccountID id = *parseBase58<AccountID>(
|
||||
"rwfSjJNK2YQuN64bSWn7T2eY9FJAyAPYJT");
|
||||
dummy.setAccountID(f, id);
|
||||
BEAST_EXPECT(dummy.getAccountID(f) == id);
|
||||
BEAST_EXPECT(dummy.isFieldPresent(f));
|
||||
break;
|
||||
}
|
||||
|
||||
case STI_OBJECT: {
|
||||
dummy.emplace_back(STObject{f});
|
||||
BEAST_EXPECT(dummy.getField(f).getFName() == f);
|
||||
BEAST_EXPECT(dummy.isFieldPresent(f));
|
||||
break;
|
||||
}
|
||||
|
||||
case STI_ARRAY: {
|
||||
STArray dummy2{f, 2};
|
||||
dummy2.push_back(STObject{sfGeneric});
|
||||
dummy2.push_back(STObject{sfGeneric});
|
||||
dummy.setFieldArray(f, dummy2);
|
||||
BEAST_EXPECT(dummy.getFieldArray(f) == dummy2);
|
||||
BEAST_EXPECT(dummy.isFieldPresent(f));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
BEAST_EXPECT(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
using namespace test::jtx;
|
||||
testHookFields();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Hooks, protocol, ripple);
|
||||
|
||||
} // namespace ripple
|
||||
@@ -17,14 +17,15 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <test/jtx.h>
|
||||
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/json/json_reader.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/STParsedJSON.h>
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
#include <xrpl/json/json_reader.h> // Json::Reader
|
||||
#include <xrpl/protocol/ErrorCodes.h> // RPC::containsError
|
||||
#include <xrpl/protocol/STParsedJSON.h> // STParsedJSONObject
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
using namespace ripple;
|
||||
namespace ripple {
|
||||
|
||||
namespace InnerObjectFormatsUnitTestDetail {
|
||||
|
||||
@@ -35,6 +36,7 @@ struct TestJSONTxt
|
||||
};
|
||||
|
||||
static TestJSONTxt const testArray[] = {
|
||||
|
||||
// Valid SignerEntry
|
||||
{R"({
|
||||
"Account" : "rDg53Haik2475DJx8bjMDSDPj4VX7htaMd",
|
||||
@@ -59,6 +61,7 @@ static TestJSONTxt const testArray[] = {
|
||||
"TransactionType" : "SignerListSet"
|
||||
})",
|
||||
false},
|
||||
|
||||
// SignerEntry missing Account
|
||||
{R"({
|
||||
"Account" : "rDg53Haik2475DJx8bjMDSDPj4VX7htaMd",
|
||||
@@ -82,6 +85,7 @@ static TestJSONTxt const testArray[] = {
|
||||
"TransactionType" : "SignerListSet"
|
||||
})",
|
||||
true},
|
||||
|
||||
// SignerEntry missing SignerWeight
|
||||
{R"({
|
||||
"Account" : "rDg53Haik2475DJx8bjMDSDPj4VX7htaMd",
|
||||
@@ -105,6 +109,7 @@ static TestJSONTxt const testArray[] = {
|
||||
"TransactionType" : "SignerListSet"
|
||||
})",
|
||||
true},
|
||||
|
||||
// SignerEntry with unexpected Amount
|
||||
{R"({
|
||||
"Account" : "rDg53Haik2475DJx8bjMDSDPj4VX7htaMd",
|
||||
@@ -130,6 +135,7 @@ static TestJSONTxt const testArray[] = {
|
||||
"TransactionType" : "SignerListSet"
|
||||
})",
|
||||
true},
|
||||
|
||||
// SignerEntry with no Account and unexpected Amount
|
||||
{R"({
|
||||
"Account" : "rDg53Haik2475DJx8bjMDSDPj4VX7htaMd",
|
||||
@@ -154,36 +160,47 @@ static TestJSONTxt const testArray[] = {
|
||||
"TransactionType" : "SignerListSet"
|
||||
})",
|
||||
true},
|
||||
|
||||
};
|
||||
|
||||
} // namespace InnerObjectFormatsUnitTestDetail
|
||||
|
||||
TEST_SUITE_BEGIN("InnerObjectFormatsParsedJSON");
|
||||
|
||||
TEST_CASE("InnerObjectFormatsParsedJSON")
|
||||
class InnerObjectFormatsParsedJSON_test : public beast::unit_test::suite
|
||||
{
|
||||
using namespace InnerObjectFormatsUnitTestDetail;
|
||||
|
||||
for (auto const& test : testArray)
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
Json::Value req;
|
||||
Json::Reader().parse(test.txt, req);
|
||||
if (RPC::contains_error(req))
|
||||
using namespace InnerObjectFormatsUnitTestDetail;
|
||||
|
||||
// Instantiate a jtx::Env so debugLog writes are exercised.
|
||||
test::jtx::Env env(*this);
|
||||
|
||||
for (auto const& test : testArray)
|
||||
{
|
||||
Throw<std::runtime_error>(
|
||||
"Internal InnerObjectFormatsParsedJSON error. Bad JSON.");
|
||||
}
|
||||
STParsedJSONObject parsed("request", req);
|
||||
bool const noObj = !parsed.object.has_value();
|
||||
if (noObj == test.expectFail)
|
||||
CHECK(true);
|
||||
else
|
||||
{
|
||||
std::string errStr("Unexpected STParsedJSON result on:\n");
|
||||
errStr += test.txt;
|
||||
FAIL(errStr);
|
||||
Json::Value req;
|
||||
Json::Reader().parse(test.txt, req);
|
||||
if (RPC::contains_error(req))
|
||||
{
|
||||
Throw<std::runtime_error>(
|
||||
"Internal InnerObjectFormatsParsedJSON error. Bad JSON.");
|
||||
}
|
||||
STParsedJSONObject parsed("request", req);
|
||||
bool const noObj = !parsed.object.has_value();
|
||||
if (noObj == test.expectFail)
|
||||
{
|
||||
pass();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string errStr("Unexpected STParsedJSON result on:\n");
|
||||
errStr += test.txt;
|
||||
fail(errStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_SUITE_END();
|
||||
BEAST_DEFINE_TESTSUITE(InnerObjectFormatsParsedJSON, ripple_app, ripple);
|
||||
|
||||
} // namespace ripple
|
||||
983
src/test/protocol/Issue_test.cpp
Normal file
983
src/test/protocol/Issue_test.cpp
Normal file
@@ -0,0 +1,983 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpl/basics/UnorderedContainers.h>
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
#include <xrpl/protocol/Book.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <typeinfo>
|
||||
#include <unordered_set>
|
||||
|
||||
#if BEAST_MSVC
|
||||
#define STL_SET_HAS_EMPLACE 1
|
||||
#else
|
||||
#define STL_SET_HAS_EMPLACE 0
|
||||
#endif
|
||||
|
||||
#ifndef RIPPLE_ASSETS_ENABLE_STD_HASH
|
||||
#if BEAST_MAC || BEAST_IOS
|
||||
#define RIPPLE_ASSETS_ENABLE_STD_HASH 0
|
||||
#else
|
||||
#define RIPPLE_ASSETS_ENABLE_STD_HASH 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class Issue_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
using Domain = uint256;
|
||||
|
||||
// Comparison, hash tests for uint60 (via base_uint)
|
||||
template <typename Unsigned>
|
||||
void
|
||||
testUnsigned()
|
||||
{
|
||||
Unsigned const u1(1);
|
||||
Unsigned const u2(2);
|
||||
Unsigned const u3(3);
|
||||
|
||||
BEAST_EXPECT(u1 != u2);
|
||||
BEAST_EXPECT(u1 < u2);
|
||||
BEAST_EXPECT(u1 <= u2);
|
||||
BEAST_EXPECT(u2 <= u2);
|
||||
BEAST_EXPECT(u2 == u2);
|
||||
BEAST_EXPECT(u2 >= u2);
|
||||
BEAST_EXPECT(u3 >= u2);
|
||||
BEAST_EXPECT(u3 > u2);
|
||||
|
||||
std::hash<Unsigned> hash;
|
||||
|
||||
BEAST_EXPECT(hash(u1) == hash(u1));
|
||||
BEAST_EXPECT(hash(u2) == hash(u2));
|
||||
BEAST_EXPECT(hash(u3) == hash(u3));
|
||||
BEAST_EXPECT(hash(u1) != hash(u2));
|
||||
BEAST_EXPECT(hash(u1) != hash(u3));
|
||||
BEAST_EXPECT(hash(u2) != hash(u3));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// Comparison, hash tests for Issue
|
||||
template <class Issue>
|
||||
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);
|
||||
|
||||
BEAST_EXPECT(Issue(c1, i1) != Issue(c2, i1));
|
||||
BEAST_EXPECT(Issue(c1, i1) < Issue(c2, i1));
|
||||
BEAST_EXPECT(Issue(c1, i1) <= Issue(c2, i1));
|
||||
BEAST_EXPECT(Issue(c2, i1) <= Issue(c2, i1));
|
||||
BEAST_EXPECT(Issue(c2, i1) == Issue(c2, i1));
|
||||
BEAST_EXPECT(Issue(c2, i1) >= Issue(c2, i1));
|
||||
BEAST_EXPECT(Issue(c3, i1) >= Issue(c2, i1));
|
||||
BEAST_EXPECT(Issue(c3, i1) > Issue(c2, i1));
|
||||
BEAST_EXPECT(Issue(c1, i1) != Issue(c1, i2));
|
||||
BEAST_EXPECT(Issue(c1, i1) < Issue(c1, i2));
|
||||
BEAST_EXPECT(Issue(c1, i1) <= Issue(c1, i2));
|
||||
BEAST_EXPECT(Issue(c1, i2) <= Issue(c1, i2));
|
||||
BEAST_EXPECT(Issue(c1, i2) == Issue(c1, i2));
|
||||
BEAST_EXPECT(Issue(c1, i2) >= Issue(c1, i2));
|
||||
BEAST_EXPECT(Issue(c1, i3) >= Issue(c1, i2));
|
||||
BEAST_EXPECT(Issue(c1, i3) > Issue(c1, i2));
|
||||
|
||||
std::hash<Issue> hash;
|
||||
|
||||
BEAST_EXPECT(hash(Issue(c1, i1)) == hash(Issue(c1, i1)));
|
||||
BEAST_EXPECT(hash(Issue(c1, i2)) == hash(Issue(c1, i2)));
|
||||
BEAST_EXPECT(hash(Issue(c1, i3)) == hash(Issue(c1, i3)));
|
||||
BEAST_EXPECT(hash(Issue(c2, i1)) == hash(Issue(c2, i1)));
|
||||
BEAST_EXPECT(hash(Issue(c2, i2)) == hash(Issue(c2, i2)));
|
||||
BEAST_EXPECT(hash(Issue(c2, i3)) == hash(Issue(c2, i3)));
|
||||
BEAST_EXPECT(hash(Issue(c3, i1)) == hash(Issue(c3, i1)));
|
||||
BEAST_EXPECT(hash(Issue(c3, i2)) == hash(Issue(c3, i2)));
|
||||
BEAST_EXPECT(hash(Issue(c3, i3)) == hash(Issue(c3, i3)));
|
||||
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c1, i2)));
|
||||
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c1, i3)));
|
||||
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c2, i1)));
|
||||
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c2, i2)));
|
||||
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c2, i3)));
|
||||
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c3, i1)));
|
||||
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c3, i2)));
|
||||
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c3, i3)));
|
||||
}
|
||||
|
||||
template <class Set>
|
||||
void
|
||||
testIssueSet()
|
||||
{
|
||||
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);
|
||||
|
||||
{
|
||||
Set c;
|
||||
|
||||
c.insert(a1);
|
||||
if (!BEAST_EXPECT(c.size() == 1))
|
||||
return;
|
||||
c.insert(a2);
|
||||
if (!BEAST_EXPECT(c.size() == 2))
|
||||
return;
|
||||
|
||||
if (!BEAST_EXPECT(c.erase(Issue(c1, i2)) == 0))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Issue(c1, i1)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Issue(c2, i2)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.empty()))
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
Set c;
|
||||
|
||||
c.insert(a1);
|
||||
if (!BEAST_EXPECT(c.size() == 1))
|
||||
return;
|
||||
c.insert(a2);
|
||||
if (!BEAST_EXPECT(c.size() == 2))
|
||||
return;
|
||||
|
||||
if (!BEAST_EXPECT(c.erase(Issue(c1, i2)) == 0))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Issue(c1, i1)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Issue(c2, i2)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.empty()))
|
||||
return;
|
||||
|
||||
#if STL_SET_HAS_EMPLACE
|
||||
c.emplace(c1, i1);
|
||||
if (!BEAST_EXPECT(c.size() == 1))
|
||||
return;
|
||||
c.emplace(c2, i2);
|
||||
if (!BEAST_EXPECT(c.size() == 2))
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
template <class Map>
|
||||
void
|
||||
testIssueMap()
|
||||
{
|
||||
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);
|
||||
|
||||
{
|
||||
Map c;
|
||||
|
||||
c.insert(std::make_pair(a1, 1));
|
||||
if (!BEAST_EXPECT(c.size() == 1))
|
||||
return;
|
||||
c.insert(std::make_pair(a2, 2));
|
||||
if (!BEAST_EXPECT(c.size() == 2))
|
||||
return;
|
||||
|
||||
if (!BEAST_EXPECT(c.erase(Issue(c1, i2)) == 0))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Issue(c1, i1)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Issue(c2, i2)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.empty()))
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
Map c;
|
||||
|
||||
c.insert(std::make_pair(a1, 1));
|
||||
if (!BEAST_EXPECT(c.size() == 1))
|
||||
return;
|
||||
c.insert(std::make_pair(a2, 2));
|
||||
if (!BEAST_EXPECT(c.size() == 2))
|
||||
return;
|
||||
|
||||
if (!BEAST_EXPECT(c.erase(Issue(c1, i2)) == 0))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Issue(c1, i1)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Issue(c2, i2)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.empty()))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Set>
|
||||
void
|
||||
testIssueDomainSet()
|
||||
{
|
||||
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};
|
||||
uint256 const domain2{2};
|
||||
|
||||
Set c;
|
||||
|
||||
c.insert(std::make_pair(a1, domain1));
|
||||
if (!BEAST_EXPECT(c.size() == 1))
|
||||
return;
|
||||
c.insert(std::make_pair(a2, domain1));
|
||||
if (!BEAST_EXPECT(c.size() == 2))
|
||||
return;
|
||||
c.insert(std::make_pair(a2, domain2));
|
||||
if (!BEAST_EXPECT(c.size() == 3))
|
||||
return;
|
||||
|
||||
if (!BEAST_EXPECT(c.erase(std::make_pair(Issue(c1, i2), domain1)) == 0))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(std::make_pair(a1, domain1)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(std::make_pair(a2, domain1)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(std::make_pair(a2, domain2)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.empty()))
|
||||
return;
|
||||
}
|
||||
|
||||
template <class Map>
|
||||
void
|
||||
testIssueDomainMap()
|
||||
{
|
||||
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};
|
||||
uint256 const domain2{2};
|
||||
|
||||
Map c;
|
||||
|
||||
c.insert(std::make_pair(std::make_pair(a1, domain1), 1));
|
||||
if (!BEAST_EXPECT(c.size() == 1))
|
||||
return;
|
||||
c.insert(std::make_pair(std::make_pair(a2, domain1), 2));
|
||||
if (!BEAST_EXPECT(c.size() == 2))
|
||||
return;
|
||||
c.insert(std::make_pair(std::make_pair(a2, domain2), 2));
|
||||
if (!BEAST_EXPECT(c.size() == 3))
|
||||
return;
|
||||
|
||||
if (!BEAST_EXPECT(c.erase(std::make_pair(Issue(c1, i2), domain1)) == 0))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(std::make_pair(a1, domain1)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(std::make_pair(a2, domain1)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(std::make_pair(a2, domain2)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.empty()))
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
testIssueDomainSets()
|
||||
{
|
||||
testcase("std::set <std::pair<Issue, Domain>>");
|
||||
testIssueDomainSet<std::set<std::pair<Issue, Domain>>>();
|
||||
|
||||
testcase("std::set <std::pair<Issue, Domain>>");
|
||||
testIssueDomainSet<std::set<std::pair<Issue, Domain>>>();
|
||||
|
||||
testcase("hash_set <std::pair<Issue, Domain>>");
|
||||
testIssueDomainSet<hash_set<std::pair<Issue, Domain>>>();
|
||||
|
||||
testcase("hash_set <std::pair<Issue, Domain>>");
|
||||
testIssueDomainSet<hash_set<std::pair<Issue, Domain>>>();
|
||||
}
|
||||
|
||||
void
|
||||
testIssueDomainMaps()
|
||||
{
|
||||
testcase("std::map <std::pair<Issue, Domain>, int>");
|
||||
testIssueDomainMap<std::map<std::pair<Issue, Domain>, int>>();
|
||||
|
||||
testcase("std::map <std::pair<Issue, Domain>, int>");
|
||||
testIssueDomainMap<std::map<std::pair<Issue, Domain>, int>>();
|
||||
|
||||
#if RIPPLE_ASSETS_ENABLE_STD_HASH
|
||||
testcase("hash_map <std::pair<Issue, Domain>, int>");
|
||||
testIssueDomainMap<hash_map<std::pair<Issue, Domain>, int>>();
|
||||
|
||||
testcase("hash_map <std::pair<Issue, Domain>, int>");
|
||||
testIssueDomainMap<hash_map<std::pair<Issue, Domain>, int>>();
|
||||
|
||||
testcase("hardened_hash_map <std::pair<Issue, Domain>, int>");
|
||||
testIssueDomainMap<hardened_hash_map<std::pair<Issue, Domain>, int>>();
|
||||
|
||||
testcase("hardened_hash_map <std::pair<Issue, Domain>, int>");
|
||||
testIssueDomainMap<hardened_hash_map<std::pair<Issue, Domain>, int>>();
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
testIssueSets()
|
||||
{
|
||||
testcase("std::set <Issue>");
|
||||
testIssueSet<std::set<Issue>>();
|
||||
|
||||
testcase("std::set <Issue>");
|
||||
testIssueSet<std::set<Issue>>();
|
||||
|
||||
#if RIPPLE_ASSETS_ENABLE_STD_HASH
|
||||
testcase("std::unordered_set <Issue>");
|
||||
testIssueSet<std::unordered_set<Issue>>();
|
||||
|
||||
testcase("std::unordered_set <Issue>");
|
||||
testIssueSet<std::unordered_set<Issue>>();
|
||||
#endif
|
||||
|
||||
testcase("hash_set <Issue>");
|
||||
testIssueSet<hash_set<Issue>>();
|
||||
|
||||
testcase("hash_set <Issue>");
|
||||
testIssueSet<hash_set<Issue>>();
|
||||
}
|
||||
|
||||
void
|
||||
testIssueMaps()
|
||||
{
|
||||
testcase("std::map <Issue, int>");
|
||||
testIssueMap<std::map<Issue, int>>();
|
||||
|
||||
testcase("std::map <Issue, int>");
|
||||
testIssueMap<std::map<Issue, int>>();
|
||||
|
||||
#if RIPPLE_ASSETS_ENABLE_STD_HASH
|
||||
testcase("std::unordered_map <Issue, int>");
|
||||
testIssueMap<std::unordered_map<Issue, int>>();
|
||||
|
||||
testcase("std::unordered_map <Issue, int>");
|
||||
testIssueMap<std::unordered_map<Issue, int>>();
|
||||
|
||||
testcase("hash_map <Issue, int>");
|
||||
testIssueMap<hash_map<Issue, int>>();
|
||||
|
||||
testcase("hash_map <Issue, int>");
|
||||
testIssueMap<hash_map<Issue, int>>();
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// Comparison, hash tests for Book
|
||||
template <class Book>
|
||||
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);
|
||||
|
||||
Issue a1(c1, i1);
|
||||
Issue a2(c1, i2);
|
||||
Issue a3(c2, i2);
|
||||
Issue a4(c3, i2);
|
||||
uint256 const domain1{1};
|
||||
uint256 const domain2{2};
|
||||
|
||||
// Books without domains
|
||||
BEAST_EXPECT(Book(a1, a2, std::nullopt) != Book(a2, a3, std::nullopt));
|
||||
BEAST_EXPECT(Book(a1, a2, std::nullopt) < Book(a2, a3, std::nullopt));
|
||||
BEAST_EXPECT(Book(a1, a2, std::nullopt) <= Book(a2, a3, std::nullopt));
|
||||
BEAST_EXPECT(Book(a2, a3, std::nullopt) <= Book(a2, a3, std::nullopt));
|
||||
BEAST_EXPECT(Book(a2, a3, std::nullopt) == Book(a2, a3, std::nullopt));
|
||||
BEAST_EXPECT(Book(a2, a3, std::nullopt) >= Book(a2, a3, std::nullopt));
|
||||
BEAST_EXPECT(Book(a3, a4, std::nullopt) >= Book(a2, a3, std::nullopt));
|
||||
BEAST_EXPECT(Book(a3, a4, std::nullopt) > Book(a2, a3, std::nullopt));
|
||||
|
||||
// test domain books
|
||||
{
|
||||
// Books with different domains
|
||||
BEAST_EXPECT(Book(a2, a3, domain1) != Book(a2, a3, domain2));
|
||||
BEAST_EXPECT(Book(a2, a3, domain1) < Book(a2, a3, domain2));
|
||||
BEAST_EXPECT(Book(a2, a3, domain2) > Book(a2, a3, domain1));
|
||||
|
||||
// One Book has a domain, the other does not
|
||||
BEAST_EXPECT(Book(a2, a3, domain1) != Book(a2, a3, std::nullopt));
|
||||
BEAST_EXPECT(Book(a2, a3, std::nullopt) < Book(a2, a3, domain1));
|
||||
BEAST_EXPECT(Book(a2, a3, domain1) > Book(a2, a3, std::nullopt));
|
||||
|
||||
// Both Books have the same domain
|
||||
BEAST_EXPECT(Book(a2, a3, domain1) == Book(a2, a3, domain1));
|
||||
BEAST_EXPECT(Book(a2, a3, domain2) == Book(a2, a3, domain2));
|
||||
BEAST_EXPECT(
|
||||
Book(a2, a3, std::nullopt) == Book(a2, a3, std::nullopt));
|
||||
|
||||
// Both Books have no domain
|
||||
BEAST_EXPECT(
|
||||
Book(a2, a3, std::nullopt) == Book(a2, a3, std::nullopt));
|
||||
|
||||
// Testing comparisons with >= and <=
|
||||
|
||||
// When comparing books with domain1 vs domain2
|
||||
BEAST_EXPECT(Book(a2, a3, domain1) <= Book(a2, a3, domain2));
|
||||
BEAST_EXPECT(Book(a2, a3, domain2) >= Book(a2, a3, domain1));
|
||||
BEAST_EXPECT(Book(a2, a3, domain1) >= Book(a2, a3, domain1));
|
||||
BEAST_EXPECT(Book(a2, a3, domain2) <= Book(a2, a3, domain2));
|
||||
|
||||
// One Book has domain1 and the other has no domain
|
||||
BEAST_EXPECT(Book(a2, a3, domain1) > Book(a2, a3, std::nullopt));
|
||||
BEAST_EXPECT(Book(a2, a3, std::nullopt) < Book(a2, a3, domain1));
|
||||
|
||||
// One Book has domain2 and the other has no domain
|
||||
BEAST_EXPECT(Book(a2, a3, domain2) > Book(a2, a3, std::nullopt));
|
||||
BEAST_EXPECT(Book(a2, a3, std::nullopt) < Book(a2, a3, domain2));
|
||||
|
||||
// Comparing two Books with no domains
|
||||
BEAST_EXPECT(
|
||||
Book(a2, a3, std::nullopt) <= Book(a2, a3, std::nullopt));
|
||||
BEAST_EXPECT(
|
||||
Book(a2, a3, std::nullopt) >= Book(a2, a3, std::nullopt));
|
||||
|
||||
// Test case where domain1 is less than domain2
|
||||
BEAST_EXPECT(Book(a2, a3, domain1) <= Book(a2, a3, domain2));
|
||||
BEAST_EXPECT(Book(a2, a3, domain2) >= Book(a2, a3, domain1));
|
||||
|
||||
// Test case where domain2 is equal to domain1
|
||||
BEAST_EXPECT(Book(a2, a3, domain1) >= Book(a2, a3, domain1));
|
||||
BEAST_EXPECT(Book(a2, a3, domain1) <= Book(a2, a3, domain1));
|
||||
|
||||
// More test cases involving a4 (with domain2)
|
||||
|
||||
// Comparing Book with domain2 (a4) to a Book with domain1
|
||||
BEAST_EXPECT(Book(a2, a3, domain1) < Book(a3, a4, domain2));
|
||||
BEAST_EXPECT(Book(a3, a4, domain2) > Book(a2, a3, domain1));
|
||||
|
||||
// Comparing Book with domain2 (a4) to a Book with no domain
|
||||
BEAST_EXPECT(Book(a3, a4, domain2) > Book(a2, a3, std::nullopt));
|
||||
BEAST_EXPECT(Book(a2, a3, std::nullopt) < Book(a3, a4, domain2));
|
||||
|
||||
// Comparing Book with domain2 (a4) to a Book with the same domain
|
||||
BEAST_EXPECT(Book(a3, a4, domain2) == Book(a3, a4, domain2));
|
||||
|
||||
// Comparing Book with domain2 (a4) to a Book with domain1
|
||||
BEAST_EXPECT(Book(a2, a3, domain1) < Book(a3, a4, domain2));
|
||||
BEAST_EXPECT(Book(a3, a4, domain2) > Book(a2, a3, domain1));
|
||||
}
|
||||
|
||||
std::hash<Book> hash;
|
||||
|
||||
// log << std::hex << hash (Book (a1, a2));
|
||||
// log << std::hex << hash (Book (a1, a2));
|
||||
//
|
||||
// log << std::hex << hash (Book (a1, a3));
|
||||
// log << std::hex << hash (Book (a1, a3));
|
||||
//
|
||||
// log << std::hex << hash (Book (a1, a4));
|
||||
// log << std::hex << hash (Book (a1, a4));
|
||||
//
|
||||
// log << std::hex << hash (Book (a2, a3));
|
||||
// log << std::hex << hash (Book (a2, a3));
|
||||
//
|
||||
// log << std::hex << hash (Book (a2, a4));
|
||||
// log << std::hex << hash (Book (a2, a4));
|
||||
//
|
||||
// log << std::hex << hash (Book (a3, a4));
|
||||
// log << std::hex << hash (Book (a3, a4));
|
||||
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a1, a2, std::nullopt)) ==
|
||||
hash(Book(a1, a2, std::nullopt)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a1, a3, std::nullopt)) ==
|
||||
hash(Book(a1, a3, std::nullopt)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a1, a4, std::nullopt)) ==
|
||||
hash(Book(a1, a4, std::nullopt)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a2, a3, std::nullopt)) ==
|
||||
hash(Book(a2, a3, std::nullopt)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a2, a4, std::nullopt)) ==
|
||||
hash(Book(a2, a4, std::nullopt)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a3, a4, std::nullopt)) ==
|
||||
hash(Book(a3, a4, std::nullopt)));
|
||||
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a1, a2, std::nullopt)) !=
|
||||
hash(Book(a1, a3, std::nullopt)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a1, a2, std::nullopt)) !=
|
||||
hash(Book(a1, a4, std::nullopt)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a1, a2, std::nullopt)) !=
|
||||
hash(Book(a2, a3, std::nullopt)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a1, a2, std::nullopt)) !=
|
||||
hash(Book(a2, a4, std::nullopt)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a1, a2, std::nullopt)) !=
|
||||
hash(Book(a3, a4, std::nullopt)));
|
||||
|
||||
// Books with domain
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a1, a2, domain1)) == hash(Book(a1, a2, domain1)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a1, a3, domain1)) == hash(Book(a1, a3, domain1)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a1, a4, domain1)) == hash(Book(a1, a4, domain1)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a2, a3, domain1)) == hash(Book(a2, a3, domain1)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a2, a4, domain1)) == hash(Book(a2, a4, domain1)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a3, a4, domain1)) == hash(Book(a3, a4, domain1)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a1, a2, std::nullopt)) ==
|
||||
hash(Book(a1, a2, std::nullopt)));
|
||||
|
||||
// Comparing Books with domain1 vs no domain
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a1, a2, std::nullopt)) != hash(Book(a1, a2, domain1)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a1, a3, std::nullopt)) != hash(Book(a1, a3, domain1)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a1, a4, std::nullopt)) != hash(Book(a1, a4, domain1)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a2, a3, std::nullopt)) != hash(Book(a2, a3, domain1)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a2, a4, std::nullopt)) != hash(Book(a2, a4, domain1)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a3, a4, std::nullopt)) != hash(Book(a3, a4, domain1)));
|
||||
|
||||
// Books with domain1 but different Issues
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a1, a2, domain1)) != hash(Book(a1, a3, domain1)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a1, a2, domain1)) != hash(Book(a1, a4, domain1)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a2, a3, domain1)) != hash(Book(a2, a4, domain1)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a1, a2, domain1)) != hash(Book(a2, a3, domain1)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a2, a4, domain1)) != hash(Book(a3, a4, domain1)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a3, a4, domain1)) != hash(Book(a1, a4, domain1)));
|
||||
|
||||
// Books with domain1 and domain2
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a1, a2, domain1)) != hash(Book(a1, a2, domain2)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a1, a3, domain1)) != hash(Book(a1, a3, domain2)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a1, a4, domain1)) != hash(Book(a1, a4, domain2)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a2, a3, domain1)) != hash(Book(a2, a3, domain2)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a2, a4, domain1)) != hash(Book(a2, a4, domain2)));
|
||||
BEAST_EXPECT(
|
||||
hash(Book(a3, a4, domain1)) != hash(Book(a3, a4, domain2)));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
template <class Set>
|
||||
void
|
||||
testBookSet()
|
||||
{
|
||||
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);
|
||||
Book const b2(a2, a1, std::nullopt);
|
||||
|
||||
uint256 const domain1{1};
|
||||
uint256 const domain2{2};
|
||||
|
||||
Book const b1_d1(a1, a2, domain1);
|
||||
Book const b2_d1(a2, a1, domain1);
|
||||
Book const b1_d2(a1, a2, domain2);
|
||||
Book const b2_d2(a2, a1, domain2);
|
||||
|
||||
{
|
||||
Set c;
|
||||
|
||||
c.insert(b1);
|
||||
if (!BEAST_EXPECT(c.size() == 1))
|
||||
return;
|
||||
c.insert(b2);
|
||||
if (!BEAST_EXPECT(c.size() == 2))
|
||||
return;
|
||||
|
||||
if (!BEAST_EXPECT(c.erase(Book(a1, a1, std::nullopt)) == 0))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.empty()))
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
Set c;
|
||||
|
||||
c.insert(b1);
|
||||
if (!BEAST_EXPECT(c.size() == 1))
|
||||
return;
|
||||
c.insert(b2);
|
||||
if (!BEAST_EXPECT(c.size() == 2))
|
||||
return;
|
||||
|
||||
if (!BEAST_EXPECT(c.erase(Book(a1, a1, std::nullopt)) == 0))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.empty()))
|
||||
return;
|
||||
|
||||
#if STL_SET_HAS_EMPLACE
|
||||
c.emplace(a1, a2);
|
||||
if (!BEAST_EXPECT(c.size() == 1))
|
||||
return;
|
||||
c.emplace(a2, a1);
|
||||
if (!BEAST_EXPECT(c.size() == 2))
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
{
|
||||
Set c;
|
||||
|
||||
c.insert(b1_d1);
|
||||
if (!BEAST_EXPECT(c.size() == 1))
|
||||
return;
|
||||
c.insert(b2_d1);
|
||||
if (!BEAST_EXPECT(c.size() == 2))
|
||||
return;
|
||||
c.insert(b1_d2);
|
||||
if (!BEAST_EXPECT(c.size() == 3))
|
||||
return;
|
||||
c.insert(b2_d2);
|
||||
if (!BEAST_EXPECT(c.size() == 4))
|
||||
return;
|
||||
|
||||
// Try removing non-existent elements
|
||||
if (!BEAST_EXPECT(c.erase(Book(a2, a2, domain1)) == 0))
|
||||
return;
|
||||
|
||||
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain1)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain1)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.size() == 2))
|
||||
return;
|
||||
|
||||
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain2)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain2)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.empty()))
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
Set c;
|
||||
|
||||
c.insert(b1);
|
||||
c.insert(b2);
|
||||
c.insert(b1_d1);
|
||||
c.insert(b2_d1);
|
||||
if (!BEAST_EXPECT(c.size() == 4))
|
||||
return;
|
||||
|
||||
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.size() == 2))
|
||||
return;
|
||||
|
||||
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain1)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain1)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.empty()))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Map>
|
||||
void
|
||||
testBookMap()
|
||||
{
|
||||
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);
|
||||
Book const b2(a2, a1, std::nullopt);
|
||||
|
||||
uint256 const domain1{1};
|
||||
uint256 const domain2{2};
|
||||
|
||||
Book const b1_d1(a1, a2, domain1);
|
||||
Book const b2_d1(a2, a1, domain1);
|
||||
Book const b1_d2(a1, a2, domain2);
|
||||
Book const b2_d2(a2, a1, domain2);
|
||||
|
||||
// typename Map::value_type value_type;
|
||||
// std::pair <Book const, int> value_type;
|
||||
|
||||
{
|
||||
Map c;
|
||||
|
||||
// c.insert (value_type (b1, 1));
|
||||
c.insert(std::make_pair(b1, 1));
|
||||
if (!BEAST_EXPECT(c.size() == 1))
|
||||
return;
|
||||
// c.insert (value_type (b2, 2));
|
||||
c.insert(std::make_pair(b2, 1));
|
||||
if (!BEAST_EXPECT(c.size() == 2))
|
||||
return;
|
||||
|
||||
if (!BEAST_EXPECT(c.erase(Book(a1, a1, std::nullopt)) == 0))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.empty()))
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
Map c;
|
||||
|
||||
// c.insert (value_type (b1, 1));
|
||||
c.insert(std::make_pair(b1, 1));
|
||||
if (!BEAST_EXPECT(c.size() == 1))
|
||||
return;
|
||||
// c.insert (value_type (b2, 2));
|
||||
c.insert(std::make_pair(b2, 1));
|
||||
if (!BEAST_EXPECT(c.size() == 2))
|
||||
return;
|
||||
|
||||
if (!BEAST_EXPECT(c.erase(Book(a1, a1, std::nullopt)) == 0))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.empty()))
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
Map c;
|
||||
|
||||
c.insert(std::make_pair(b1_d1, 10));
|
||||
if (!BEAST_EXPECT(c.size() == 1))
|
||||
return;
|
||||
c.insert(std::make_pair(b2_d1, 20));
|
||||
if (!BEAST_EXPECT(c.size() == 2))
|
||||
return;
|
||||
c.insert(std::make_pair(b1_d2, 30));
|
||||
if (!BEAST_EXPECT(c.size() == 3))
|
||||
return;
|
||||
c.insert(std::make_pair(b2_d2, 40));
|
||||
if (!BEAST_EXPECT(c.size() == 4))
|
||||
return;
|
||||
|
||||
// Try removing non-existent elements
|
||||
if (!BEAST_EXPECT(c.erase(Book(a2, a2, domain1)) == 0))
|
||||
return;
|
||||
|
||||
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain1)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain1)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.size() == 2))
|
||||
return;
|
||||
|
||||
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain2)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain2)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.empty()))
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
Map c;
|
||||
|
||||
c.insert(std::make_pair(b1, 1));
|
||||
c.insert(std::make_pair(b2, 2));
|
||||
c.insert(std::make_pair(b1_d1, 3));
|
||||
c.insert(std::make_pair(b2_d1, 4));
|
||||
if (!BEAST_EXPECT(c.size() == 4))
|
||||
return;
|
||||
|
||||
// Try removing non-existent elements
|
||||
if (!BEAST_EXPECT(c.erase(Book(a1, a1, domain1)) == 0))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Book(a2, a2, domain2)) == 0))
|
||||
return;
|
||||
|
||||
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.size() == 2))
|
||||
return;
|
||||
|
||||
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain1)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain1)) == 1))
|
||||
return;
|
||||
if (!BEAST_EXPECT(c.empty()))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testBookSets()
|
||||
{
|
||||
testcase("std::set <Book>");
|
||||
testBookSet<std::set<Book>>();
|
||||
|
||||
testcase("std::set <Book>");
|
||||
testBookSet<std::set<Book>>();
|
||||
|
||||
#if RIPPLE_ASSETS_ENABLE_STD_HASH
|
||||
testcase("std::unordered_set <Book>");
|
||||
testBookSet<std::unordered_set<Book>>();
|
||||
|
||||
testcase("std::unordered_set <Book>");
|
||||
testBookSet<std::unordered_set<Book>>();
|
||||
#endif
|
||||
|
||||
testcase("hash_set <Book>");
|
||||
testBookSet<hash_set<Book>>();
|
||||
|
||||
testcase("hash_set <Book>");
|
||||
testBookSet<hash_set<Book>>();
|
||||
}
|
||||
|
||||
void
|
||||
testBookMaps()
|
||||
{
|
||||
testcase("std::map <Book, int>");
|
||||
testBookMap<std::map<Book, int>>();
|
||||
|
||||
testcase("std::map <Book, int>");
|
||||
testBookMap<std::map<Book, int>>();
|
||||
|
||||
#if RIPPLE_ASSETS_ENABLE_STD_HASH
|
||||
testcase("std::unordered_map <Book, int>");
|
||||
testBookMap<std::unordered_map<Book, int>>();
|
||||
|
||||
testcase("std::unordered_map <Book, int>");
|
||||
testBookMap<std::unordered_map<Book, int>>();
|
||||
|
||||
testcase("hash_map <Book, int>");
|
||||
testBookMap<hash_map<Book, int>>();
|
||||
|
||||
testcase("hash_map <Book, int>");
|
||||
testBookMap<hash_map<Book, int>>();
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testcase("Currency");
|
||||
testUnsigned<Currency>();
|
||||
|
||||
testcase("AccountID");
|
||||
testUnsigned<AccountID>();
|
||||
|
||||
// ---
|
||||
|
||||
testcase("Issue");
|
||||
testIssue<Issue>();
|
||||
|
||||
testcase("Issue");
|
||||
testIssue<Issue>();
|
||||
|
||||
testIssueSets();
|
||||
testIssueMaps();
|
||||
|
||||
// ---
|
||||
|
||||
testcase("Book");
|
||||
testBook<Book>();
|
||||
|
||||
testcase("Book");
|
||||
testBook<Book>();
|
||||
|
||||
testBookSets();
|
||||
testBookMaps();
|
||||
|
||||
// ---
|
||||
testIssueDomainSets();
|
||||
testIssueDomainMaps();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Issue, protocol, ripple);
|
||||
|
||||
} // namespace ripple
|
||||
140
src/test/protocol/Memo_test.cpp
Normal file
140
src/test/protocol/Memo_test.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2022 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <test/jtx.h>
|
||||
|
||||
#include <xrpl/basics/strHex.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class Memo_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
testMemos()
|
||||
{
|
||||
testcase("Test memos");
|
||||
|
||||
using namespace test::jtx;
|
||||
Account alice{"alice"};
|
||||
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), alice);
|
||||
env.close();
|
||||
|
||||
// Lambda that returns a valid JTx with a memo that we can hack up.
|
||||
// This is the basis for building tests of invalid states.
|
||||
auto makeJtxWithMemo = [&env, &alice]() {
|
||||
JTx example = noop(alice);
|
||||
memo const exampleMemo{"tic", "tac", "toe"};
|
||||
exampleMemo(env, example);
|
||||
return example;
|
||||
};
|
||||
|
||||
// A valid memo.
|
||||
env(makeJtxWithMemo());
|
||||
env.close();
|
||||
|
||||
{
|
||||
// Make sure that too big a memo is flagged as invalid.
|
||||
JTx memoSize = makeJtxWithMemo();
|
||||
memoSize.jv[sfMemos.jsonName][0u][sfMemo.jsonName]
|
||||
[sfMemoData.jsonName] = std::string(2020, '0');
|
||||
env(memoSize,
|
||||
rpc("invalidTransaction",
|
||||
"fails local checks: The memo exceeds the maximum allowed "
|
||||
"size."));
|
||||
|
||||
// This memo is just barely small enough.
|
||||
memoSize.jv[sfMemos.jsonName][0u][sfMemo.jsonName]
|
||||
[sfMemoData.jsonName] = std::string(2018, '1');
|
||||
env(memoSize);
|
||||
}
|
||||
{
|
||||
// Put a non-Memo in the Memos array.
|
||||
JTx memoNonMemo = noop(alice);
|
||||
auto& jv = memoNonMemo.jv;
|
||||
auto& ma = jv[sfMemos.jsonName];
|
||||
auto& mi = ma[ma.size()];
|
||||
auto& m = mi[sfCreatedNode.jsonName]; // CreatedNode in Memos
|
||||
m[sfMemoData.jsonName] = "3030303030";
|
||||
|
||||
env(memoNonMemo,
|
||||
rpc("invalidTransaction",
|
||||
"fails local checks: A memo array may contain only Memo "
|
||||
"objects."));
|
||||
}
|
||||
{
|
||||
// Put an invalid field in a Memo object.
|
||||
JTx memoExtra = makeJtxWithMemo();
|
||||
memoExtra
|
||||
.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfFlags.jsonName] =
|
||||
13;
|
||||
env(memoExtra,
|
||||
rpc("invalidTransaction",
|
||||
"fails local checks: A memo may contain only MemoType, "
|
||||
"MemoData or MemoFormat fields."));
|
||||
}
|
||||
{
|
||||
// Put a character that is not allowed in a URL in a MemoType field.
|
||||
JTx memoBadChar = makeJtxWithMemo();
|
||||
memoBadChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName]
|
||||
[sfMemoType.jsonName] =
|
||||
strHex(std::string_view("ONE<INFINITY"));
|
||||
env(memoBadChar,
|
||||
rpc("invalidTransaction",
|
||||
"fails local checks: The MemoType and MemoFormat fields "
|
||||
"may only contain characters that are allowed in URLs "
|
||||
"under RFC 3986."));
|
||||
}
|
||||
{
|
||||
// Put a character that is not allowed in a URL in a MemoData field.
|
||||
// That's okay.
|
||||
JTx memoLegitChar = makeJtxWithMemo();
|
||||
memoLegitChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName]
|
||||
[sfMemoData.jsonName] =
|
||||
strHex(std::string_view("ONE<INFINITY"));
|
||||
env(memoLegitChar);
|
||||
}
|
||||
{
|
||||
// Put a character that is not allowed in a URL in a MemoFormat.
|
||||
JTx memoBadChar = makeJtxWithMemo();
|
||||
memoBadChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName]
|
||||
[sfMemoFormat.jsonName] =
|
||||
strHex(std::string_view("NoBraces{}InURL"));
|
||||
env(memoBadChar,
|
||||
rpc("invalidTransaction",
|
||||
"fails local checks: The MemoType and MemoFormat fields "
|
||||
"may only contain characters that are allowed in URLs "
|
||||
"under RFC 3986."));
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testMemos();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Memo, ripple_data, ripple);
|
||||
|
||||
} // namespace ripple
|
||||
1074
src/test/protocol/MultiApiJson_test.cpp
Normal file
1074
src/test/protocol/MultiApiJson_test.cpp
Normal file
File diff suppressed because it is too large
Load Diff
474
src/test/protocol/PublicKey_test.cpp
Normal file
474
src/test/protocol/PublicKey_test.cpp
Normal file
@@ -0,0 +1,474 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/SecretKey.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class PublicKey_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
using blob = std::vector<std::uint8_t>;
|
||||
|
||||
template <class FwdIter, class Container>
|
||||
static void
|
||||
hex_to_binary(FwdIter first, FwdIter last, Container& out)
|
||||
{
|
||||
struct Table
|
||||
{
|
||||
int val[256];
|
||||
Table()
|
||||
{
|
||||
std::fill(val, val + 256, 0);
|
||||
for (int i = 0; i < 10; ++i)
|
||||
val['0' + i] = i;
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{
|
||||
val['A' + i] = 10 + i;
|
||||
val['a' + i] = 10 + i;
|
||||
}
|
||||
}
|
||||
int
|
||||
operator[](int i)
|
||||
{
|
||||
return val[i];
|
||||
}
|
||||
};
|
||||
|
||||
static Table lut;
|
||||
out.reserve(std::distance(first, last) / 2);
|
||||
while (first != last)
|
||||
{
|
||||
auto const hi(lut[(*first++)]);
|
||||
auto const lo(lut[(*first++)]);
|
||||
out.push_back((hi * 16) + lo);
|
||||
}
|
||||
}
|
||||
|
||||
blob
|
||||
sig(std::string const& hex)
|
||||
{
|
||||
blob b;
|
||||
hex_to_binary(hex.begin(), hex.end(), b);
|
||||
return b;
|
||||
}
|
||||
|
||||
bool
|
||||
check(std::optional<ECDSACanonicality> answer, std::string const& s)
|
||||
{
|
||||
return ecdsaCanonicality(makeSlice(sig(s))) == answer;
|
||||
}
|
||||
|
||||
void
|
||||
testCanonical()
|
||||
{
|
||||
testcase("Canonical");
|
||||
|
||||
// Fully canonical
|
||||
BEAST_EXPECT(check(
|
||||
ECDSACanonicality::fullyCanonical,
|
||||
"3045"
|
||||
"022100FF478110D1D4294471EC76E0157540C2181F47DEBD25D7F9E7DDCCCD47EE"
|
||||
"E905"
|
||||
"0220078F07CDAE6C240855D084AD91D1479609533C147C93B0AEF19BC9724D003F"
|
||||
"28"));
|
||||
BEAST_EXPECT(check(
|
||||
ECDSACanonicality::fullyCanonical,
|
||||
"3045"
|
||||
"0221009218248292F1762D8A51BE80F8A7F2CD288D810CE781D5955700DA1684DF"
|
||||
"1D2D"
|
||||
"022041A1EE1746BFD72C9760CC93A7AAA8047D52C8833A03A20EAAE92EA19717B4"
|
||||
"54"));
|
||||
BEAST_EXPECT(check(
|
||||
ECDSACanonicality::fullyCanonical,
|
||||
"3044"
|
||||
"02206A9E43775F73B6D1EC420E4DDD222A80D4C6DF5D1BEECC431A91B63C928B75"
|
||||
"81"
|
||||
"022023E9CC2D61DDA6F73EAA6BCB12688BEB0F434769276B3127E4044ED895C9D9"
|
||||
"6B"));
|
||||
BEAST_EXPECT(check(
|
||||
ECDSACanonicality::fullyCanonical,
|
||||
"3044"
|
||||
"022056E720007221F3CD4EFBB6352741D8E5A0968D48D8D032C2FBC4F6304AD1D0"
|
||||
"4E"
|
||||
"02201F39EB392C20D7801C3E8D81D487E742FA84A1665E923225BD6323847C7187"
|
||||
"9F"));
|
||||
BEAST_EXPECT(check(
|
||||
ECDSACanonicality::fullyCanonical,
|
||||
"3045"
|
||||
"022100FDFD5AD05518CEA0017A2DCB5C4DF61E7C73B6D3A38E7AE93210A1564E8C"
|
||||
"2F12"
|
||||
"0220214FF061CCC123C81D0BB9D0EDEA04CD40D96BF1425D311DA62A7096BB18EA"
|
||||
"18"));
|
||||
|
||||
// Canonical but not fully canonical
|
||||
BEAST_EXPECT(check(
|
||||
ECDSACanonicality::canonical,
|
||||
"3046"
|
||||
"022100F477B3FA6F31C7CB3A0D1AD94A231FDD24B8D78862EE334CEA7CD08F6CBC"
|
||||
"0A1B"
|
||||
"022100928E6BCF1ED2684679730C5414AEC48FD62282B090041C41453C1D064AF5"
|
||||
"97A1"));
|
||||
BEAST_EXPECT(check(
|
||||
ECDSACanonicality::canonical,
|
||||
"3045"
|
||||
"022063E7C7CA93CB2400E413A342C027D00665F8BAB9C22EF0A7B8AE3AAF092230"
|
||||
"B6"
|
||||
"0221008F2E8BB7D09521ABBC277717B14B93170AE6465C5A1B36561099319C4BEB"
|
||||
"254C"));
|
||||
BEAST_EXPECT(check(
|
||||
ECDSACanonicality::canonical,
|
||||
"3046"
|
||||
"02210099DCA1188663DDEA506A06A7B20C2B7D8C26AFF41DECE69D6C5F7C967D32"
|
||||
"625F"
|
||||
"022100897658A6B1F9EEE5D140D7A332DA0BD73BB98974EA53F6201B01C1B594F2"
|
||||
"86EA"));
|
||||
BEAST_EXPECT(check(
|
||||
ECDSACanonicality::canonical,
|
||||
"3045"
|
||||
"02200855DE366E4E323AA2CE2A25674401A7D11F72EC432770D07F7B57DF7387AE"
|
||||
"C0"
|
||||
"022100DA4C6ADDEA14888858DE2AC5B91ED9050D6972BB388DEF582628CEE32869"
|
||||
"AE35"));
|
||||
|
||||
// valid
|
||||
BEAST_EXPECT(check(
|
||||
ECDSACanonicality::fullyCanonical,
|
||||
"3006"
|
||||
"020101"
|
||||
"020102"));
|
||||
BEAST_EXPECT(check(
|
||||
ECDSACanonicality::fullyCanonical,
|
||||
"3044"
|
||||
"02203932c892e2e550f3af8ee4ce9c215a87f9bb831dcac87b2838e2c2eaa891df"
|
||||
"0c"
|
||||
"022030b61dd36543125d56b9f9f3a1f53189e5af33cdda8d77a5209aec03978fa0"
|
||||
"01"));
|
||||
BEAST_EXPECT(check(
|
||||
ECDSACanonicality::canonical,
|
||||
"3045"
|
||||
"0220076045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40f9"
|
||||
"0a"
|
||||
"0221008fffd599910eefe00bc803c688eca1d2ba7f6b180620eaa03488e6585db6"
|
||||
"ba01"));
|
||||
BEAST_EXPECT(check(
|
||||
ECDSACanonicality::canonical,
|
||||
"3046"
|
||||
"022100876045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40"
|
||||
"f90a"
|
||||
"0221008fffd599910eefe00bc803c688c2eca1d2ba7f6b180620eaa03488e6585d"
|
||||
"b6ba"));
|
||||
|
||||
BEAST_EXPECT(check(
|
||||
std::nullopt,
|
||||
"3005"
|
||||
"0201FF"
|
||||
"0200"));
|
||||
BEAST_EXPECT(check(
|
||||
std::nullopt,
|
||||
"3006"
|
||||
"020101"
|
||||
"020202"));
|
||||
BEAST_EXPECT(check(
|
||||
std::nullopt,
|
||||
"3006"
|
||||
"020701"
|
||||
"020102"));
|
||||
BEAST_EXPECT(check(
|
||||
std::nullopt,
|
||||
"3006"
|
||||
"020401"
|
||||
"020102"));
|
||||
BEAST_EXPECT(check(
|
||||
std::nullopt,
|
||||
"3006"
|
||||
"020501"
|
||||
"020102"));
|
||||
BEAST_EXPECT(check(
|
||||
std::nullopt,
|
||||
"3006"
|
||||
"020201"
|
||||
"020102"));
|
||||
BEAST_EXPECT(check(
|
||||
std::nullopt,
|
||||
"3006"
|
||||
"020301"
|
||||
"020202"));
|
||||
BEAST_EXPECT(check(
|
||||
std::nullopt,
|
||||
"3006"
|
||||
"020401"
|
||||
"020202"));
|
||||
BEAST_EXPECT(check(
|
||||
std::nullopt,
|
||||
"3047"
|
||||
"0221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba"
|
||||
"6105"
|
||||
"022200002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e56"
|
||||
"6695ed"));
|
||||
BEAST_EXPECT(check(
|
||||
std::nullopt,
|
||||
"3144"
|
||||
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
|
||||
"05"
|
||||
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
|
||||
"ed"));
|
||||
BEAST_EXPECT(check(
|
||||
std::nullopt,
|
||||
"3045"
|
||||
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
|
||||
"05"
|
||||
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
|
||||
"ed"));
|
||||
BEAST_EXPECT(check(
|
||||
std::nullopt,
|
||||
"301F"
|
||||
"01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1"));
|
||||
BEAST_EXPECT(check(
|
||||
std::nullopt,
|
||||
"3045"
|
||||
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
|
||||
"05"
|
||||
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
|
||||
"ed00"));
|
||||
BEAST_EXPECT(check(
|
||||
std::nullopt,
|
||||
"3044"
|
||||
"01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
|
||||
"05"
|
||||
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
|
||||
"ed"));
|
||||
BEAST_EXPECT(check(
|
||||
std::nullopt,
|
||||
"3024"
|
||||
"0200"
|
||||
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
|
||||
"ed"));
|
||||
BEAST_EXPECT(check(
|
||||
std::nullopt,
|
||||
"3044"
|
||||
"02208990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
|
||||
"05"
|
||||
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
|
||||
"ed"));
|
||||
BEAST_EXPECT(check(
|
||||
std::nullopt,
|
||||
"3045"
|
||||
"0221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba"
|
||||
"6105"
|
||||
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
|
||||
"ed"));
|
||||
BEAST_EXPECT(check(
|
||||
std::nullopt,
|
||||
"3044"
|
||||
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
|
||||
"05012"
|
||||
"02d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695e"
|
||||
"d"));
|
||||
BEAST_EXPECT(check(
|
||||
std::nullopt,
|
||||
"3024"
|
||||
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
|
||||
"05"
|
||||
"0200"));
|
||||
BEAST_EXPECT(check(
|
||||
std::nullopt,
|
||||
"3044"
|
||||
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
|
||||
"05"
|
||||
"0220fd5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
|
||||
"ed"));
|
||||
BEAST_EXPECT(check(
|
||||
std::nullopt,
|
||||
"3045"
|
||||
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
|
||||
"05"
|
||||
"0221002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e5666"
|
||||
"95ed"));
|
||||
}
|
||||
|
||||
void
|
||||
testBase58(KeyType keyType)
|
||||
{
|
||||
// Try converting short, long and malformed data
|
||||
BEAST_EXPECT(!parseBase58<PublicKey>(TokenType::NodePublic, ""));
|
||||
BEAST_EXPECT(!parseBase58<PublicKey>(TokenType::NodePublic, " "));
|
||||
BEAST_EXPECT(
|
||||
!parseBase58<PublicKey>(TokenType::NodePublic, "!ty89234gh45"));
|
||||
|
||||
auto const good = toBase58(
|
||||
TokenType::NodePublic, derivePublicKey(keyType, randomSecretKey()));
|
||||
|
||||
// Short (non-empty) strings
|
||||
{
|
||||
auto s = good;
|
||||
|
||||
// Remove all characters from the string in random order:
|
||||
std::hash<std::string> r;
|
||||
|
||||
while (!s.empty())
|
||||
{
|
||||
s.erase(r(s) % s.size(), 1);
|
||||
BEAST_EXPECT(!parseBase58<PublicKey>(TokenType::NodePublic, s));
|
||||
}
|
||||
}
|
||||
|
||||
// Long strings
|
||||
for (std::size_t i = 1; i != 16; i++)
|
||||
{
|
||||
auto s = good;
|
||||
s.resize(s.size() + i, s[i % s.size()]);
|
||||
BEAST_EXPECT(!parseBase58<PublicKey>(TokenType::NodePublic, s));
|
||||
}
|
||||
|
||||
// Strings with invalid Base58 characters
|
||||
for (auto c : std::string("0IOl"))
|
||||
{
|
||||
for (std::size_t i = 0; i != good.size(); ++i)
|
||||
{
|
||||
auto s = good;
|
||||
s[i % s.size()] = c;
|
||||
BEAST_EXPECT(!parseBase58<PublicKey>(TokenType::NodePublic, s));
|
||||
}
|
||||
}
|
||||
|
||||
// Strings with incorrect prefix
|
||||
{
|
||||
auto s = good;
|
||||
|
||||
for (auto c : std::string("apsrJqtv7"))
|
||||
{
|
||||
s[0] = c;
|
||||
BEAST_EXPECT(!parseBase58<PublicKey>(TokenType::NodePublic, s));
|
||||
}
|
||||
}
|
||||
|
||||
// Try some random secret keys
|
||||
std::vector<PublicKey> keys;
|
||||
keys.reserve(32);
|
||||
|
||||
for (std::size_t i = 0; i != keys.capacity(); ++i)
|
||||
keys.emplace_back(derivePublicKey(keyType, randomSecretKey()));
|
||||
BEAST_EXPECT(keys.size() == 32);
|
||||
|
||||
for (std::size_t i = 0; i != keys.size(); ++i)
|
||||
{
|
||||
auto const si = toBase58(TokenType::NodePublic, keys[i]);
|
||||
BEAST_EXPECT(!si.empty());
|
||||
|
||||
auto const ski = parseBase58<PublicKey>(TokenType::NodePublic, si);
|
||||
BEAST_EXPECT(ski && (keys[i] == *ski));
|
||||
|
||||
for (std::size_t j = i; j != keys.size(); ++j)
|
||||
{
|
||||
BEAST_EXPECT((keys[i] == keys[j]) == (i == j));
|
||||
|
||||
auto const sj = toBase58(TokenType::NodePublic, keys[j]);
|
||||
|
||||
BEAST_EXPECT((si == sj) == (i == j));
|
||||
|
||||
auto const skj =
|
||||
parseBase58<PublicKey>(TokenType::NodePublic, sj);
|
||||
BEAST_EXPECT(skj && (keys[j] == *skj));
|
||||
|
||||
BEAST_EXPECT((*ski == *skj) == (i == j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testBase58()
|
||||
{
|
||||
testcase("Base58: secp256k1");
|
||||
|
||||
{
|
||||
auto const pk1 = derivePublicKey(
|
||||
KeyType::secp256k1,
|
||||
generateSecretKey(
|
||||
KeyType::secp256k1, generateSeed("masterpassphrase")));
|
||||
|
||||
auto const pk2 = parseBase58<PublicKey>(
|
||||
TokenType::NodePublic,
|
||||
"n94a1u4jAz288pZLtw6yFWVbi89YamiC6JBXPVUj5zmExe5fTVg9");
|
||||
BEAST_EXPECT(pk2);
|
||||
|
||||
BEAST_EXPECT(pk1 == *pk2);
|
||||
}
|
||||
|
||||
testBase58(KeyType::secp256k1);
|
||||
|
||||
testcase("Base58: ed25519");
|
||||
|
||||
{
|
||||
auto const pk1 = derivePublicKey(
|
||||
KeyType::ed25519,
|
||||
generateSecretKey(
|
||||
KeyType::ed25519, generateSeed("masterpassphrase")));
|
||||
|
||||
auto const pk2 = parseBase58<PublicKey>(
|
||||
TokenType::NodePublic,
|
||||
"nHUeeJCSY2dM71oxM8Cgjouf5ekTuev2mwDpc374aLMxzDLXNmjf");
|
||||
BEAST_EXPECT(pk2);
|
||||
|
||||
BEAST_EXPECT(pk1 == *pk2);
|
||||
}
|
||||
|
||||
testBase58(KeyType::ed25519);
|
||||
}
|
||||
|
||||
void
|
||||
testMiscOperations()
|
||||
{
|
||||
testcase("Miscellaneous operations");
|
||||
|
||||
auto const pk1 = derivePublicKey(
|
||||
KeyType::secp256k1,
|
||||
generateSecretKey(
|
||||
KeyType::secp256k1, generateSeed("masterpassphrase")));
|
||||
|
||||
PublicKey pk2(pk1);
|
||||
BEAST_EXPECT(pk1 == pk2);
|
||||
BEAST_EXPECT(pk2 == pk1);
|
||||
|
||||
PublicKey pk3 = derivePublicKey(
|
||||
KeyType::secp256k1,
|
||||
generateSecretKey(
|
||||
KeyType::secp256k1, generateSeed("arbitraryPassPhrase")));
|
||||
// Testing the copy assignment operation of PublicKey class
|
||||
pk3 = pk2;
|
||||
BEAST_EXPECT(pk3 == pk2);
|
||||
BEAST_EXPECT(pk1 == pk3);
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testBase58();
|
||||
testCanonical();
|
||||
testMiscOperations();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(PublicKey, protocol, ripple);
|
||||
|
||||
} // namespace ripple
|
||||
@@ -12,5 +12,3 @@ xrpl_add_test(basics)
|
||||
target_link_libraries(xrpl.test.basics PRIVATE xrpl.imports.test)
|
||||
xrpl_add_test(crypto)
|
||||
target_link_libraries(xrpl.test.crypto PRIVATE xrpl.imports.test)
|
||||
xrpl_add_test(protocol)
|
||||
target_link_libraries(xrpl.test.protocol PRIVATE xrpl.imports.test)
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/XRPLF/rippled/
|
||||
Copyright (c) 2023 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpl/protocol/ApiVersion.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
using namespace ripple;
|
||||
|
||||
TEST_SUITE_BEGIN("ApiVersion");
|
||||
|
||||
TEST_CASE("API versions invariants")
|
||||
{
|
||||
static_assert(
|
||||
RPC::apiMinimumSupportedVersion <= RPC::apiMaximumSupportedVersion);
|
||||
static_assert(
|
||||
RPC::apiMinimumSupportedVersion <= RPC::apiMaximumValidVersion);
|
||||
static_assert(
|
||||
RPC::apiMaximumSupportedVersion <= RPC::apiMaximumValidVersion);
|
||||
static_assert(RPC::apiBetaVersion <= RPC::apiMaximumValidVersion);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_CASE("API versions")
|
||||
{
|
||||
// Update when we change versions
|
||||
static_assert(RPC::apiMinimumSupportedVersion >= 1);
|
||||
static_assert(RPC::apiMinimumSupportedVersion < 2);
|
||||
static_assert(RPC::apiMaximumSupportedVersion >= 2);
|
||||
static_assert(RPC::apiMaximumSupportedVersion < 3);
|
||||
static_assert(RPC::apiMaximumValidVersion >= 3);
|
||||
static_assert(RPC::apiMaximumValidVersion < 4);
|
||||
static_assert(RPC::apiBetaVersion >= 3);
|
||||
static_assert(RPC::apiBetaVersion < 4);
|
||||
|
||||
CHECK(true);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
@@ -1,98 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2020 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpl/protocol/BuildInfo.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
using namespace ripple;
|
||||
|
||||
TEST_SUITE_BEGIN("BuildInfo");
|
||||
|
||||
TEST_CASE("EncodeSoftwareVersion")
|
||||
{
|
||||
auto encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.3-b7");
|
||||
|
||||
// the first two bytes identify the particular implementation, 0x183B
|
||||
CHECK(
|
||||
(encodedVersion & 0xFFFF'0000'0000'0000LLU) ==
|
||||
0x183B'0000'0000'0000LLU);
|
||||
|
||||
// the next three bytes: major version, minor version, patch version,
|
||||
// 0x010203
|
||||
CHECK(
|
||||
(encodedVersion & 0x0000'FFFF'FF00'0000LLU) ==
|
||||
0x0000'0102'0300'0000LLU);
|
||||
|
||||
// the next two bits:
|
||||
{
|
||||
// 01 if a beta
|
||||
CHECK((encodedVersion & 0x0000'0000'00C0'0000LLU) >> 22 == 0b01);
|
||||
// 10 if an RC
|
||||
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.4-rc7");
|
||||
CHECK((encodedVersion & 0x0000'0000'00C0'0000LLU) >> 22 == 0b10);
|
||||
// 11 if neither an RC nor a beta
|
||||
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.5");
|
||||
CHECK((encodedVersion & 0x0000'0000'00C0'0000LLU) >> 22 == 0b11);
|
||||
}
|
||||
|
||||
// the next six bits: rc/beta number (1-63)
|
||||
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.6-b63");
|
||||
CHECK((encodedVersion & 0x0000'0000'003F'0000LLU) >> 16 == 63);
|
||||
|
||||
// the last two bytes are zeros
|
||||
CHECK((encodedVersion & 0x0000'0000'0000'FFFFLLU) == 0);
|
||||
|
||||
// Test some version strings with wrong formats:
|
||||
// no rc/beta number
|
||||
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.3-b");
|
||||
CHECK((encodedVersion & 0x0000'0000'00FF'0000LLU) == 0);
|
||||
// rc/beta number out of range
|
||||
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.3-b64");
|
||||
CHECK((encodedVersion & 0x0000'0000'00FF'0000LLU) == 0);
|
||||
|
||||
// Check that the rc/beta number of a release is 0:
|
||||
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.6");
|
||||
CHECK((encodedVersion & 0x0000'0000'003F'0000LLU) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("IsRippledVersion")
|
||||
{
|
||||
auto vFF = 0xFFFF'FFFF'FFFF'FFFFLLU;
|
||||
CHECK(!BuildInfo::isRippledVersion(vFF));
|
||||
auto vRippled = 0x183B'0000'0000'0000LLU;
|
||||
CHECK(BuildInfo::isRippledVersion(vRippled));
|
||||
}
|
||||
|
||||
TEST_CASE("IsNewerVersion")
|
||||
{
|
||||
auto vFF = 0xFFFF'FFFF'FFFF'FFFFLLU;
|
||||
CHECK(!BuildInfo::isNewerVersion(vFF));
|
||||
|
||||
auto v159 = BuildInfo::encodeSoftwareVersion("1.5.9");
|
||||
CHECK(!BuildInfo::isNewerVersion(v159));
|
||||
|
||||
auto vCurrent = BuildInfo::getEncodedVersion();
|
||||
CHECK(!BuildInfo::isNewerVersion(vCurrent));
|
||||
|
||||
auto vMax = BuildInfo::encodeSoftwareVersion("255.255.255");
|
||||
CHECK(BuildInfo::isNewerVersion(vMax));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
@@ -1,179 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2017 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <test/jtx.h>
|
||||
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
using namespace ripple;
|
||||
|
||||
TEST_SUITE_BEGIN("Hooks");
|
||||
|
||||
TEST_CASE("Test Hooks fields")
|
||||
{
|
||||
using namespace test::jtx;
|
||||
|
||||
std::vector<std::reference_wrapper<SField const>> fields_to_test = {
|
||||
sfHookResult,
|
||||
sfHookStateChangeCount,
|
||||
sfHookEmitCount,
|
||||
sfHookExecutionIndex,
|
||||
sfHookApiVersion,
|
||||
sfHookStateCount,
|
||||
sfEmitGeneration,
|
||||
sfHookOn,
|
||||
sfHookInstructionCount,
|
||||
sfEmitBurden,
|
||||
sfHookReturnCode,
|
||||
sfReferenceCount,
|
||||
sfEmitParentTxnID,
|
||||
sfEmitNonce,
|
||||
sfEmitHookHash,
|
||||
sfHookStateKey,
|
||||
sfHookHash,
|
||||
sfHookNamespace,
|
||||
sfHookSetTxnID,
|
||||
sfHookStateData,
|
||||
sfHookReturnString,
|
||||
sfHookParameterName,
|
||||
sfHookParameterValue,
|
||||
sfEmitCallback,
|
||||
sfHookAccount,
|
||||
sfEmittedTxn,
|
||||
sfHook,
|
||||
sfHookDefinition,
|
||||
sfHookParameter,
|
||||
sfHookGrant,
|
||||
sfEmitDetails,
|
||||
sfHookExecutions,
|
||||
sfHookExecution,
|
||||
sfHookParameters,
|
||||
sfHooks,
|
||||
sfHookGrants};
|
||||
|
||||
for (auto const& rf : fields_to_test)
|
||||
{
|
||||
SField const& f = rf.get();
|
||||
|
||||
STObject dummy{sfGeneric};
|
||||
|
||||
CHECK(!dummy.isFieldPresent(f));
|
||||
|
||||
switch (f.fieldType)
|
||||
{
|
||||
case STI_UINT8: {
|
||||
dummy.setFieldU8(f, 0);
|
||||
CHECK(dummy.getFieldU8(f) == 0);
|
||||
|
||||
dummy.setFieldU8(f, 255);
|
||||
CHECK(dummy.getFieldU8(f) == 255);
|
||||
|
||||
CHECK(dummy.isFieldPresent(f));
|
||||
break;
|
||||
}
|
||||
|
||||
case STI_UINT16: {
|
||||
dummy.setFieldU16(f, 0);
|
||||
CHECK(dummy.getFieldU16(f) == 0);
|
||||
|
||||
dummy.setFieldU16(f, 0xFFFFU);
|
||||
CHECK(dummy.getFieldU16(f) == 0xFFFFU);
|
||||
|
||||
CHECK(dummy.isFieldPresent(f));
|
||||
break;
|
||||
}
|
||||
|
||||
case STI_UINT32: {
|
||||
dummy.setFieldU32(f, 0);
|
||||
CHECK(dummy.getFieldU32(f) == 0);
|
||||
|
||||
dummy.setFieldU32(f, 0xFFFFFFFFU);
|
||||
CHECK(dummy.getFieldU32(f) == 0xFFFFFFFFU);
|
||||
|
||||
CHECK(dummy.isFieldPresent(f));
|
||||
break;
|
||||
}
|
||||
|
||||
case STI_UINT64: {
|
||||
dummy.setFieldU64(f, 0);
|
||||
CHECK(dummy.getFieldU64(f) == 0);
|
||||
|
||||
dummy.setFieldU64(f, 0xFFFFFFFFFFFFFFFFU);
|
||||
CHECK(dummy.getFieldU64(f) == 0xFFFFFFFFFFFFFFFFU);
|
||||
|
||||
CHECK(dummy.isFieldPresent(f));
|
||||
break;
|
||||
}
|
||||
|
||||
case STI_UINT256: {
|
||||
uint256 u = uint256::fromVoid(
|
||||
"DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDE"
|
||||
"ADBEEF");
|
||||
dummy.setFieldH256(f, u);
|
||||
CHECK(dummy.getFieldH256(f) == u);
|
||||
CHECK(dummy.isFieldPresent(f));
|
||||
break;
|
||||
}
|
||||
|
||||
case STI_VL: {
|
||||
std::vector<uint8_t> v{1, 2, 3};
|
||||
dummy.setFieldVL(f, v);
|
||||
CHECK(dummy.getFieldVL(f) == v);
|
||||
CHECK(dummy.isFieldPresent(f));
|
||||
break;
|
||||
}
|
||||
|
||||
case STI_ACCOUNT: {
|
||||
AccountID id = *parseBase58<AccountID>(
|
||||
"rwfSjJNK2YQuN64bSWn7T2eY9FJAyAPYJT");
|
||||
dummy.setAccountID(f, id);
|
||||
CHECK(dummy.getAccountID(f) == id);
|
||||
CHECK(dummy.isFieldPresent(f));
|
||||
break;
|
||||
}
|
||||
|
||||
case STI_OBJECT: {
|
||||
dummy.emplace_back(STObject{f});
|
||||
CHECK(dummy.getField(f).getFName() == f);
|
||||
CHECK(dummy.isFieldPresent(f));
|
||||
break;
|
||||
}
|
||||
|
||||
case STI_ARRAY: {
|
||||
STArray dummy2{f, 2};
|
||||
dummy2.push_back(STObject{sfGeneric});
|
||||
dummy2.push_back(STObject{sfGeneric});
|
||||
dummy.setFieldArray(f, dummy2);
|
||||
CHECK(dummy.getFieldArray(f) == dummy2);
|
||||
CHECK(dummy.isFieldPresent(f));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
CHECK(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
@@ -1,860 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpl/basics/UnorderedContainers.h>
|
||||
#include <doctest/doctest.h>
|
||||
#include <xrpl/protocol/Book.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <typeinfo>
|
||||
#include <unordered_set>
|
||||
|
||||
#if BEAST_MSVC
|
||||
#define STL_SET_HAS_EMPLACE 1
|
||||
#else
|
||||
#define STL_SET_HAS_EMPLACE 0
|
||||
#endif
|
||||
|
||||
#ifndef RIPPLE_ASSETS_ENABLE_STD_HASH
|
||||
#if BEAST_MAC || BEAST_IOS
|
||||
#define RIPPLE_ASSETS_ENABLE_STD_HASH 0
|
||||
#else
|
||||
#define RIPPLE_ASSETS_ENABLE_STD_HASH 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace ripple {
|
||||
|
||||
namespace {
|
||||
|
||||
using Domain = uint256;
|
||||
|
||||
// Comparison, hash tests for uint60 (via base_uint)
|
||||
template <typename Unsigned>
|
||||
void
|
||||
testUnsigned()
|
||||
{
|
||||
Unsigned const u1(1);
|
||||
Unsigned const u2(2);
|
||||
Unsigned const u3(3);
|
||||
|
||||
CHECK(u1 != u2);
|
||||
CHECK(u1 < u2);
|
||||
CHECK(u1 <= u2);
|
||||
CHECK(u2 <= u2);
|
||||
CHECK(u2 == u2);
|
||||
CHECK(u2 >= u2);
|
||||
CHECK(u3 >= u2);
|
||||
CHECK(u3 > u2);
|
||||
|
||||
std::hash<Unsigned> hash;
|
||||
|
||||
CHECK(hash(u1) == hash(u1));
|
||||
CHECK(hash(u2) == hash(u2));
|
||||
CHECK(hash(u3) == hash(u3));
|
||||
CHECK(hash(u1) != hash(u2));
|
||||
CHECK(hash(u1) != hash(u3));
|
||||
CHECK(hash(u2) != hash(u3));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// Comparison, hash tests for Issue
|
||||
template <class Issue>
|
||||
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);
|
||||
|
||||
CHECK(Issue(c1, i1) != Issue(c2, i1));
|
||||
CHECK(Issue(c1, i1) < Issue(c2, i1));
|
||||
CHECK(Issue(c1, i1) <= Issue(c2, i1));
|
||||
CHECK(Issue(c2, i1) <= Issue(c2, i1));
|
||||
CHECK(Issue(c2, i1) == Issue(c2, i1));
|
||||
CHECK(Issue(c2, i1) >= Issue(c2, i1));
|
||||
CHECK(Issue(c3, i1) >= Issue(c2, i1));
|
||||
CHECK(Issue(c3, i1) > Issue(c2, i1));
|
||||
CHECK(Issue(c1, i1) != Issue(c1, i2));
|
||||
CHECK(Issue(c1, i1) < Issue(c1, i2));
|
||||
CHECK(Issue(c1, i1) <= Issue(c1, i2));
|
||||
CHECK(Issue(c1, i2) <= Issue(c1, i2));
|
||||
CHECK(Issue(c1, i2) == Issue(c1, i2));
|
||||
CHECK(Issue(c1, i2) >= Issue(c1, i2));
|
||||
CHECK(Issue(c1, i3) >= Issue(c1, i2));
|
||||
CHECK(Issue(c1, i3) > Issue(c1, i2));
|
||||
|
||||
std::hash<Issue> hash;
|
||||
|
||||
CHECK(hash(Issue(c1, i1)) == hash(Issue(c1, i1)));
|
||||
CHECK(hash(Issue(c1, i2)) == hash(Issue(c1, i2)));
|
||||
CHECK(hash(Issue(c1, i3)) == hash(Issue(c1, i3)));
|
||||
CHECK(hash(Issue(c2, i1)) == hash(Issue(c2, i1)));
|
||||
CHECK(hash(Issue(c2, i2)) == hash(Issue(c2, i2)));
|
||||
CHECK(hash(Issue(c2, i3)) == hash(Issue(c2, i3)));
|
||||
CHECK(hash(Issue(c3, i1)) == hash(Issue(c3, i1)));
|
||||
CHECK(hash(Issue(c3, i2)) == hash(Issue(c3, i2)));
|
||||
CHECK(hash(Issue(c3, i3)) == hash(Issue(c3, i3)));
|
||||
CHECK(hash(Issue(c1, i1)) != hash(Issue(c1, i2)));
|
||||
CHECK(hash(Issue(c1, i1)) != hash(Issue(c1, i3)));
|
||||
CHECK(hash(Issue(c1, i1)) != hash(Issue(c2, i1)));
|
||||
CHECK(hash(Issue(c1, i1)) != hash(Issue(c2, i2)));
|
||||
CHECK(hash(Issue(c1, i1)) != hash(Issue(c2, i3)));
|
||||
CHECK(hash(Issue(c1, i1)) != hash(Issue(c3, i1)));
|
||||
CHECK(hash(Issue(c1, i1)) != hash(Issue(c3, i2)));
|
||||
CHECK(hash(Issue(c1, i1)) != hash(Issue(c3, i3)));
|
||||
}
|
||||
|
||||
template <class Set>
|
||||
void
|
||||
testIssueSet()
|
||||
{
|
||||
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);
|
||||
|
||||
{
|
||||
Set c;
|
||||
|
||||
c.insert(a1);
|
||||
CHECK(c.size() == 1);
|
||||
c.insert(a2);
|
||||
CHECK(c.size() == 2);
|
||||
|
||||
CHECK(c.erase(Issue(c1, i2)) == 0);
|
||||
CHECK(c.erase(Issue(c1, i1)) == 1);
|
||||
CHECK(c.erase(Issue(c2, i2)) == 1);
|
||||
CHECK(c.empty());
|
||||
}
|
||||
|
||||
{
|
||||
Set c;
|
||||
|
||||
c.insert(a1);
|
||||
CHECK(c.size() == 1);
|
||||
c.insert(a2);
|
||||
CHECK(c.size() == 2);
|
||||
|
||||
CHECK(c.erase(Issue(c1, i2)) == 0);
|
||||
CHECK(c.erase(Issue(c1, i1)) == 1);
|
||||
CHECK(c.erase(Issue(c2, i2)) == 1);
|
||||
CHECK(c.empty());
|
||||
|
||||
#if STL_SET_HAS_EMPLACE
|
||||
c.emplace(c1, i1);
|
||||
CHECK(c.size() == 1);
|
||||
c.emplace(c2, i2);
|
||||
CHECK(c.size() == 2);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
template <class Map>
|
||||
void
|
||||
testIssueMap()
|
||||
{
|
||||
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);
|
||||
|
||||
{
|
||||
Map c;
|
||||
|
||||
c.insert(std::make_pair(a1, 1));
|
||||
CHECK(c.size() == 1);
|
||||
c.insert(std::make_pair(a2, 2));
|
||||
CHECK(c.size() == 2);
|
||||
|
||||
CHECK(c.erase(Issue(c1, i2)) == 0);
|
||||
CHECK(c.erase(Issue(c1, i1)) == 1);
|
||||
CHECK(c.erase(Issue(c2, i2)) == 1);
|
||||
CHECK(c.empty());
|
||||
}
|
||||
|
||||
{
|
||||
Map c;
|
||||
|
||||
c.insert(std::make_pair(a1, 1));
|
||||
CHECK(c.size() == 1);
|
||||
c.insert(std::make_pair(a2, 2));
|
||||
CHECK(c.size() == 2);
|
||||
|
||||
CHECK(c.erase(Issue(c1, i2)) == 0);
|
||||
CHECK(c.erase(Issue(c1, i1)) == 1);
|
||||
CHECK(c.erase(Issue(c2, i2)) == 1);
|
||||
CHECK(c.empty());
|
||||
}
|
||||
}
|
||||
|
||||
template <class Set>
|
||||
void
|
||||
testIssueDomainSet()
|
||||
{
|
||||
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};
|
||||
uint256 const domain2{2};
|
||||
|
||||
Set c;
|
||||
|
||||
c.insert(std::make_pair(a1, domain1));
|
||||
CHECK(c.size() == 1);
|
||||
c.insert(std::make_pair(a2, domain1));
|
||||
CHECK(c.size() == 2);
|
||||
c.insert(std::make_pair(a2, domain2));
|
||||
CHECK(c.size() == 3);
|
||||
|
||||
CHECK(c.erase(std::make_pair(Issue(c1, i2), domain1)) == 0);
|
||||
CHECK(c.erase(std::make_pair(a1, domain1)) == 1);
|
||||
CHECK(c.erase(std::make_pair(a2, domain1)) == 1);
|
||||
CHECK(c.erase(std::make_pair(a2, domain2)) == 1);
|
||||
CHECK(c.empty());
|
||||
}
|
||||
|
||||
template <class Map>
|
||||
void
|
||||
testIssueDomainMap()
|
||||
{
|
||||
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};
|
||||
uint256 const domain2{2};
|
||||
|
||||
Map c;
|
||||
|
||||
c.insert(std::make_pair(std::make_pair(a1, domain1), 1));
|
||||
CHECK(c.size() == 1);
|
||||
c.insert(std::make_pair(std::make_pair(a2, domain1), 2));
|
||||
CHECK(c.size() == 2);
|
||||
c.insert(std::make_pair(std::make_pair(a2, domain2), 2));
|
||||
CHECK(c.size() == 3);
|
||||
|
||||
CHECK(c.erase(std::make_pair(Issue(c1, i2), domain1)) == 0);
|
||||
CHECK(c.erase(std::make_pair(a1, domain1)) == 1);
|
||||
CHECK(c.erase(std::make_pair(a2, domain1)) == 1);
|
||||
CHECK(c.erase(std::make_pair(a2, domain2)) == 1);
|
||||
CHECK(c.empty());
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// Comparison, hash tests for Book
|
||||
template <class Book>
|
||||
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);
|
||||
|
||||
Issue a1(c1, i1);
|
||||
Issue a2(c1, i2);
|
||||
Issue a3(c2, i2);
|
||||
Issue a4(c3, i2);
|
||||
uint256 const domain1{1};
|
||||
uint256 const domain2{2};
|
||||
|
||||
// Books without domains
|
||||
CHECK(Book(a1, a2, std::nullopt) != Book(a2, a3, std::nullopt));
|
||||
CHECK(Book(a1, a2, std::nullopt) < Book(a2, a3, std::nullopt));
|
||||
CHECK(Book(a1, a2, std::nullopt) <= Book(a2, a3, std::nullopt));
|
||||
CHECK(Book(a2, a3, std::nullopt) <= Book(a2, a3, std::nullopt));
|
||||
CHECK(Book(a2, a3, std::nullopt) == Book(a2, a3, std::nullopt));
|
||||
CHECK(Book(a2, a3, std::nullopt) >= Book(a2, a3, std::nullopt));
|
||||
CHECK(Book(a3, a4, std::nullopt) >= Book(a2, a3, std::nullopt));
|
||||
CHECK(Book(a3, a4, std::nullopt) > Book(a2, a3, std::nullopt));
|
||||
|
||||
// test domain books
|
||||
{
|
||||
// Books with different domains
|
||||
CHECK(Book(a2, a3, domain1) != Book(a2, a3, domain2));
|
||||
CHECK(Book(a2, a3, domain1) < Book(a2, a3, domain2));
|
||||
CHECK(Book(a2, a3, domain2) > Book(a2, a3, domain1));
|
||||
|
||||
// One Book has a domain, the other does not
|
||||
CHECK(Book(a2, a3, domain1) != Book(a2, a3, std::nullopt));
|
||||
CHECK(Book(a2, a3, std::nullopt) < Book(a2, a3, domain1));
|
||||
CHECK(Book(a2, a3, domain1) > Book(a2, a3, std::nullopt));
|
||||
|
||||
// Both Books have the same domain
|
||||
CHECK(Book(a2, a3, domain1) == Book(a2, a3, domain1));
|
||||
CHECK(Book(a2, a3, domain2) == Book(a2, a3, domain2));
|
||||
CHECK(
|
||||
Book(a2, a3, std::nullopt) == Book(a2, a3, std::nullopt));
|
||||
|
||||
// Both Books have no domain
|
||||
CHECK(
|
||||
Book(a2, a3, std::nullopt) == Book(a2, a3, std::nullopt));
|
||||
|
||||
// Testing comparisons with >= and <=
|
||||
|
||||
// When comparing books with domain1 vs domain2
|
||||
CHECK(Book(a2, a3, domain1) <= Book(a2, a3, domain2));
|
||||
CHECK(Book(a2, a3, domain2) >= Book(a2, a3, domain1));
|
||||
CHECK(Book(a2, a3, domain1) >= Book(a2, a3, domain1));
|
||||
CHECK(Book(a2, a3, domain2) <= Book(a2, a3, domain2));
|
||||
|
||||
// One Book has domain1 and the other has no domain
|
||||
CHECK(Book(a2, a3, domain1) > Book(a2, a3, std::nullopt));
|
||||
CHECK(Book(a2, a3, std::nullopt) < Book(a2, a3, domain1));
|
||||
|
||||
// One Book has domain2 and the other has no domain
|
||||
CHECK(Book(a2, a3, domain2) > Book(a2, a3, std::nullopt));
|
||||
CHECK(Book(a2, a3, std::nullopt) < Book(a2, a3, domain2));
|
||||
|
||||
// Comparing two Books with no domains
|
||||
CHECK(
|
||||
Book(a2, a3, std::nullopt) <= Book(a2, a3, std::nullopt));
|
||||
CHECK(
|
||||
Book(a2, a3, std::nullopt) >= Book(a2, a3, std::nullopt));
|
||||
|
||||
// Test case where domain1 is less than domain2
|
||||
CHECK(Book(a2, a3, domain1) <= Book(a2, a3, domain2));
|
||||
CHECK(Book(a2, a3, domain2) >= Book(a2, a3, domain1));
|
||||
|
||||
// Test case where domain2 is equal to domain1
|
||||
CHECK(Book(a2, a3, domain1) >= Book(a2, a3, domain1));
|
||||
CHECK(Book(a2, a3, domain1) <= Book(a2, a3, domain1));
|
||||
|
||||
// More test cases involving a4 (with domain2)
|
||||
|
||||
// Comparing Book with domain2 (a4) to a Book with domain1
|
||||
CHECK(Book(a2, a3, domain1) < Book(a3, a4, domain2));
|
||||
CHECK(Book(a3, a4, domain2) > Book(a2, a3, domain1));
|
||||
|
||||
// Comparing Book with domain2 (a4) to a Book with no domain
|
||||
CHECK(Book(a3, a4, domain2) > Book(a2, a3, std::nullopt));
|
||||
CHECK(Book(a2, a3, std::nullopt) < Book(a3, a4, domain2));
|
||||
|
||||
// Comparing Book with domain2 (a4) to a Book with the same domain
|
||||
CHECK(Book(a3, a4, domain2) == Book(a3, a4, domain2));
|
||||
|
||||
// Comparing Book with domain2 (a4) to a Book with domain1
|
||||
CHECK(Book(a2, a3, domain1) < Book(a3, a4, domain2));
|
||||
CHECK(Book(a3, a4, domain2) > Book(a2, a3, domain1));
|
||||
}
|
||||
|
||||
std::hash<Book> hash;
|
||||
|
||||
// log << std::hex << hash (Book (a1, a2));
|
||||
// log << std::hex << hash (Book (a1, a2));
|
||||
//
|
||||
// log << std::hex << hash (Book (a1, a3));
|
||||
// log << std::hex << hash (Book (a1, a3));
|
||||
//
|
||||
// log << std::hex << hash (Book (a1, a4));
|
||||
// log << std::hex << hash (Book (a1, a4));
|
||||
//
|
||||
// log << std::hex << hash (Book (a2, a3));
|
||||
// log << std::hex << hash (Book (a2, a3));
|
||||
//
|
||||
// log << std::hex << hash (Book (a2, a4));
|
||||
// log << std::hex << hash (Book (a2, a4));
|
||||
//
|
||||
// log << std::hex << hash (Book (a3, a4));
|
||||
// log << std::hex << hash (Book (a3, a4));
|
||||
|
||||
CHECK(
|
||||
hash(Book(a1, a2, std::nullopt)) ==
|
||||
hash(Book(a1, a2, std::nullopt)));
|
||||
CHECK(
|
||||
hash(Book(a1, a3, std::nullopt)) ==
|
||||
hash(Book(a1, a3, std::nullopt)));
|
||||
CHECK(
|
||||
hash(Book(a1, a4, std::nullopt)) ==
|
||||
hash(Book(a1, a4, std::nullopt)));
|
||||
CHECK(
|
||||
hash(Book(a2, a3, std::nullopt)) ==
|
||||
hash(Book(a2, a3, std::nullopt)));
|
||||
CHECK(
|
||||
hash(Book(a2, a4, std::nullopt)) ==
|
||||
hash(Book(a2, a4, std::nullopt)));
|
||||
CHECK(
|
||||
hash(Book(a3, a4, std::nullopt)) ==
|
||||
hash(Book(a3, a4, std::nullopt)));
|
||||
|
||||
CHECK(
|
||||
hash(Book(a1, a2, std::nullopt)) !=
|
||||
hash(Book(a1, a3, std::nullopt)));
|
||||
CHECK(
|
||||
hash(Book(a1, a2, std::nullopt)) !=
|
||||
hash(Book(a1, a4, std::nullopt)));
|
||||
CHECK(
|
||||
hash(Book(a1, a2, std::nullopt)) !=
|
||||
hash(Book(a2, a3, std::nullopt)));
|
||||
CHECK(
|
||||
hash(Book(a1, a2, std::nullopt)) !=
|
||||
hash(Book(a2, a4, std::nullopt)));
|
||||
CHECK(
|
||||
hash(Book(a1, a2, std::nullopt)) !=
|
||||
hash(Book(a3, a4, std::nullopt)));
|
||||
|
||||
// Books with domain
|
||||
CHECK(
|
||||
hash(Book(a1, a2, domain1)) == hash(Book(a1, a2, domain1)));
|
||||
CHECK(
|
||||
hash(Book(a1, a3, domain1)) == hash(Book(a1, a3, domain1)));
|
||||
CHECK(
|
||||
hash(Book(a1, a4, domain1)) == hash(Book(a1, a4, domain1)));
|
||||
CHECK(
|
||||
hash(Book(a2, a3, domain1)) == hash(Book(a2, a3, domain1)));
|
||||
CHECK(
|
||||
hash(Book(a2, a4, domain1)) == hash(Book(a2, a4, domain1)));
|
||||
CHECK(
|
||||
hash(Book(a3, a4, domain1)) == hash(Book(a3, a4, domain1)));
|
||||
CHECK(
|
||||
hash(Book(a1, a2, std::nullopt)) ==
|
||||
hash(Book(a1, a2, std::nullopt)));
|
||||
|
||||
// Comparing Books with domain1 vs no domain
|
||||
CHECK(
|
||||
hash(Book(a1, a2, std::nullopt)) != hash(Book(a1, a2, domain1)));
|
||||
CHECK(
|
||||
hash(Book(a1, a3, std::nullopt)) != hash(Book(a1, a3, domain1)));
|
||||
CHECK(
|
||||
hash(Book(a1, a4, std::nullopt)) != hash(Book(a1, a4, domain1)));
|
||||
CHECK(
|
||||
hash(Book(a2, a3, std::nullopt)) != hash(Book(a2, a3, domain1)));
|
||||
CHECK(
|
||||
hash(Book(a2, a4, std::nullopt)) != hash(Book(a2, a4, domain1)));
|
||||
CHECK(
|
||||
hash(Book(a3, a4, std::nullopt)) != hash(Book(a3, a4, domain1)));
|
||||
|
||||
// Books with domain1 but different Issues
|
||||
CHECK(
|
||||
hash(Book(a1, a2, domain1)) != hash(Book(a1, a3, domain1)));
|
||||
CHECK(
|
||||
hash(Book(a1, a2, domain1)) != hash(Book(a1, a4, domain1)));
|
||||
CHECK(
|
||||
hash(Book(a2, a3, domain1)) != hash(Book(a2, a4, domain1)));
|
||||
CHECK(
|
||||
hash(Book(a1, a2, domain1)) != hash(Book(a2, a3, domain1)));
|
||||
CHECK(
|
||||
hash(Book(a2, a4, domain1)) != hash(Book(a3, a4, domain1)));
|
||||
CHECK(
|
||||
hash(Book(a3, a4, domain1)) != hash(Book(a1, a4, domain1)));
|
||||
|
||||
// Books with domain1 and domain2
|
||||
CHECK(
|
||||
hash(Book(a1, a2, domain1)) != hash(Book(a1, a2, domain2)));
|
||||
CHECK(
|
||||
hash(Book(a1, a3, domain1)) != hash(Book(a1, a3, domain2)));
|
||||
CHECK(
|
||||
hash(Book(a1, a4, domain1)) != hash(Book(a1, a4, domain2)));
|
||||
CHECK(
|
||||
hash(Book(a2, a3, domain1)) != hash(Book(a2, a3, domain2)));
|
||||
CHECK(
|
||||
hash(Book(a2, a4, domain1)) != hash(Book(a2, a4, domain2)));
|
||||
CHECK(
|
||||
hash(Book(a3, a4, domain1)) != hash(Book(a3, a4, domain2)));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
template <class Set>
|
||||
void
|
||||
testBookSet()
|
||||
{
|
||||
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);
|
||||
Book const b2(a2, a1, std::nullopt);
|
||||
|
||||
uint256 const domain1{1};
|
||||
uint256 const domain2{2};
|
||||
|
||||
Book const b1_d1(a1, a2, domain1);
|
||||
Book const b2_d1(a2, a1, domain1);
|
||||
Book const b1_d2(a1, a2, domain2);
|
||||
Book const b2_d2(a2, a1, domain2);
|
||||
|
||||
{
|
||||
Set c;
|
||||
|
||||
c.insert(b1);
|
||||
CHECK(c.size() == 1);
|
||||
c.insert(b2);
|
||||
CHECK(c.size() == 2);
|
||||
|
||||
CHECK(c.erase(Book(a1, a1, std::nullopt)) == 0);
|
||||
CHECK(c.erase(Book(a1, a2, std::nullopt)) == 1);
|
||||
CHECK(c.erase(Book(a2, a1, std::nullopt)) == 1);
|
||||
CHECK(c.empty());
|
||||
}
|
||||
|
||||
{
|
||||
Set c;
|
||||
|
||||
c.insert(b1);
|
||||
CHECK(c.size() == 1);
|
||||
c.insert(b2);
|
||||
CHECK(c.size() == 2);
|
||||
|
||||
CHECK(c.erase(Book(a1, a1, std::nullopt)) == 0);
|
||||
CHECK(c.erase(Book(a1, a2, std::nullopt)) == 1);
|
||||
CHECK(c.erase(Book(a2, a1, std::nullopt)) == 1);
|
||||
CHECK(c.empty());
|
||||
|
||||
#if STL_SET_HAS_EMPLACE
|
||||
c.emplace(a1, a2);
|
||||
CHECK(c.size() == 1);
|
||||
c.emplace(a2, a1);
|
||||
CHECK(c.size() == 2);
|
||||
#endif
|
||||
}
|
||||
|
||||
{
|
||||
Set c;
|
||||
|
||||
c.insert(b1_d1);
|
||||
CHECK(c.size() == 1);
|
||||
c.insert(b2_d1);
|
||||
CHECK(c.size() == 2);
|
||||
c.insert(b1_d2);
|
||||
CHECK(c.size() == 3);
|
||||
c.insert(b2_d2);
|
||||
CHECK(c.size() == 4);
|
||||
|
||||
// Try removing non-existent elements
|
||||
CHECK(c.erase(Book(a2, a2, domain1)) == 0);
|
||||
|
||||
CHECK(c.erase(Book(a1, a2, domain1)) == 1);
|
||||
CHECK(c.erase(Book(a2, a1, domain1)) == 1);
|
||||
CHECK(c.size() == 2);
|
||||
|
||||
CHECK(c.erase(Book(a1, a2, domain2)) == 1);
|
||||
CHECK(c.erase(Book(a2, a1, domain2)) == 1);
|
||||
CHECK(c.empty());
|
||||
}
|
||||
|
||||
{
|
||||
Set c;
|
||||
|
||||
c.insert(b1);
|
||||
c.insert(b2);
|
||||
c.insert(b1_d1);
|
||||
c.insert(b2_d1);
|
||||
CHECK(c.size() == 4);
|
||||
|
||||
CHECK(c.erase(Book(a1, a2, std::nullopt)) == 1);
|
||||
CHECK(c.erase(Book(a2, a1, std::nullopt)) == 1);
|
||||
CHECK(c.size() == 2);
|
||||
|
||||
CHECK(c.erase(Book(a1, a2, domain1)) == 1);
|
||||
CHECK(c.erase(Book(a2, a1, domain1)) == 1);
|
||||
CHECK(c.empty());
|
||||
}
|
||||
}
|
||||
|
||||
template <class Map>
|
||||
void
|
||||
testBookMap()
|
||||
{
|
||||
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);
|
||||
Book const b2(a2, a1, std::nullopt);
|
||||
|
||||
uint256 const domain1{1};
|
||||
uint256 const domain2{2};
|
||||
|
||||
Book const b1_d1(a1, a2, domain1);
|
||||
Book const b2_d1(a2, a1, domain1);
|
||||
Book const b1_d2(a1, a2, domain2);
|
||||
Book const b2_d2(a2, a1, domain2);
|
||||
|
||||
// typename Map::value_type value_type;
|
||||
// std::pair <Book const, int> value_type;
|
||||
|
||||
{
|
||||
Map c;
|
||||
|
||||
// c.insert (value_type (b1, 1));
|
||||
c.insert(std::make_pair(b1, 1));
|
||||
CHECK(c.size() == 1);
|
||||
// c.insert (value_type (b2, 2));
|
||||
c.insert(std::make_pair(b2, 1));
|
||||
CHECK(c.size() == 2);
|
||||
|
||||
CHECK(c.erase(Book(a1, a1, std::nullopt)) == 0);
|
||||
CHECK(c.erase(Book(a1, a2, std::nullopt)) == 1);
|
||||
CHECK(c.erase(Book(a2, a1, std::nullopt)) == 1);
|
||||
CHECK(c.empty());
|
||||
}
|
||||
|
||||
{
|
||||
Map c;
|
||||
|
||||
// c.insert (value_type (b1, 1));
|
||||
c.insert(std::make_pair(b1, 1));
|
||||
CHECK(c.size() == 1);
|
||||
// c.insert (value_type (b2, 2));
|
||||
c.insert(std::make_pair(b2, 1));
|
||||
CHECK(c.size() == 2);
|
||||
|
||||
CHECK(c.erase(Book(a1, a1, std::nullopt)) == 0);
|
||||
CHECK(c.erase(Book(a1, a2, std::nullopt)) == 1);
|
||||
CHECK(c.erase(Book(a2, a1, std::nullopt)) == 1);
|
||||
CHECK(c.empty());
|
||||
}
|
||||
|
||||
{
|
||||
Map c;
|
||||
|
||||
c.insert(std::make_pair(b1_d1, 10));
|
||||
CHECK(c.size() == 1);
|
||||
c.insert(std::make_pair(b2_d1, 20));
|
||||
CHECK(c.size() == 2);
|
||||
c.insert(std::make_pair(b1_d2, 30));
|
||||
CHECK(c.size() == 3);
|
||||
c.insert(std::make_pair(b2_d2, 40));
|
||||
CHECK(c.size() == 4);
|
||||
|
||||
// Try removing non-existent elements
|
||||
CHECK(c.erase(Book(a2, a2, domain1)) == 0);
|
||||
|
||||
CHECK(c.erase(Book(a1, a2, domain1)) == 1);
|
||||
CHECK(c.erase(Book(a2, a1, domain1)) == 1);
|
||||
CHECK(c.size() == 2);
|
||||
|
||||
CHECK(c.erase(Book(a1, a2, domain2)) == 1);
|
||||
CHECK(c.erase(Book(a2, a1, domain2)) == 1);
|
||||
CHECK(c.empty());
|
||||
}
|
||||
|
||||
{
|
||||
Map c;
|
||||
|
||||
c.insert(std::make_pair(b1, 1));
|
||||
c.insert(std::make_pair(b2, 2));
|
||||
c.insert(std::make_pair(b1_d1, 3));
|
||||
c.insert(std::make_pair(b2_d1, 4));
|
||||
CHECK(c.size() == 4);
|
||||
|
||||
// Try removing non-existent elements
|
||||
CHECK(c.erase(Book(a1, a1, domain1)) == 0);
|
||||
CHECK(c.erase(Book(a2, a2, domain2)) == 0);
|
||||
|
||||
CHECK(c.erase(Book(a1, a2, std::nullopt)) == 1);
|
||||
CHECK(c.erase(Book(a2, a1, std::nullopt)) == 1);
|
||||
CHECK(c.size() == 2);
|
||||
|
||||
CHECK(c.erase(Book(a1, a2, domain1)) == 1);
|
||||
CHECK(c.erase(Book(a2, a1, domain1)) == 1);
|
||||
CHECK(c.empty());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// End of helper functions.
|
||||
}
|
||||
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
using namespace ripple;
|
||||
|
||||
TEST_SUITE_BEGIN("Issue");
|
||||
|
||||
TEST_CASE("Unsigned types")
|
||||
{
|
||||
testUnsigned<Currency>();
|
||||
testUnsigned<AccountID>();
|
||||
}
|
||||
|
||||
TEST_CASE("Issue")
|
||||
{
|
||||
testIssue<Issue>();
|
||||
}
|
||||
|
||||
TEST_CASE("Issue sets")
|
||||
{
|
||||
INFO("std::set <Issue>");
|
||||
testIssueSet<std::set<Issue>>();
|
||||
|
||||
INFO("std::set <Issue>");
|
||||
testIssueSet<std::set<Issue>>();
|
||||
|
||||
#if RIPPLE_ASSETS_ENABLE_STD_HASH
|
||||
INFO("std::unordered_set <Issue>");
|
||||
testIssueSet<std::unordered_set<Issue>>();
|
||||
|
||||
INFO("std::unordered_set <Issue>");
|
||||
testIssueSet<std::unordered_set<Issue>>();
|
||||
#endif
|
||||
|
||||
INFO("hash_set <Issue>");
|
||||
testIssueSet<hash_set<Issue>>();
|
||||
|
||||
INFO("hash_set <Issue>");
|
||||
testIssueSet<hash_set<Issue>>();
|
||||
}
|
||||
|
||||
TEST_CASE("Issue maps")
|
||||
{
|
||||
INFO("std::map <Issue, int>");
|
||||
testIssueMap<std::map<Issue, int>>();
|
||||
|
||||
INFO("std::map <Issue, int>");
|
||||
testIssueMap<std::map<Issue, int>>();
|
||||
|
||||
#if RIPPLE_ASSETS_ENABLE_STD_HASH
|
||||
INFO("std::unordered_map <Issue, int>");
|
||||
testIssueMap<std::unordered_map<Issue, int>>();
|
||||
|
||||
INFO("std::unordered_map <Issue, int>");
|
||||
testIssueMap<std::unordered_map<Issue, int>>();
|
||||
|
||||
INFO("hash_map <Issue, int>");
|
||||
testIssueMap<hash_map<Issue, int>>();
|
||||
|
||||
INFO("hash_map <Issue, int>");
|
||||
testIssueMap<hash_map<Issue, int>>();
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("Book")
|
||||
{
|
||||
testBook<Book>();
|
||||
}
|
||||
|
||||
TEST_CASE("Book sets")
|
||||
{
|
||||
INFO("std::set <Book>");
|
||||
testBookSet<std::set<Book>>();
|
||||
|
||||
INFO("std::set <Book>");
|
||||
testBookSet<std::set<Book>>();
|
||||
|
||||
#if RIPPLE_ASSETS_ENABLE_STD_HASH
|
||||
INFO("std::unordered_set <Book>");
|
||||
testBookSet<std::unordered_set<Book>>();
|
||||
|
||||
INFO("std::unordered_set <Book>");
|
||||
testBookSet<std::unordered_set<Book>>();
|
||||
#endif
|
||||
|
||||
INFO("hash_set <Book>");
|
||||
testBookSet<hash_set<Book>>();
|
||||
|
||||
INFO("hash_set <Book>");
|
||||
testBookSet<hash_set<Book>>();
|
||||
}
|
||||
|
||||
TEST_CASE("Book maps")
|
||||
{
|
||||
INFO("std::map <Book, int>");
|
||||
testBookMap<std::map<Book, int>>();
|
||||
|
||||
INFO("std::map <Book, int>");
|
||||
testBookMap<std::map<Book, int>>();
|
||||
|
||||
#if RIPPLE_ASSETS_ENABLE_STD_HASH
|
||||
INFO("std::unordered_map <Book, int>");
|
||||
testBookMap<std::unordered_map<Book, int>>();
|
||||
|
||||
INFO("std::unordered_map <Book, int>");
|
||||
testBookMap<std::unordered_map<Book, int>>();
|
||||
|
||||
INFO("hash_map <Book, int>");
|
||||
testBookMap<hash_map<Book, int>>();
|
||||
|
||||
INFO("hash_map <Book, int>");
|
||||
testBookMap<hash_map<Book, int>>();
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("Issue domain sets")
|
||||
{
|
||||
INFO("std::set <std::pair<Issue, Domain>>");
|
||||
testIssueDomainSet<std::set<std::pair<Issue, Domain>>>();
|
||||
|
||||
INFO("std::set <std::pair<Issue, Domain>>");
|
||||
testIssueDomainSet<std::set<std::pair<Issue, Domain>>>();
|
||||
|
||||
INFO("hash_set <std::pair<Issue, Domain>>");
|
||||
testIssueDomainSet<hash_set<std::pair<Issue, Domain>>>();
|
||||
|
||||
INFO("hash_set <std::pair<Issue, Domain>>");
|
||||
testIssueDomainSet<hash_set<std::pair<Issue, Domain>>>();
|
||||
}
|
||||
|
||||
TEST_CASE("Issue domain maps")
|
||||
{
|
||||
INFO("std::map <std::pair<Issue, Domain>, int>");
|
||||
testIssueDomainMap<std::map<std::pair<Issue, Domain>, int>>();
|
||||
|
||||
INFO("std::map <std::pair<Issue, Domain>, int>");
|
||||
testIssueDomainMap<std::map<std::pair<Issue, Domain>, int>>();
|
||||
|
||||
#if RIPPLE_ASSETS_ENABLE_STD_HASH
|
||||
INFO("hash_map <std::pair<Issue, Domain>, int>");
|
||||
testIssueDomainMap<hash_map<std::pair<Issue, Domain>, int>>();
|
||||
|
||||
INFO("hash_map <std::pair<Issue, Domain>, int>");
|
||||
testIssueDomainMap<hash_map<std::pair<Issue, Domain>, int>>();
|
||||
|
||||
INFO("hardened_hash_map <std::pair<Issue, Domain>, int>");
|
||||
testIssueDomainMap<hardened_hash_map<std::pair<Issue, Domain>, int>>();
|
||||
|
||||
INFO("hardened_hash_map <std::pair<Issue, Domain>, int>");
|
||||
testIssueDomainMap<hardened_hash_map<std::pair<Issue, Domain>, int>>();
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2022 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <test/jtx.h>
|
||||
#include <doctest/doctest.h>
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
|
||||
#include <xrpl/basics/strHex.h>
|
||||
|
||||
using namespace ripple;
|
||||
|
||||
TEST_SUITE_BEGIN("Memo");
|
||||
|
||||
TEST_CASE("Memo")
|
||||
{
|
||||
struct DummySuite : beast::unit_test::suite
|
||||
{
|
||||
void run() override {}
|
||||
} suite;
|
||||
|
||||
using namespace test::jtx;
|
||||
Account alice{"alice"};
|
||||
|
||||
Env env{suite};
|
||||
env.fund(XRP(10000), alice);
|
||||
env.close();
|
||||
|
||||
// Lambda that returns a valid JTx with a memo that we can hack up.
|
||||
// This is the basis for building tests of invalid states.
|
||||
auto makeJtxWithMemo = [&env, &alice]() {
|
||||
JTx example = noop(alice);
|
||||
memo const exampleMemo{"tic", "tac", "toe"};
|
||||
exampleMemo(env, example);
|
||||
return example;
|
||||
};
|
||||
|
||||
// A valid memo.
|
||||
env(makeJtxWithMemo());
|
||||
env.close();
|
||||
|
||||
{
|
||||
// Make sure that too big a memo is flagged as invalid.
|
||||
JTx memoSize = makeJtxWithMemo();
|
||||
memoSize.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfMemoData.jsonName] =
|
||||
std::string(2020, '0');
|
||||
env(memoSize,
|
||||
rpc(
|
||||
"invalidTransaction",
|
||||
"fails local checks: The memo exceeds the maximum allowed size."));
|
||||
|
||||
// This memo is just barely small enough.
|
||||
memoSize.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfMemoData.jsonName] =
|
||||
std::string(2018, '1');
|
||||
env(memoSize);
|
||||
}
|
||||
{
|
||||
// Put a non-Memo in the Memos array.
|
||||
JTx memoNonMemo = noop(alice);
|
||||
auto& jv = memoNonMemo.jv;
|
||||
auto& ma = jv[sfMemos.jsonName];
|
||||
auto& mi = ma[ma.size()];
|
||||
auto& m = mi[sfCreatedNode.jsonName]; // CreatedNode in Memos
|
||||
m[sfMemoData.jsonName] = "3030303030";
|
||||
|
||||
env(
|
||||
memoNonMemo,
|
||||
rpc(
|
||||
"invalidTransaction",
|
||||
"fails local checks: A memo array may contain only Memo objects."));
|
||||
}
|
||||
{
|
||||
// Put an invalid field in a Memo object.
|
||||
JTx memoExtra = makeJtxWithMemo();
|
||||
memoExtra.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfFlags.jsonName] = 13;
|
||||
env(
|
||||
memoExtra,
|
||||
rpc(
|
||||
"invalidTransaction",
|
||||
"fails local checks: A memo may contain only MemoType, MemoData or MemoFormat fields."));
|
||||
}
|
||||
{
|
||||
// Put a character that is not allowed in a URL in a MemoType field.
|
||||
JTx memoBadChar = makeJtxWithMemo();
|
||||
memoBadChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfMemoType.jsonName] =
|
||||
strHex(std::string_view("ONE<INFINITY"));
|
||||
env(
|
||||
memoBadChar,
|
||||
rpc(
|
||||
"invalidTransaction",
|
||||
"fails local checks: The MemoType and MemoFormat fields may only contain characters that are allowed in URLs under RFC 3986."));
|
||||
}
|
||||
{
|
||||
// Put a character that is not allowed in a URL in a MemoData field.
|
||||
// That's okay.
|
||||
JTx memoLegitChar = makeJtxWithMemo();
|
||||
memoLegitChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfMemoData.jsonName] =
|
||||
strHex(std::string_view("ONE<INFINITY"));
|
||||
env(memoLegitChar);
|
||||
}
|
||||
{
|
||||
// Put a character that is not allowed in a URL in a MemoFormat.
|
||||
JTx memoBadChar = makeJtxWithMemo();
|
||||
memoBadChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfMemoFormat.jsonName] =
|
||||
strHex(std::string_view("NoBraces{}InURL"));
|
||||
env(
|
||||
memoBadChar,
|
||||
rpc(
|
||||
"invalidTransaction",
|
||||
"fails local checks: The MemoType and MemoFormat fields may only contain characters that are allowed in URLs under RFC 3986."));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
@@ -1,646 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/XRPLF/rippled/
|
||||
Copyright (c) 2023 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
#include <xrpl/protocol/MultiApiJson.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
namespace {
|
||||
|
||||
// This needs to be in a namespace because of deduction guide
|
||||
template <typename... Ts>
|
||||
struct Overload : Ts...
|
||||
{
|
||||
using Ts::operator()...;
|
||||
};
|
||||
template <typename... Ts>
|
||||
Overload(Ts...) -> Overload<Ts...>;
|
||||
|
||||
} // namespace
|
||||
|
||||
static auto
|
||||
makeJson(char const* key, int val)
|
||||
{
|
||||
Json::Value obj(Json::objectValue);
|
||||
obj[key] = val;
|
||||
return obj;
|
||||
}
|
||||
|
||||
struct MultiApiJsonFixture
|
||||
{
|
||||
using MultiApiJson13 = ripple::detail::MultiApiJson<1, 3>;
|
||||
|
||||
Json::Value const obj1{makeJson("value", 1)};
|
||||
Json::Value const obj2{makeJson("value", 2)};
|
||||
Json::Value const obj3{makeJson("value", 3)};
|
||||
Json::Value const jsonNull{};
|
||||
|
||||
MultiApiJsonFixture() = default;
|
||||
};
|
||||
TEST_SUITE_BEGIN("MultiApiJson");
|
||||
|
||||
TEST_CASE_METHOD(
|
||||
MultiApiJsonFixture,
|
||||
"forApiVersions, forAllApiVersions",
|
||||
"[MultiApiJson]")
|
||||
{
|
||||
using ripple::detail::MultiApiJson;
|
||||
|
||||
MultiApiJson13 subject{};
|
||||
static_assert(sizeof(subject) == sizeof(subject.val));
|
||||
static_assert(subject.size == subject.val.size());
|
||||
static_assert(
|
||||
std::is_same_v<decltype(subject.val), std::array<Json::Value, 3>>);
|
||||
|
||||
CHECK(subject.val.size() == 3);
|
||||
CHECK(subject.val == std::array<Json::Value, 3>{jsonNull, jsonNull, jsonNull});
|
||||
|
||||
subject.val[0] = obj1;
|
||||
subject.val[1] = obj2;
|
||||
|
||||
// Some static data for test inputs
|
||||
static int const primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23,
|
||||
29, 31, 37, 41, 43, 47, 53, 59, 61,
|
||||
67, 71, 73, 79, 83, 89, 97};
|
||||
static_assert(std::size(primes) > RPC::apiMaximumValidVersion);
|
||||
|
||||
MultiApiJson13 s1{};
|
||||
static_assert(
|
||||
s1.size ==
|
||||
RPC::apiMaximumValidVersion + 1 - RPC::apiMinimumSupportedVersion);
|
||||
|
||||
int productAllVersions = 1;
|
||||
for (unsigned i = RPC::apiMinimumSupportedVersion;
|
||||
i <= RPC::apiMaximumValidVersion;
|
||||
++i)
|
||||
{
|
||||
auto const index = i - RPC::apiMinimumSupportedVersion;
|
||||
CHECK(index == s1.index(i));
|
||||
CHECK(s1.valid(i));
|
||||
s1.val[index] = makeJson("value", primes[i]);
|
||||
productAllVersions *= primes[i];
|
||||
}
|
||||
CHECK(!s1.valid(0));
|
||||
CHECK(!s1.valid(RPC::apiMaximumValidVersion + 1));
|
||||
CHECK(
|
||||
!s1.valid(std::numeric_limits<
|
||||
decltype(RPC::apiMaximumValidVersion.value)>::max()));
|
||||
|
||||
int result = 1;
|
||||
static_assert(
|
||||
RPC::apiMinimumSupportedVersion + 1 <= RPC::apiMaximumValidVersion);
|
||||
forApiVersions<
|
||||
RPC::apiMinimumSupportedVersion,
|
||||
RPC::apiMinimumSupportedVersion + 1>(
|
||||
std::as_const(s1).visit(),
|
||||
[](
|
||||
Json::Value const& json,
|
||||
unsigned int version,
|
||||
int* result) {
|
||||
CHECK(
|
||||
version >= RPC::apiMinimumSupportedVersion &&
|
||||
version <= RPC::apiMinimumSupportedVersion + 1);
|
||||
if (CHECK(json.isMember("value")))
|
||||
{
|
||||
*result *= json["value"].asInt();
|
||||
}
|
||||
},
|
||||
&result);
|
||||
CHECK(
|
||||
result ==
|
||||
primes[RPC::apiMinimumSupportedVersion] *
|
||||
primes[RPC::apiMinimumSupportedVersion + 1]);
|
||||
|
||||
// Check all the values with mutable data
|
||||
forAllApiVersions(
|
||||
s1.visit(), [&s1](Json::Value& json, auto version) {
|
||||
CHECK(s1.val[s1.index(version)] == json);
|
||||
if (CHECK(json.isMember("value")))
|
||||
{
|
||||
CHECK(json["value"].asInt() == primes[version]);
|
||||
}
|
||||
});
|
||||
|
||||
result = 1;
|
||||
forAllApiVersions(
|
||||
std::as_const(s1).visit(),
|
||||
[](
|
||||
Json::Value const& json,
|
||||
unsigned int version,
|
||||
int* result) {
|
||||
CHECK(
|
||||
version >= RPC::apiMinimumSupportedVersion &&
|
||||
version <= RPC::apiMaximumValidVersion);
|
||||
if (CHECK(json.isMember("value")))
|
||||
{
|
||||
*result *= json["value"].asInt();
|
||||
}
|
||||
},
|
||||
&result);
|
||||
|
||||
CHECK(result == productAllVersions);
|
||||
|
||||
// Several overloads we want to fail
|
||||
static_assert([](auto&& v) {
|
||||
return !requires {
|
||||
forAllApiVersions(
|
||||
std::forward<decltype(v)>(v).visit(), //
|
||||
[](Json::Value&, auto) {}); // missing const
|
||||
};
|
||||
}(std::as_const(s1)));
|
||||
static_assert([](auto&& v) {
|
||||
return !requires {
|
||||
forAllApiVersions(
|
||||
std::forward<decltype(v)>(v).visit(), //
|
||||
[](Json::Value&) {}); // missing const
|
||||
};
|
||||
}(std::as_const(s1)));
|
||||
static_assert([](auto&& v) {
|
||||
return !requires {
|
||||
forAllApiVersions(
|
||||
std::forward<decltype(v)>(v).visit(), //
|
||||
[]() {}); // missing parameters
|
||||
};
|
||||
}(std::as_const(s1)));
|
||||
static_assert([](auto&& v) {
|
||||
return !requires {
|
||||
forAllApiVersions(
|
||||
std::forward<decltype(v)>(v).visit(), //
|
||||
[](auto) {},
|
||||
1); // missing parameters
|
||||
};
|
||||
}(std::as_const(s1)));
|
||||
static_assert([](auto&& v) {
|
||||
return !requires {
|
||||
forAllApiVersions(
|
||||
std::forward<decltype(v)>(v).visit(), //
|
||||
[](auto, auto) {},
|
||||
1); // missing parameters
|
||||
};
|
||||
}(std::as_const(s1)));
|
||||
static_assert([](auto&& v) {
|
||||
return !requires {
|
||||
forAllApiVersions(
|
||||
std::forward<decltype(v)>(v).visit(), //
|
||||
[](auto, auto, char const*) {},
|
||||
1); // parameter type mismatch
|
||||
};
|
||||
}(std::as_const(s1)));
|
||||
|
||||
// Sanity checks
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
forAllApiVersions(
|
||||
std::forward<decltype(v)>(v).visit(), //
|
||||
[](auto) {});
|
||||
};
|
||||
}(s1));
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
forAllApiVersions(
|
||||
std::forward<decltype(v)>(v).visit(), //
|
||||
[](Json::Value const&) {});
|
||||
};
|
||||
}(std::as_const(s1)));
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
forAllApiVersions(
|
||||
std::forward<decltype(v)>(v).visit(), //
|
||||
[](auto...) {});
|
||||
};
|
||||
}(s1));
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
forAllApiVersions(
|
||||
std::forward<decltype(v)>(v).visit(), //
|
||||
[](Json::Value const&, auto...) {});
|
||||
};
|
||||
}(std::as_const(s1)));
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
forAllApiVersions(
|
||||
std::forward<decltype(v)>(v).visit(), //
|
||||
[](Json::Value&, auto, auto, auto...) {},
|
||||
0,
|
||||
"");
|
||||
};
|
||||
}(s1));
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
forAllApiVersions(
|
||||
std::forward<decltype(v)>(v).visit(), //
|
||||
[]<unsigned int Version>(
|
||||
Json::Value const&,
|
||||
std::integral_constant<unsigned int, Version>,
|
||||
int,
|
||||
char const*) {},
|
||||
0,
|
||||
"");
|
||||
};
|
||||
}(std::as_const(s1)));
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
forAllApiVersions(
|
||||
std::forward<decltype(v)>(v).visit(), //
|
||||
[](auto...) {});
|
||||
};
|
||||
}(std::move(s1)));
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
forAllApiVersions(
|
||||
std::forward<decltype(v)>(v).visit(), //
|
||||
[](auto...) {});
|
||||
};
|
||||
}(std::move(std::as_const(s1))));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(
|
||||
MultiApiJsonFixture,
|
||||
"default copy construction / assignment",
|
||||
"[MultiApiJson]")
|
||||
{
|
||||
using ripple::detail::MultiApiJson;
|
||||
|
||||
MultiApiJson13 subject{};
|
||||
subject.val[0] = obj1;
|
||||
subject.val[1] = obj2;
|
||||
|
||||
MultiApiJson13 x{subject};
|
||||
|
||||
CHECK(x.val.size() == subject.val.size());
|
||||
CHECK(x.val[0] == subject.val[0]);
|
||||
CHECK(x.val[1] == subject.val[1]);
|
||||
CHECK(x.val[2] == subject.val[2]);
|
||||
CHECK(x.val == subject.val);
|
||||
CHECK(&x.val[0] != &subject.val[0]);
|
||||
CHECK(&x.val[1] != &subject.val[1]);
|
||||
CHECK(&x.val[2] != &subject.val[2]);
|
||||
|
||||
MultiApiJson13 y;
|
||||
CHECK((y.val == std::array<Json::Value, 3>{}));
|
||||
y = subject;
|
||||
CHECK(y.val == subject.val);
|
||||
CHECK(&y.val[0] != &subject.val[0]);
|
||||
CHECK(&y.val[1] != &subject.val[1]);
|
||||
CHECK(&y.val[2] != &subject.val[2]);
|
||||
|
||||
y = std::move(x);
|
||||
CHECK(y.val == subject.val);
|
||||
CHECK(&y.val[0] != &subject.val[0]);
|
||||
CHECK(&y.val[1] != &subject.val[1]);
|
||||
CHECK(&y.val[2] != &subject.val[2]);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(MultiApiJsonFixture, "set", "[MultiApiJson]")
|
||||
{
|
||||
using ripple::detail::MultiApiJson;
|
||||
|
||||
auto x = MultiApiJson<1, 2>{Json::objectValue};
|
||||
x.set("name1", 42);
|
||||
CHECK(x.val[0].isMember("name1"));
|
||||
CHECK(x.val[1].isMember("name1"));
|
||||
CHECK(x.val[0]["name1"].isInt());
|
||||
CHECK(x.val[1]["name1"].isInt());
|
||||
CHECK(x.val[0]["name1"].asInt() == 42);
|
||||
CHECK(x.val[1]["name1"].asInt() == 42);
|
||||
|
||||
x.set("name2", "bar");
|
||||
CHECK(x.val[0].isMember("name2"));
|
||||
CHECK(x.val[1].isMember("name2"));
|
||||
CHECK(x.val[0]["name2"].isString());
|
||||
CHECK(x.val[1]["name2"].isString());
|
||||
CHECK(x.val[0]["name2"].asString() == "bar");
|
||||
CHECK(x.val[1]["name2"].asString() == "bar");
|
||||
|
||||
// Tests of requires clause - these are expected to match
|
||||
static_assert([](auto&& v) {
|
||||
return requires { v.set("name", Json::nullValue); };
|
||||
}(x));
|
||||
static_assert([](auto&& v) {
|
||||
return requires { v.set("name", "value"); };
|
||||
}(x));
|
||||
static_assert(
|
||||
[](auto&& v) { return requires { v.set("name", true); }; }(x));
|
||||
static_assert(
|
||||
[](auto&& v) { return requires { v.set("name", 42); }; }(x));
|
||||
|
||||
// Tests of requires clause - these are expected NOT to match
|
||||
struct foo_t final {};
|
||||
static_assert([](auto&& v) {
|
||||
return !requires { v.set("name", foo_t{}); };
|
||||
}(x));
|
||||
static_assert([](auto&& v) {
|
||||
return !requires { v.set("name", std::nullopt); };
|
||||
}(x));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(MultiApiJsonFixture, "isMember", "[MultiApiJson]")
|
||||
{
|
||||
using ripple::detail::MultiApiJson;
|
||||
|
||||
MultiApiJson13 subject{};
|
||||
subject.val[0] = obj1;
|
||||
subject.val[1] = obj2;
|
||||
|
||||
// Well defined behaviour even if we have different types of members
|
||||
CHECK(subject.isMember("foo") == decltype(subject)::none);
|
||||
|
||||
{
|
||||
// All variants have element "One", none have element "Two"
|
||||
MultiApiJson<1, 2> s1{};
|
||||
s1.val[0] = makeJson("One", 12);
|
||||
s1.val[1] = makeJson("One", 42);
|
||||
CHECK(s1.isMember("One") == decltype(s1)::all);
|
||||
CHECK(s1.isMember("Two") == decltype(s1)::none);
|
||||
}
|
||||
|
||||
{
|
||||
// Some variants have element "One" and some have "Two"
|
||||
MultiApiJson<1, 2> s2{};
|
||||
s2.val[0] = makeJson("One", 12);
|
||||
s2.val[1] = makeJson("Two", 42);
|
||||
CHECK(s2.isMember("One") == decltype(s2)::some);
|
||||
CHECK(s2.isMember("Two") == decltype(s2)::some);
|
||||
}
|
||||
|
||||
{
|
||||
// Not all variants have element "One", because last one is null
|
||||
MultiApiJson<1, 3> s3{};
|
||||
s3.val[0] = makeJson("One", 12);
|
||||
s3.val[1] = makeJson("One", 42);
|
||||
CHECK(s3.isMember("One") == decltype(s3)::some);
|
||||
CHECK(s3.isMember("Two") == decltype(s3)::none);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(MultiApiJsonFixture, "visitor", "[MultiApiJson]")
|
||||
{
|
||||
using ripple::detail::MultiApiJson;
|
||||
|
||||
MultiApiJson13 s1{};
|
||||
s1.val[0] = makeJson("value", 2);
|
||||
s1.val[1] = makeJson("value", 3);
|
||||
s1.val[2] = makeJson("value", 5);
|
||||
|
||||
CHECK(not s1.valid(0));
|
||||
CHECK(s1.index(0) == 0);
|
||||
|
||||
CHECK(s1.valid(1));
|
||||
CHECK(s1.index(1) == 0);
|
||||
|
||||
CHECK(not s1.valid(4));
|
||||
|
||||
// Test different overloads
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
v.visitor(
|
||||
v,
|
||||
std::integral_constant<unsigned, 1>{},
|
||||
[](Json::Value&, std::integral_constant<unsigned, 1>) {});
|
||||
};
|
||||
}(s1));
|
||||
CHECK(
|
||||
s1.visitor(
|
||||
s1,
|
||||
std::integral_constant<unsigned, 1>{},
|
||||
Overload{
|
||||
[](Json::Value& v,
|
||||
std::integral_constant<unsigned, 1>) {
|
||||
return v["value"].asInt();
|
||||
},
|
||||
[](Json::Value const&, auto) { return 0; },
|
||||
[](auto, auto) { return 0; }}) == 2);
|
||||
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
v.visitor(
|
||||
v,
|
||||
std::integral_constant<unsigned, 1>{},
|
||||
[](Json::Value&) {});
|
||||
};
|
||||
}(s1));
|
||||
CHECK(
|
||||
s1.visitor(
|
||||
s1,
|
||||
std::integral_constant<unsigned, 1>{},
|
||||
Overload{
|
||||
[](Json::Value& v) { return v["value"].asInt(); },
|
||||
[](Json::Value const&) { return 0; },
|
||||
[](auto...) { return 0; }}) == 2);
|
||||
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
v.visitor(
|
||||
v,
|
||||
std::integral_constant<unsigned, 1>{},
|
||||
[](Json::Value const&,
|
||||
std::integral_constant<unsigned, 1>) {});
|
||||
};
|
||||
}(std::as_const(s1)));
|
||||
CHECK(
|
||||
s1.visitor(
|
||||
std::as_const(s1),
|
||||
std::integral_constant<unsigned, 2>{},
|
||||
Overload{
|
||||
[](Json::Value const& v,
|
||||
std::integral_constant<unsigned, 2>) {
|
||||
return v["value"].asInt();
|
||||
},
|
||||
[](Json::Value&, auto) { return 0; },
|
||||
[](auto, auto) { return 0; }}) == 3);
|
||||
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
v.visitor(
|
||||
v,
|
||||
std::integral_constant<unsigned, 1>{},
|
||||
[](auto, auto, auto...) {});
|
||||
};
|
||||
}(s1));
|
||||
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
v.visitor(v, 1, [](auto, auto, auto...) {});
|
||||
};
|
||||
}(s1));
|
||||
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
v.visitor(v, 1, [](auto, auto, auto...) {}, "");
|
||||
};
|
||||
}(s1));
|
||||
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
v.visitor(v, 1, [](auto, auto, auto, auto...) {}, "");
|
||||
};
|
||||
}(s1));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(MultiApiJsonFixture, "visit", "[MultiApiJson]")
|
||||
{
|
||||
using ripple::detail::MultiApiJson;
|
||||
|
||||
MultiApiJson13 s1{};
|
||||
s1.val[0] = makeJson("value", 2);
|
||||
s1.val[1] = makeJson("value", 3);
|
||||
s1.val[2] = makeJson("value", 5);
|
||||
|
||||
// Test different overloads
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
v.visit(
|
||||
std::integral_constant<unsigned, 1>{},
|
||||
[](Json::Value&, std::integral_constant<unsigned, 1>) {});
|
||||
};
|
||||
}(s1));
|
||||
CHECK(
|
||||
s1.visit(
|
||||
std::integral_constant<unsigned, 1>{},
|
||||
Overload{
|
||||
[](Json::Value& v,
|
||||
std::integral_constant<unsigned, 1>) {
|
||||
return v["value"].asInt();
|
||||
},
|
||||
[](Json::Value const&, auto) { return 0; },
|
||||
[](auto, auto) { return 0; }}) == 2);
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
v.visit()(
|
||||
std::integral_constant<unsigned, 1>{},
|
||||
[](Json::Value&, std::integral_constant<unsigned, 1>) {});
|
||||
};
|
||||
}(s1));
|
||||
CHECK(
|
||||
s1.visit()(std::integral_constant<unsigned, 1>{},
|
||||
Overload{[](Json::Value& v,
|
||||
std::integral_constant<unsigned, 1>) {
|
||||
return v["value"].asInt();
|
||||
},
|
||||
[](Json::Value const&, auto) { return 0; },
|
||||
[](auto, auto) { return 0; }}) == 2);
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
v.visit(
|
||||
std::integral_constant<unsigned, 1>{},
|
||||
[](Json::Value&) {});
|
||||
};
|
||||
}(s1));
|
||||
CHECK(
|
||||
s1.visit(
|
||||
std::integral_constant<unsigned, 1>{},
|
||||
Overload{
|
||||
[](Json::Value& v) { return v["value"].asInt(); },
|
||||
[](Json::Value const&) { return 0; },
|
||||
[](auto...) { return 0; }}) == 2);
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
v.visit()(std::integral_constant<unsigned, 1>{}, [](Json::Value&) {});
|
||||
};
|
||||
}(s1));
|
||||
CHECK(
|
||||
s1.visit()(std::integral_constant<unsigned, 1>{},
|
||||
Overload{[](Json::Value& v) { return v["value"].asInt(); },
|
||||
[](Json::Value const&) { return 0; },
|
||||
[](auto...) { return 0; }}) == 2);
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
v.visit(
|
||||
std::integral_constant<unsigned, 1>{},
|
||||
[](Json::Value const&,
|
||||
std::integral_constant<unsigned, 1>) {});
|
||||
};
|
||||
}(std::as_const(s1)));
|
||||
CHECK(
|
||||
std::as_const(s1).visit(
|
||||
std::integral_constant<unsigned, 2>{},
|
||||
Overload{
|
||||
[](Json::Value const& v,
|
||||
std::integral_constant<unsigned, 2>) {
|
||||
return v["value"].asInt();
|
||||
},
|
||||
[](Json::Value&, auto) { return 0; },
|
||||
[](auto, auto) { return 0; }}) == 3);
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
v.visit()(std::integral_constant<unsigned, 1>{},
|
||||
[](Json::Value const&,
|
||||
std::integral_constant<unsigned, 1>) {});
|
||||
};
|
||||
}(std::as_const(s1)));
|
||||
CHECK(
|
||||
std::as_const(s1).visit()(std::integral_constant<unsigned, 2>{},
|
||||
Overload{[](Json::Value const& v,
|
||||
std::integral_constant<unsigned,
|
||||
2>) {
|
||||
return v["value"].asInt();
|
||||
},
|
||||
[](Json::Value&, auto) { return 0; },
|
||||
[](auto, auto) { return 0; }}) == 3);
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
v.visit(
|
||||
std::integral_constant<unsigned, 1>{}, [](Json::Value const&) {});
|
||||
};
|
||||
}(std::as_const(s1)));
|
||||
CHECK(
|
||||
std::as_const(s1).visit(
|
||||
std::integral_constant<unsigned, 2>{},
|
||||
Overload{
|
||||
[](Json::Value const& v) { return v["value"].asInt(); },
|
||||
[](Json::Value&) { return 0; },
|
||||
[](auto...) { return 0; }}) == 3);
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
v.visit()(std::integral_constant<unsigned, 1>{},
|
||||
[](Json::Value const&) {});
|
||||
};
|
||||
}(std::as_const(s1)));
|
||||
CHECK(
|
||||
std::as_const(s1).visit()(std::integral_constant<unsigned, 2>{},
|
||||
Overload{[](Json::Value const& v) {
|
||||
return v["value"].asInt();
|
||||
},
|
||||
[](Json::Value&) { return 0; },
|
||||
[](auto...) { return 0; }}) == 3);
|
||||
|
||||
static_assert([](auto&& v) {
|
||||
return requires { v.visit(v, 1, [](auto, auto, auto...) {}); };
|
||||
}(s1));
|
||||
|
||||
static_assert([](auto&& v) {
|
||||
return requires { v.visit(v, 1, [](auto, auto, auto...) {}, ""); };
|
||||
}(s1));
|
||||
|
||||
static_assert([](auto&& v) {
|
||||
return requires {
|
||||
v.visit(v, 1, [](auto, auto, auto, auto...) {}, "");
|
||||
};
|
||||
}(s1));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
@@ -1,507 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/SecretKey.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
namespace {
|
||||
|
||||
using blob = std::vector<std::uint8_t>;
|
||||
|
||||
template <class FwdIter, class Container>
|
||||
void
|
||||
hex_to_binary(FwdIter first, FwdIter last, Container& out)
|
||||
{
|
||||
struct Table
|
||||
{
|
||||
int val[256];
|
||||
Table()
|
||||
{
|
||||
std::fill(val, val + 256, 0);
|
||||
for (int i = 0; i < 10; ++i)
|
||||
val['0' + i] = i;
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{
|
||||
val['A' + i] = 10 + i;
|
||||
val['a' + i] = 10 + i;
|
||||
}
|
||||
}
|
||||
int
|
||||
operator[](int i)
|
||||
{
|
||||
return val[i];
|
||||
}
|
||||
};
|
||||
|
||||
static Table lut;
|
||||
out.reserve(std::distance(first, last) / 2);
|
||||
while (first != last)
|
||||
{
|
||||
auto const hi(lut[(*first++)]);
|
||||
auto const lo(lut[(*first++)]);
|
||||
out.push_back((hi * 16) + lo);
|
||||
}
|
||||
}
|
||||
|
||||
blob
|
||||
sig(std::string const& hex)
|
||||
{
|
||||
blob b;
|
||||
hex_to_binary(hex.begin(), hex.end(), b);
|
||||
return b;
|
||||
}
|
||||
|
||||
bool
|
||||
check(std::optional<ECDSACanonicality> answer, std::string const& s)
|
||||
{
|
||||
return ecdsaCanonicality(makeSlice(sig(s))) == answer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_SUITE_BEGIN("PublicKey");
|
||||
|
||||
TEST_CASE("Base58")
|
||||
{
|
||||
INFO("Base58: secp256k1");
|
||||
|
||||
{
|
||||
auto const pk1 = derivePublicKey(
|
||||
KeyType::secp256k1,
|
||||
generateSecretKey(
|
||||
KeyType::secp256k1, generateSeed("masterpassphrase")));
|
||||
|
||||
auto const pk2 = parseBase58<PublicKey>(
|
||||
TokenType::NodePublic,
|
||||
"n94a1u4jAz288pZLtw6yFWVbi89YamiC6JBXPVUj5zmExe5fTVg9");
|
||||
CHECK(pk2);
|
||||
|
||||
CHECK(pk1 == *pk2);
|
||||
}
|
||||
|
||||
// Try converting short, long and malformed data for secp256k1 keys
|
||||
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, ""));
|
||||
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, " "));
|
||||
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, "!ty89234gh45"));
|
||||
|
||||
auto good = toBase58(
|
||||
TokenType::NodePublic, derivePublicKey(KeyType::secp256k1, randomSecretKey()));
|
||||
|
||||
{
|
||||
auto s = good;
|
||||
std::hash<std::string> r;
|
||||
while (!s.empty())
|
||||
{
|
||||
s.erase(r(s) % s.size(), 1);
|
||||
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t i = 1; i != 16; i++)
|
||||
{
|
||||
auto s = good;
|
||||
s.resize(s.size() + i, s[i % s.size()]);
|
||||
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
|
||||
}
|
||||
|
||||
for (auto c : std::string("0IOl"))
|
||||
{
|
||||
for (std::size_t i = 0; i != good.size(); ++i)
|
||||
{
|
||||
auto s = good;
|
||||
s[i % s.size()] = c;
|
||||
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto s = good;
|
||||
for (auto c : std::string("apsrJqtv7"))
|
||||
{
|
||||
s[0] = c;
|
||||
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<PublicKey> keys;
|
||||
keys.reserve(32);
|
||||
for (std::size_t i = 0; i != keys.capacity(); ++i)
|
||||
keys.emplace_back(derivePublicKey(KeyType::secp256k1, randomSecretKey()));
|
||||
CHECK(keys.size() == 32);
|
||||
for (std::size_t i = 0; i != keys.size(); ++i)
|
||||
{
|
||||
auto const si = toBase58(TokenType::NodePublic, keys[i]);
|
||||
CHECK(!si.empty());
|
||||
auto const ski = parseBase58<PublicKey>(TokenType::NodePublic, si);
|
||||
CHECK(ski);
|
||||
CHECK(keys[i] == *ski);
|
||||
for (std::size_t j = i; j != keys.size(); ++j)
|
||||
{
|
||||
CHECK((keys[i] == keys[j]) == (i == j));
|
||||
auto const sj = toBase58(TokenType::NodePublic, keys[j]);
|
||||
CHECK((si == sj) == (i == j));
|
||||
auto const skj = parseBase58<PublicKey>(TokenType::NodePublic, sj);
|
||||
CHECK(skj);
|
||||
CHECK(keys[j] == *skj);
|
||||
CHECK((*ski == *skj) == (i == j));
|
||||
}
|
||||
}
|
||||
|
||||
INFO("Base58: ed25519");
|
||||
|
||||
{
|
||||
auto const pk1 = derivePublicKey(
|
||||
KeyType::ed25519,
|
||||
generateSecretKey(
|
||||
KeyType::ed25519, generateSeed("masterpassphrase")));
|
||||
|
||||
auto const pk2 = parseBase58<PublicKey>(
|
||||
TokenType::NodePublic,
|
||||
"nHUeeJCSY2dM71oxM8Cgjouf5ekTuev2mwDpc374aLMxzDLXNmjf");
|
||||
CHECK(pk2);
|
||||
|
||||
CHECK(pk1 == *pk2);
|
||||
}
|
||||
|
||||
// Repeat Base58 conversion tests for ed25519 keys
|
||||
good = toBase58(
|
||||
TokenType::NodePublic, derivePublicKey(KeyType::ed25519, randomSecretKey()));
|
||||
|
||||
{
|
||||
auto s = good;
|
||||
std::hash<std::string> r;
|
||||
while (!s.empty())
|
||||
{
|
||||
s.erase(r(s) % s.size(), 1);
|
||||
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t i = 1; i != 16; i++)
|
||||
{
|
||||
auto s = good;
|
||||
s.resize(s.size() + i, s[i % s.size()]);
|
||||
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
|
||||
}
|
||||
|
||||
for (auto c : std::string("0IOl"))
|
||||
{
|
||||
for (std::size_t i = 0; i != good.size(); ++i)
|
||||
{
|
||||
auto s = good;
|
||||
s[i % s.size()] = c;
|
||||
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto s = good;
|
||||
for (auto c : std::string("apsrJqtv7"))
|
||||
{
|
||||
s[0] = c;
|
||||
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
|
||||
}
|
||||
}
|
||||
|
||||
keys.clear();
|
||||
for (std::size_t i = 0; i != 32; ++i)
|
||||
keys.emplace_back(derivePublicKey(KeyType::ed25519, randomSecretKey()));
|
||||
CHECK(keys.size() == 32);
|
||||
for (std::size_t i = 0; i != keys.size(); ++i)
|
||||
{
|
||||
auto const si = toBase58(TokenType::NodePublic, keys[i]);
|
||||
CHECK(!si.empty());
|
||||
auto const ski = parseBase58<PublicKey>(TokenType::NodePublic, si);
|
||||
CHECK(ski);
|
||||
CHECK(keys[i] == *ski);
|
||||
for (std::size_t j = i; j != keys.size(); ++j)
|
||||
{
|
||||
CHECK((keys[i] == keys[j]) == (i == j));
|
||||
auto const sj = toBase58(TokenType::NodePublic, keys[j]);
|
||||
CHECK((si == sj) == (i == j));
|
||||
auto const skj = parseBase58<PublicKey>(TokenType::NodePublic, sj);
|
||||
CHECK(skj);
|
||||
CHECK(keys[j] == *skj);
|
||||
CHECK((*ski == *skj) == (i == j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Canonical")
|
||||
{
|
||||
INFO("Canonical");
|
||||
|
||||
// Fully canonical
|
||||
CHECK(check(
|
||||
ECDSACanonicality::fullyCanonical,
|
||||
"3045"
|
||||
"022100FF478110D1D4294471EC76E0157540C2181F47DEBD25D7F9E7DDCCCD47EE"
|
||||
"E905"
|
||||
"0220078F07CDAE6C240855D084AD91D1479609533C147C93B0AEF19BC9724D003F"
|
||||
"28"));
|
||||
CHECK(check(
|
||||
ECDSACanonicality::fullyCanonical,
|
||||
"3045"
|
||||
"0221009218248292F1762D8A51BE80F8A7F2CD288D810CE781D5955700DA1684DF"
|
||||
"1D2D"
|
||||
"022041A1EE1746BFD72C9760CC93A7AAA8047D52C8833A03A20EAAE92EA19717B4"
|
||||
"54"));
|
||||
CHECK(check(
|
||||
ECDSACanonicality::fullyCanonical,
|
||||
"3044"
|
||||
"02206A9E43775F73B6D1EC420E4DDD222A80D4C6DF5D1BEECC431A91B63C928B75"
|
||||
"81"
|
||||
"022023E9CC2D61DDA6F73EAA6BCB12688BEB0F434769276B3127E4044ED895C9D9"
|
||||
"6B"));
|
||||
CHECK(check(
|
||||
ECDSACanonicality::fullyCanonical,
|
||||
"3044"
|
||||
"022056E720007221F3CD4EFBB6352741D8E5A0968D48D8D032C2FBC4F6304AD1D0"
|
||||
"4E"
|
||||
"02201F39EB392C20D7801C3E8D81D487E742FA84A1665E923225BD6323847C7187"
|
||||
"9F"));
|
||||
CHECK(check(
|
||||
ECDSACanonicality::fullyCanonical,
|
||||
"3045"
|
||||
"022100FDFD5AD05518CEA0017A2DCB5C4DF61E7C73B6D3A38E7AE93210A1564E8C"
|
||||
"2F12"
|
||||
"0220214FF061CCC123C81D0BB9D0EDEA04CD40D96BF1425D311DA62A7096BB18EA"
|
||||
"18"));
|
||||
|
||||
// Canonical but not fully canonical
|
||||
CHECK(check(
|
||||
ECDSACanonicality::canonical,
|
||||
"3046"
|
||||
"022100F477B3FA6F31C7CB3A0D1AD94A231FDD24B8D78862EE334CEA7CD08F6CBC"
|
||||
"0A1B"
|
||||
"022100928E6BCF1ED2684679730C5414AEC48FD62282B090041C41453C1D064AF5"
|
||||
"97A1"));
|
||||
CHECK(check(
|
||||
ECDSACanonicality::canonical,
|
||||
"3045"
|
||||
"022063E7C7CA93CB2400E413A342C027D00665F8BAB9C22EF0A7B8AE3AAF092230"
|
||||
"B6"
|
||||
"0221008F2E8BB7D09521ABBC277717B14B93170AE6465C5A1B36561099319C4BEB"
|
||||
"254C"));
|
||||
CHECK(check(
|
||||
ECDSACanonicality::canonical,
|
||||
"3046"
|
||||
"02210099DCA1188663DDEA506A06A7B20C2B7D8C26AFF41DECE69D6C5F7C967D32"
|
||||
"625F"
|
||||
"022100897658A6B1F9EEE5D140D7A332DA0BD73BB98974EA53F6201B01C1B594F2"
|
||||
"86EA"));
|
||||
CHECK(check(
|
||||
ECDSACanonicality::canonical,
|
||||
"3045"
|
||||
"02200855DE366E4E323AA2CE2A25674401A7D11F72EC432770D07F7B57DF7387AE"
|
||||
"C0"
|
||||
"022100DA4C6ADDEA14888858DE2AC5B91ED9050D6972BB388DEF582628CEE32869"
|
||||
"AE35"));
|
||||
|
||||
// valid
|
||||
CHECK(check(
|
||||
ECDSACanonicality::fullyCanonical,
|
||||
"3006"
|
||||
"020101"
|
||||
"020102"));
|
||||
CHECK(check(
|
||||
ECDSACanonicality::fullyCanonical,
|
||||
"3044"
|
||||
"02203932c892e2e550f3af8ee4ce9c215a87f9bb831dcac87b2838e2c2eaa891df"
|
||||
"0c"
|
||||
"022030b61dd36543125d56b9f9f3a1f53189e5af33cdda8d77a5209aec03978fa0"
|
||||
"01"));
|
||||
CHECK(check(
|
||||
ECDSACanonicality::canonical,
|
||||
"3045"
|
||||
"0220076045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40f9"
|
||||
"0a"
|
||||
"0221008fffd599910eefe00bc803c688eca1d2ba7f6b180620eaa03488e6585db6"
|
||||
"ba01"));
|
||||
CHECK(check(
|
||||
ECDSACanonicality::canonical,
|
||||
"3046"
|
||||
"022100876045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40"
|
||||
"f90a"
|
||||
"0221008fffd599910eefe00bc803c688c2eca1d2ba7f6b180620eaa03488e6585d"
|
||||
"b6ba"));
|
||||
|
||||
CHECK(check(
|
||||
std::nullopt,
|
||||
"3005"
|
||||
"0201FF"
|
||||
"0200"));
|
||||
CHECK(check(
|
||||
std::nullopt,
|
||||
"3006"
|
||||
"020101"
|
||||
"020202"));
|
||||
CHECK(check(
|
||||
std::nullopt,
|
||||
"3006"
|
||||
"020701"
|
||||
"020102"));
|
||||
CHECK(check(
|
||||
std::nullopt,
|
||||
"3006"
|
||||
"020401"
|
||||
"020102"));
|
||||
CHECK(check(
|
||||
std::nullopt,
|
||||
"3006"
|
||||
"020501"
|
||||
"020102"));
|
||||
CHECK(check(
|
||||
std::nullopt,
|
||||
"3006"
|
||||
"020201"
|
||||
"020102"));
|
||||
CHECK(check(
|
||||
std::nullopt,
|
||||
"3006"
|
||||
"020301"
|
||||
"020202"));
|
||||
CHECK(check(
|
||||
std::nullopt,
|
||||
"3006"
|
||||
"020401"
|
||||
"020202"));
|
||||
CHECK(check(
|
||||
std::nullopt,
|
||||
"3047"
|
||||
"0221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba"
|
||||
"6105"
|
||||
"022200002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e56"
|
||||
"6695ed"));
|
||||
CHECK(check(
|
||||
std::nullopt,
|
||||
"3144"
|
||||
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
|
||||
"05"
|
||||
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
|
||||
"ed"));
|
||||
CHECK(check(
|
||||
std::nullopt,
|
||||
"3045"
|
||||
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
|
||||
"05"
|
||||
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
|
||||
"ed"));
|
||||
CHECK(check(
|
||||
std::nullopt,
|
||||
"301F"
|
||||
"01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1"));
|
||||
CHECK(check(
|
||||
std::nullopt,
|
||||
"3045"
|
||||
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
|
||||
"05"
|
||||
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
|
||||
"ed00"));
|
||||
CHECK(check(
|
||||
std::nullopt,
|
||||
"3044"
|
||||
"01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
|
||||
"05"
|
||||
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
|
||||
"ed"));
|
||||
CHECK(check(
|
||||
std::nullopt,
|
||||
"3024"
|
||||
"0200"
|
||||
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
|
||||
"ed"));
|
||||
CHECK(check(
|
||||
std::nullopt,
|
||||
"3044"
|
||||
"02208990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
|
||||
"05"
|
||||
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
|
||||
"ed"));
|
||||
CHECK(check(
|
||||
std::nullopt,
|
||||
"3045"
|
||||
"0221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba"
|
||||
"6105"
|
||||
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
|
||||
"ed"));
|
||||
CHECK(check(
|
||||
std::nullopt,
|
||||
"3044"
|
||||
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
|
||||
"05012"
|
||||
"02d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695e"
|
||||
"d"));
|
||||
CHECK(check(
|
||||
std::nullopt,
|
||||
"3024"
|
||||
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
|
||||
"05"
|
||||
"0200"));
|
||||
CHECK(check(
|
||||
std::nullopt,
|
||||
"3044"
|
||||
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
|
||||
"05"
|
||||
"0220fd5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
|
||||
"ed"));
|
||||
CHECK(check(
|
||||
std::nullopt,
|
||||
"3045"
|
||||
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
|
||||
"05"
|
||||
"0221002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e5666"
|
||||
"95ed"));
|
||||
}
|
||||
|
||||
TEST_CASE("Miscellaneous operations")
|
||||
{
|
||||
INFO("Miscellaneous operations");
|
||||
|
||||
auto const pk1 = derivePublicKey(
|
||||
KeyType::secp256k1,
|
||||
generateSecretKey(
|
||||
KeyType::secp256k1, generateSeed("masterpassphrase")));
|
||||
|
||||
PublicKey pk2(pk1);
|
||||
CHECK(pk1 == pk2);
|
||||
CHECK(pk2 == pk1);
|
||||
|
||||
PublicKey pk3 = derivePublicKey(
|
||||
KeyType::secp256k1,
|
||||
generateSecretKey(
|
||||
KeyType::secp256k1, generateSeed("arbitraryPassPhrase")));
|
||||
pk3 = pk2;
|
||||
CHECK(pk3 == pk2);
|
||||
CHECK(pk1 == pk3);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include <doctest/doctest.h>
|
||||
@@ -63,8 +63,8 @@ LedgerHistory::insert(
|
||||
ledger->stateMap().getHash().isNonZero(),
|
||||
"ripple::LedgerHistory::insert : nonzero hash");
|
||||
|
||||
std::unique_lock sl(m_ledgers_by_hash.peekMutex());
|
||||
|
||||
// TODOL merge the below into a single call to avoid lock and race
|
||||
// conditions, i.e. - return alreadyHad on assignment somehow.
|
||||
bool const alreadyHad = m_ledgers_by_hash.canonicalize_replace_cache(
|
||||
ledger->info().hash, ledger);
|
||||
if (validated)
|
||||
@@ -76,7 +76,7 @@ LedgerHistory::insert(
|
||||
LedgerHash
|
||||
LedgerHistory::getLedgerHash(LedgerIndex index)
|
||||
{
|
||||
std::unique_lock sl(m_ledgers_by_hash.peekMutex());
|
||||
// TODO: is it safe to get iterator without lock here?
|
||||
if (auto it = mLedgersByIndex.find(index); it != mLedgersByIndex.end())
|
||||
return it->second;
|
||||
return {};
|
||||
@@ -86,13 +86,12 @@ std::shared_ptr<Ledger const>
|
||||
LedgerHistory::getLedgerBySeq(LedgerIndex index)
|
||||
{
|
||||
{
|
||||
std::unique_lock sl(m_ledgers_by_hash.peekMutex());
|
||||
// TODO: this lock is not needed
|
||||
auto it = mLedgersByIndex.find(index);
|
||||
|
||||
if (it != mLedgersByIndex.end())
|
||||
{
|
||||
uint256 hash = it->second;
|
||||
sl.unlock();
|
||||
return getLedgerByHash(hash);
|
||||
}
|
||||
}
|
||||
@@ -108,7 +107,8 @@ LedgerHistory::getLedgerBySeq(LedgerIndex index)
|
||||
|
||||
{
|
||||
// Add this ledger to the local tracking by index
|
||||
std::unique_lock sl(m_ledgers_by_hash.peekMutex());
|
||||
// std::unique_lock sl(m_ledgers_by_hash.peekMutex());
|
||||
// TODO: make sure that canonicalize_replace_client lock the partition
|
||||
|
||||
XRPL_ASSERT(
|
||||
ret->isImmutable(),
|
||||
@@ -458,7 +458,8 @@ LedgerHistory::builtLedger(
|
||||
XRPL_ASSERT(
|
||||
!hash.isZero(), "ripple::LedgerHistory::builtLedger : nonzero hash");
|
||||
|
||||
std::unique_lock sl(m_consensus_validated.peekMutex());
|
||||
// std::unique_lock sl(m_consensus_validated.peekMutex());
|
||||
// TODO: make sure that canonicalize_replace_client lock the partition
|
||||
|
||||
auto entry = std::make_shared<cv_entry>();
|
||||
m_consensus_validated.canonicalize_replace_client(index, entry);
|
||||
@@ -500,7 +501,8 @@ LedgerHistory::validatedLedger(
|
||||
!hash.isZero(),
|
||||
"ripple::LedgerHistory::validatedLedger : nonzero hash");
|
||||
|
||||
std::unique_lock sl(m_consensus_validated.peekMutex());
|
||||
// std::unique_lock sl(m_consensus_validated.peekMutex());
|
||||
// TODO: make sure that canonicalize_replace_client lock the partition
|
||||
|
||||
auto entry = std::make_shared<cv_entry>();
|
||||
m_consensus_validated.canonicalize_replace_client(index, entry);
|
||||
@@ -535,7 +537,9 @@ LedgerHistory::validatedLedger(
|
||||
bool
|
||||
LedgerHistory::fixIndex(LedgerIndex ledgerIndex, LedgerHash const& ledgerHash)
|
||||
{
|
||||
std::unique_lock sl(m_ledgers_by_hash.peekMutex());
|
||||
// std::unique_lock sl(m_ledgers_by_hash.peekMutex());
|
||||
// TODO: how to ensure that? "Ensure m_ledgers_by_hash doesn't have the
|
||||
// wrong hash for a particular index"
|
||||
auto it = mLedgersByIndex.find(ledgerIndex);
|
||||
|
||||
if ((it != mLedgersByIndex.end()) && (it->second != ledgerHash))
|
||||
|
||||
Reference in New Issue
Block a user