Compare commits

..

8 Commits

Author SHA1 Message Date
Valentin Balaschenko
ab0a72ad93 Merge branch 'vlntb/taggedcache-lock-per-partition' into vlntb/lock-contention-analysis 2025-06-27 13:13:51 +01:00
Valentin Balaschenko
83ae5e3050 Merge branch 'develop' into vlntb/lock-contention-analysis 2025-06-27 11:35:07 +01:00
Valentin Balaschenko
56f5189a2b lock cont annotations 2025-06-20 15:22:40 +01:00
Valentin Balaschenko
da694c8304 wip lock per partition 2025-06-13 18:33:00 +01:00
Valentin Balaschenko
d0f836581b remove mutex and dead references 2025-06-13 14:45:27 +01:00
Valentin Balaschenko
984c70955a clang 2025-06-11 16:33:06 +01:00
Valentin Balaschenko
3effb54e49 wip atomics for counters 2025-06-11 16:31:48 +01:00
Valentin Balaschenko
316f9535e3 wip removing mutex and dependencies 2025-06-11 14:31:26 +01:00
23 changed files with 3248 additions and 2611 deletions

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -22,7 +22,6 @@
#include <xrpl/basics/ByteUtilities.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/partitioned_unordered_map.h>
#include <cstdint>

View 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

View 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

View 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

View File

@@ -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

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

View File

@@ -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)

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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

View File

@@ -1,2 +0,0 @@
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include <doctest/doctest.h>

View File

@@ -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))