mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-19 10:35:50 +00:00
Compare commits
13 Commits
hotfix2.5.
...
vlntb/lock
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab0a72ad93 | ||
|
|
83ae5e3050 | ||
|
|
e18f27f5f7 | ||
|
|
df6daf0d8f | ||
|
|
e9d46f0bfc | ||
|
|
42fd74b77b | ||
|
|
c55ea56c5e | ||
|
|
56f5189a2b | ||
|
|
da694c8304 | ||
|
|
d0f836581b | ||
|
|
984c70955a | ||
|
|
3effb54e49 | ||
|
|
316f9535e3 |
5
.github/workflows/macos.yml
vendored
5
.github/workflows/macos.yml
vendored
@@ -96,4 +96,7 @@ jobs:
|
||||
run: |
|
||||
n=$(nproc)
|
||||
echo "Using $n test jobs"
|
||||
${build_dir}/rippled --unittest --unittest-jobs $n
|
||||
|
||||
cd ${build_dir}
|
||||
./rippled --unittest --unittest-jobs $n
|
||||
ctest -j $n --output-on-failure
|
||||
|
||||
10
.github/workflows/nix.yml
vendored
10
.github/workflows/nix.yml
vendored
@@ -163,7 +163,9 @@ jobs:
|
||||
cmake-args: "-Dassert=TRUE -Dwerr=TRUE ${{ matrix.cmake-args }}"
|
||||
- name: test
|
||||
run: |
|
||||
${build_dir}/rippled --unittest --unittest-jobs $(nproc)
|
||||
cd ${build_dir}
|
||||
./rippled --unittest --unittest-jobs $(nproc)
|
||||
ctest -j $(nproc) --output-on-failure
|
||||
|
||||
reference-fee-test:
|
||||
strategy:
|
||||
@@ -217,8 +219,9 @@ jobs:
|
||||
cmake-args: "-Dassert=TRUE -Dwerr=TRUE ${{ matrix.cmake-args }}"
|
||||
- name: test
|
||||
run: |
|
||||
${build_dir}/rippled --unittest --unittest-jobs $(nproc)
|
||||
|
||||
cd ${build_dir}
|
||||
./rippled --unittest --unittest-jobs $(nproc)
|
||||
ctest -j $(nproc) --output-on-failure
|
||||
coverage:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -441,3 +444,4 @@ jobs:
|
||||
run: |
|
||||
cd ${BUILD_DIR}
|
||||
./rippled -u --unittest-jobs $(( $(nproc)/4 ))
|
||||
ctest -j $(nproc) --output-on-failure
|
||||
|
||||
5
.github/workflows/windows.yml
vendored
5
.github/workflows/windows.yml
vendored
@@ -95,5 +95,6 @@ jobs:
|
||||
shell: bash
|
||||
if: ${{ matrix.configuration.tests }}
|
||||
run: |
|
||||
${build_dir}/${{ matrix.configuration.type }}/rippled --unittest \
|
||||
--unittest-jobs $(nproc)
|
||||
cd ${build_dir}/${{ matrix.configuration.type }}
|
||||
./rippled --unittest --unittest-jobs $(nproc)
|
||||
ctest -j $(nproc) --output-on-failure
|
||||
|
||||
@@ -132,6 +132,7 @@ test.shamap > xrpl.protocol
|
||||
test.toplevel > test.csf
|
||||
test.toplevel > xrpl.json
|
||||
test.unit_test > xrpl.basics
|
||||
tests.libxrpl > xrpl.basics
|
||||
xrpl.json > xrpl.basics
|
||||
xrpl.protocol > xrpl.basics
|
||||
xrpl.protocol > xrpl.json
|
||||
|
||||
@@ -90,6 +90,11 @@ set_target_properties(OpenSSL::SSL PROPERTIES
|
||||
INTERFACE_COMPILE_DEFINITIONS OPENSSL_NO_SSL2
|
||||
)
|
||||
set(SECP256K1_INSTALL TRUE)
|
||||
set(SECP256K1_BUILD_BENCHMARK FALSE)
|
||||
set(SECP256K1_BUILD_TESTS FALSE)
|
||||
set(SECP256K1_BUILD_EXHAUSTIVE_TESTS FALSE)
|
||||
set(SECP256K1_BUILD_CTIME_TESTS FALSE)
|
||||
set(SECP256K1_BUILD_EXAMPLES FALSE)
|
||||
add_subdirectory(external/secp256k1)
|
||||
add_library(secp256k1::secp256k1 ALIAS secp256k1)
|
||||
add_subdirectory(external/ed25519-donna)
|
||||
@@ -144,3 +149,8 @@ set(PROJECT_EXPORT_SET RippleExports)
|
||||
include(RippledCore)
|
||||
include(RippledInstall)
|
||||
include(RippledValidatorKeys)
|
||||
|
||||
if(tests)
|
||||
include(CTest)
|
||||
add_subdirectory(src/tests/libxrpl)
|
||||
endif()
|
||||
|
||||
4817
RELEASENOTES.md
4817
RELEASENOTES.md
File diff suppressed because it is too large
Load Diff
41
cmake/xrpl_add_test.cmake
Normal file
41
cmake/xrpl_add_test.cmake
Normal file
@@ -0,0 +1,41 @@
|
||||
include(isolate_headers)
|
||||
|
||||
function(xrpl_add_test name)
|
||||
set(target ${PROJECT_NAME}.test.${name})
|
||||
|
||||
file(GLOB_RECURSE sources CONFIGURE_DEPENDS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/${name}/*.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/${name}.cpp"
|
||||
)
|
||||
add_executable(${target} EXCLUDE_FROM_ALL ${ARGN} ${sources})
|
||||
|
||||
isolate_headers(
|
||||
${target}
|
||||
"${CMAKE_SOURCE_DIR}"
|
||||
"${CMAKE_SOURCE_DIR}/tests/${name}"
|
||||
PRIVATE
|
||||
)
|
||||
|
||||
# Make sure the test isn't optimized away in unity builds
|
||||
set_target_properties(${target} PROPERTIES
|
||||
UNITY_BUILD_MODE GROUP
|
||||
UNITY_BUILD_BATCH_SIZE 0) # Adjust as needed
|
||||
|
||||
add_test(NAME ${target} COMMAND ${target})
|
||||
set_tests_properties(
|
||||
${target} PROPERTIES
|
||||
FIXTURES_REQUIRED ${target}_fixture
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME ${target}.build
|
||||
COMMAND
|
||||
${CMAKE_COMMAND}
|
||||
--build ${CMAKE_BINARY_DIR}
|
||||
--config $<CONFIG>
|
||||
--target ${target}
|
||||
)
|
||||
set_tests_properties(${target}.build PROPERTIES
|
||||
FIXTURES_SETUP ${target}_fixture
|
||||
)
|
||||
endfunction()
|
||||
@@ -24,6 +24,7 @@ class Xrpl(ConanFile):
|
||||
}
|
||||
|
||||
requires = [
|
||||
'doctest/2.4.11',
|
||||
'grpc/1.50.1',
|
||||
'libarchive/3.7.6',
|
||||
'nudb/2.0.8',
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#define RIPPLE_BASICS_SHAMAP_HASH_H_INCLUDED
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/partitioned_unordered_map.h>
|
||||
|
||||
#include <ostream>
|
||||
|
||||
|
||||
@@ -170,9 +170,6 @@ public:
|
||||
bool
|
||||
retrieve(key_type const& key, T& data);
|
||||
|
||||
mutex_type&
|
||||
peekMutex();
|
||||
|
||||
std::vector<key_type>
|
||||
getKeys() const;
|
||||
|
||||
@@ -193,11 +190,14 @@ public:
|
||||
|
||||
private:
|
||||
SharedPointerType
|
||||
initialFetch(key_type const& key, std::lock_guard<mutex_type> const& l);
|
||||
initialFetch(key_type const& key);
|
||||
|
||||
void
|
||||
collect_metrics();
|
||||
|
||||
Mutex&
|
||||
lockPartition(key_type const& key) const;
|
||||
|
||||
private:
|
||||
struct Stats
|
||||
{
|
||||
@@ -300,8 +300,8 @@ private:
|
||||
[[maybe_unused]] clock_type::time_point const& now,
|
||||
typename KeyValueCacheType::map_type& partition,
|
||||
SweptPointersVector& stuffToSweep,
|
||||
std::atomic<int>& allRemovals,
|
||||
std::lock_guard<std::recursive_mutex> const&);
|
||||
std::atomic<int>& allRemoval,
|
||||
Mutex& partitionLock);
|
||||
|
||||
[[nodiscard]] std::thread
|
||||
sweepHelper(
|
||||
@@ -310,14 +310,12 @@ private:
|
||||
typename KeyOnlyCacheType::map_type& partition,
|
||||
SweptPointersVector&,
|
||||
std::atomic<int>& allRemovals,
|
||||
std::lock_guard<std::recursive_mutex> const&);
|
||||
Mutex& partitionLock);
|
||||
|
||||
beast::Journal m_journal;
|
||||
clock_type& m_clock;
|
||||
Stats m_stats;
|
||||
|
||||
mutex_type mutable m_mutex;
|
||||
|
||||
// Used for logging
|
||||
std::string m_name;
|
||||
|
||||
@@ -328,10 +326,11 @@ private:
|
||||
clock_type::duration const m_target_age;
|
||||
|
||||
// Number of items cached
|
||||
int m_cache_count;
|
||||
std::atomic<int> m_cache_count;
|
||||
cache_type m_cache; // Hold strong reference to recent objects
|
||||
std::uint64_t m_hits;
|
||||
std::uint64_t m_misses;
|
||||
std::atomic<std::uint64_t> m_hits;
|
||||
std::atomic<std::uint64_t> m_misses;
|
||||
mutable std::vector<mutex_type> partitionLocks_;
|
||||
};
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <xrpl/basics/IntrusivePointer.ipp>
|
||||
#include <xrpl/basics/TaggedCache.h>
|
||||
#include <xrpl/beast/core/CurrentThreadName.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -60,6 +61,7 @@ inline TaggedCache<
|
||||
, m_hits(0)
|
||||
, m_misses(0)
|
||||
{
|
||||
partitionLocks_ = std::vector<mutex_type>(m_cache.partitions());
|
||||
}
|
||||
|
||||
template <
|
||||
@@ -105,8 +107,13 @@ TaggedCache<
|
||||
KeyEqual,
|
||||
Mutex>::size() const
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
return m_cache.size();
|
||||
std::size_t totalSize = 0;
|
||||
for (size_t i = 0; i < partitionLocks_.size(); ++i)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(partitionLocks_[i]);
|
||||
totalSize += m_cache.map()[i].size();
|
||||
}
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
template <
|
||||
@@ -129,8 +136,7 @@ TaggedCache<
|
||||
KeyEqual,
|
||||
Mutex>::getCacheSize() const
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
return m_cache_count;
|
||||
return m_cache_count.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
template <
|
||||
@@ -153,8 +159,7 @@ TaggedCache<
|
||||
KeyEqual,
|
||||
Mutex>::getTrackSize() const
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
return m_cache.size();
|
||||
return size();
|
||||
}
|
||||
|
||||
template <
|
||||
@@ -177,9 +182,10 @@ TaggedCache<
|
||||
KeyEqual,
|
||||
Mutex>::getHitRate()
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
auto const total = static_cast<float>(m_hits + m_misses);
|
||||
return m_hits * (100.0f / std::max(1.0f, total));
|
||||
auto hits = m_hits.load(std::memory_order_relaxed);
|
||||
auto misses = m_misses.load(std::memory_order_relaxed);
|
||||
float total = float(hits + misses);
|
||||
return hits * (100.0f / std::max(1.0f, total));
|
||||
}
|
||||
|
||||
template <
|
||||
@@ -202,9 +208,12 @@ TaggedCache<
|
||||
KeyEqual,
|
||||
Mutex>::clear()
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
for (auto& mutex : partitionLocks_)
|
||||
mutex.lock();
|
||||
m_cache.clear();
|
||||
m_cache_count = 0;
|
||||
for (auto& mutex : partitionLocks_)
|
||||
mutex.unlock();
|
||||
m_cache_count.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
template <
|
||||
@@ -227,11 +236,14 @@ TaggedCache<
|
||||
KeyEqual,
|
||||
Mutex>::reset()
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
for (auto& mutex : partitionLocks_)
|
||||
mutex.lock();
|
||||
m_cache.clear();
|
||||
m_cache_count = 0;
|
||||
m_hits = 0;
|
||||
m_misses = 0;
|
||||
for (auto& mutex : partitionLocks_)
|
||||
mutex.unlock();
|
||||
m_cache_count.store(0, std::memory_order_relaxed);
|
||||
m_hits.store(0, std::memory_order_relaxed);
|
||||
m_misses.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
template <
|
||||
@@ -255,7 +267,7 @@ TaggedCache<
|
||||
KeyEqual,
|
||||
Mutex>::touch_if_exists(KeyComparable const& key)
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
std::lock_guard<Mutex> lock(lockPartition(key));
|
||||
auto const iter(m_cache.find(key));
|
||||
if (iter == m_cache.end())
|
||||
{
|
||||
@@ -297,8 +309,6 @@ TaggedCache<
|
||||
|
||||
auto const start = std::chrono::steady_clock::now();
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
if (m_target_size == 0 ||
|
||||
(static_cast<int>(m_cache.size()) <= m_target_size))
|
||||
{
|
||||
@@ -330,12 +340,13 @@ TaggedCache<
|
||||
m_cache.map()[p],
|
||||
allStuffToSweep[p],
|
||||
allRemovals,
|
||||
lock));
|
||||
partitionLocks_[p]));
|
||||
}
|
||||
for (std::thread& worker : workers)
|
||||
worker.join();
|
||||
|
||||
m_cache_count -= allRemovals;
|
||||
int removals = allRemovals.load(std::memory_order_relaxed);
|
||||
m_cache_count.fetch_sub(removals, std::memory_order_relaxed);
|
||||
}
|
||||
// At this point allStuffToSweep will go out of scope outside the lock
|
||||
// and decrement the reference count on each strong pointer.
|
||||
@@ -369,7 +380,8 @@ TaggedCache<
|
||||
{
|
||||
// Remove from cache, if !valid, remove from map too. Returns true if
|
||||
// removed from cache
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
std::lock_guard<Mutex> lock(lockPartition(key));
|
||||
|
||||
auto cit = m_cache.find(key);
|
||||
|
||||
@@ -382,7 +394,7 @@ TaggedCache<
|
||||
|
||||
if (entry.isCached())
|
||||
{
|
||||
--m_cache_count;
|
||||
m_cache_count.fetch_sub(1, std::memory_order_relaxed);
|
||||
entry.ptr.convertToWeak();
|
||||
ret = true;
|
||||
}
|
||||
@@ -420,17 +432,16 @@ TaggedCache<
|
||||
{
|
||||
// Return canonical value, store if needed, refresh in cache
|
||||
// Return values: true=we had the data already
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
std::lock_guard<Mutex> lock(lockPartition(key));
|
||||
auto cit = m_cache.find(key);
|
||||
|
||||
if (cit == m_cache.end())
|
||||
{
|
||||
m_cache.emplace(
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple(key),
|
||||
std::forward_as_tuple(m_clock.now(), data));
|
||||
++m_cache_count;
|
||||
m_cache_count.fetch_add(1, std::memory_order_relaxed);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -479,12 +490,12 @@ TaggedCache<
|
||||
data = cachedData;
|
||||
}
|
||||
|
||||
++m_cache_count;
|
||||
m_cache_count.fetch_add(1, std::memory_order_relaxed);
|
||||
return true;
|
||||
}
|
||||
|
||||
entry.ptr = data;
|
||||
++m_cache_count;
|
||||
m_cache_count.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -560,10 +571,11 @@ TaggedCache<
|
||||
KeyEqual,
|
||||
Mutex>::fetch(key_type const& key)
|
||||
{
|
||||
std::lock_guard<mutex_type> l(m_mutex);
|
||||
auto ret = initialFetch(key, l);
|
||||
std::lock_guard<Mutex> lock(lockPartition(key));
|
||||
|
||||
auto ret = initialFetch(key);
|
||||
if (!ret)
|
||||
++m_misses;
|
||||
m_misses.fetch_add(1, std::memory_order_relaxed);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -627,8 +639,8 @@ TaggedCache<
|
||||
Mutex>::insert(key_type const& key)
|
||||
-> std::enable_if_t<IsKeyCache, ReturnType>
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
clock_type::time_point const now(m_clock.now());
|
||||
std::lock_guard<Mutex> lock(lockPartition(key));
|
||||
auto [it, inserted] = m_cache.emplace(
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple(key),
|
||||
@@ -668,29 +680,6 @@ TaggedCache<
|
||||
return true;
|
||||
}
|
||||
|
||||
template <
|
||||
class Key,
|
||||
class T,
|
||||
bool IsKeyCache,
|
||||
class SharedWeakUnionPointer,
|
||||
class SharedPointerType,
|
||||
class Hash,
|
||||
class KeyEqual,
|
||||
class Mutex>
|
||||
inline auto
|
||||
TaggedCache<
|
||||
Key,
|
||||
T,
|
||||
IsKeyCache,
|
||||
SharedWeakUnionPointer,
|
||||
SharedPointerType,
|
||||
Hash,
|
||||
KeyEqual,
|
||||
Mutex>::peekMutex() -> mutex_type&
|
||||
{
|
||||
return m_mutex;
|
||||
}
|
||||
|
||||
template <
|
||||
class Key,
|
||||
class T,
|
||||
@@ -714,10 +703,13 @@ TaggedCache<
|
||||
std::vector<key_type> v;
|
||||
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
v.reserve(m_cache.size());
|
||||
for (auto const& _ : m_cache)
|
||||
v.push_back(_.first);
|
||||
for (std::size_t i = 0; i < partitionLocks_.size(); ++i)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(partitionLocks_[i]);
|
||||
for (auto const& entry : m_cache.map()[i])
|
||||
v.push_back(entry.first);
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
@@ -743,11 +735,12 @@ TaggedCache<
|
||||
KeyEqual,
|
||||
Mutex>::rate() const
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
auto const tot = m_hits + m_misses;
|
||||
auto hits = m_hits.load(std::memory_order_relaxed);
|
||||
auto misses = m_misses.load(std::memory_order_relaxed);
|
||||
auto const tot = hits + misses;
|
||||
if (tot == 0)
|
||||
return 0;
|
||||
return double(m_hits) / tot;
|
||||
return 0.0;
|
||||
return double(hits) / tot;
|
||||
}
|
||||
|
||||
template <
|
||||
@@ -771,18 +764,16 @@ TaggedCache<
|
||||
KeyEqual,
|
||||
Mutex>::fetch(key_type const& digest, Handler const& h)
|
||||
{
|
||||
{
|
||||
std::lock_guard l(m_mutex);
|
||||
if (auto ret = initialFetch(digest, l))
|
||||
return ret;
|
||||
}
|
||||
std::lock_guard<Mutex> lock(lockPartition(digest));
|
||||
|
||||
if (auto ret = initialFetch(digest))
|
||||
return ret;
|
||||
|
||||
auto sle = h();
|
||||
if (!sle)
|
||||
return {};
|
||||
|
||||
std::lock_guard l(m_mutex);
|
||||
++m_misses;
|
||||
m_misses.fetch_add(1, std::memory_order_relaxed);
|
||||
auto const [it, inserted] =
|
||||
m_cache.emplace(digest, Entry(m_clock.now(), std::move(sle)));
|
||||
if (!inserted)
|
||||
@@ -809,9 +800,10 @@ TaggedCache<
|
||||
SharedPointerType,
|
||||
Hash,
|
||||
KeyEqual,
|
||||
Mutex>::
|
||||
initialFetch(key_type const& key, std::lock_guard<mutex_type> const& l)
|
||||
Mutex>::initialFetch(key_type const& key)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(lockPartition(key));
|
||||
|
||||
auto cit = m_cache.find(key);
|
||||
if (cit == m_cache.end())
|
||||
return {};
|
||||
@@ -819,7 +811,7 @@ TaggedCache<
|
||||
Entry& entry = cit->second;
|
||||
if (entry.isCached())
|
||||
{
|
||||
++m_hits;
|
||||
m_hits.fetch_add(1, std::memory_order_relaxed);
|
||||
entry.touch(m_clock.now());
|
||||
return entry.ptr.getStrong();
|
||||
}
|
||||
@@ -827,12 +819,13 @@ TaggedCache<
|
||||
if (entry.isCached())
|
||||
{
|
||||
// independent of cache size, so not counted as a hit
|
||||
++m_cache_count;
|
||||
m_cache_count.fetch_add(1, std::memory_order_relaxed);
|
||||
entry.touch(m_clock.now());
|
||||
return entry.ptr.getStrong();
|
||||
}
|
||||
|
||||
m_cache.erase(cit);
|
||||
m_cache.erase(cit); // TODO: if this erase happens on fetch, what is left
|
||||
// for a sweep?
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -861,10 +854,11 @@ TaggedCache<
|
||||
{
|
||||
beast::insight::Gauge::value_type hit_rate(0);
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
auto const total(m_hits + m_misses);
|
||||
auto const hits = m_hits.load(std::memory_order_relaxed);
|
||||
auto const misses = m_misses.load(std::memory_order_relaxed);
|
||||
auto const total = hits + misses;
|
||||
if (total != 0)
|
||||
hit_rate = (m_hits * 100) / total;
|
||||
hit_rate = (hits * 100) / total;
|
||||
}
|
||||
m_stats.hit_rate.set(hit_rate);
|
||||
}
|
||||
@@ -895,12 +889,16 @@ TaggedCache<
|
||||
typename KeyValueCacheType::map_type& partition,
|
||||
SweptPointersVector& stuffToSweep,
|
||||
std::atomic<int>& allRemovals,
|
||||
std::lock_guard<std::recursive_mutex> const&)
|
||||
Mutex& partitionLock)
|
||||
{
|
||||
return std::thread([&, this]() {
|
||||
beast::setCurrentThreadName("sweep-1");
|
||||
|
||||
int cacheRemovals = 0;
|
||||
int mapRemovals = 0;
|
||||
|
||||
std::lock_guard<Mutex> lock(partitionLock);
|
||||
|
||||
// Keep references to all the stuff we sweep
|
||||
// so that we can destroy them outside the lock.
|
||||
stuffToSweep.reserve(partition.size());
|
||||
@@ -984,12 +982,16 @@ TaggedCache<
|
||||
typename KeyOnlyCacheType::map_type& partition,
|
||||
SweptPointersVector&,
|
||||
std::atomic<int>& allRemovals,
|
||||
std::lock_guard<std::recursive_mutex> const&)
|
||||
Mutex& partitionLock)
|
||||
{
|
||||
return std::thread([&, this]() {
|
||||
beast::setCurrentThreadName("sweep-2");
|
||||
|
||||
int cacheRemovals = 0;
|
||||
int mapRemovals = 0;
|
||||
|
||||
std::lock_guard<Mutex> lock(partitionLock);
|
||||
|
||||
// Keep references to all the stuff we sweep
|
||||
// so that we can destroy them outside the lock.
|
||||
{
|
||||
@@ -1024,6 +1026,29 @@ TaggedCache<
|
||||
});
|
||||
}
|
||||
|
||||
template <
|
||||
class Key,
|
||||
class T,
|
||||
bool IsKeyCache,
|
||||
class SharedWeakUnionPointer,
|
||||
class SharedPointerType,
|
||||
class Hash,
|
||||
class KeyEqual,
|
||||
class Mutex>
|
||||
inline Mutex&
|
||||
TaggedCache<
|
||||
Key,
|
||||
T,
|
||||
IsKeyCache,
|
||||
SharedWeakUnionPointer,
|
||||
SharedPointerType,
|
||||
Hash,
|
||||
KeyEqual,
|
||||
Mutex>::lockPartition(key_type const& key) const
|
||||
{
|
||||
return partitionLocks_[m_cache.partition_index(key)];
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -277,6 +277,12 @@ public:
|
||||
return map_;
|
||||
}
|
||||
|
||||
partition_map_type const&
|
||||
map() const
|
||||
{
|
||||
return map_;
|
||||
}
|
||||
|
||||
iterator
|
||||
begin()
|
||||
{
|
||||
@@ -321,6 +327,12 @@ public:
|
||||
return cend();
|
||||
}
|
||||
|
||||
std::size_t
|
||||
partition_index(key_type const& key) const
|
||||
{
|
||||
return partitioner(key);
|
||||
}
|
||||
|
||||
private:
|
||||
template <class T>
|
||||
void
|
||||
@@ -380,7 +392,7 @@ public:
|
||||
clear()
|
||||
{
|
||||
for (auto& p : map_)
|
||||
p.clear();
|
||||
p.clear(); // TODO make sure that it is locked inside
|
||||
}
|
||||
|
||||
iterator
|
||||
@@ -406,7 +418,7 @@ public:
|
||||
{
|
||||
std::size_t ret = 0;
|
||||
for (auto& p : map_)
|
||||
ret += p.size();
|
||||
ret += p.size(); // TODO make sure that it is locked inside
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,18 @@
|
||||
* `VoteBehavior::DefaultYes`. The communication process is beyond
|
||||
* the scope of these instructions.
|
||||
*
|
||||
* 5) A feature marked as Obsolete can mean either:
|
||||
* 1) It is in the ledger (marked as Supported::yes) and it is on its way to
|
||||
* become Retired
|
||||
* 2) The feature is not in the ledger (has always been marked as
|
||||
* Supported::no) and the code to support it has been removed
|
||||
*
|
||||
* If we want to discontinue a feature that we've never fully supported and
|
||||
* the feature has never been enabled, we should remove all the related
|
||||
* code, and mark the feature as "abandoned". To do this:
|
||||
*
|
||||
* 1) Open features.macro, move the feature to the abandoned section and
|
||||
* change the macro to XRPL_ABANDON
|
||||
*
|
||||
* When a feature has been enabled for several years, the conditional code
|
||||
* may be removed, and the feature "retired". To retire a feature:
|
||||
@@ -88,10 +100,13 @@ namespace detail {
|
||||
#undef XRPL_FIX
|
||||
#pragma push_macro("XRPL_RETIRE")
|
||||
#undef XRPL_RETIRE
|
||||
#pragma push_macro("XRPL_ABANDON")
|
||||
#undef XRPL_ABANDON
|
||||
|
||||
#define XRPL_FEATURE(name, supported, vote) +1
|
||||
#define XRPL_FIX(name, supported, vote) +1
|
||||
#define XRPL_RETIRE(name) +1
|
||||
#define XRPL_ABANDON(name) +1
|
||||
|
||||
// This value SHOULD be equal to the number of amendments registered in
|
||||
// Feature.cpp. Because it's only used to reserve storage, and determine how
|
||||
@@ -108,6 +123,8 @@ static constexpr std::size_t numFeatures =
|
||||
#pragma pop_macro("XRPL_FIX")
|
||||
#undef XRPL_FEATURE
|
||||
#pragma pop_macro("XRPL_FEATURE")
|
||||
#undef XRPL_ABANDON
|
||||
#pragma pop_macro("XRPL_ABANDON")
|
||||
|
||||
/** Amendments that this server supports and the default voting behavior.
|
||||
Whether they are enabled depends on the Rules defined in the validated
|
||||
@@ -349,10 +366,13 @@ foreachFeature(FeatureBitset bs, F&& f)
|
||||
#undef XRPL_FIX
|
||||
#pragma push_macro("XRPL_RETIRE")
|
||||
#undef XRPL_RETIRE
|
||||
#pragma push_macro("XRPL_ABANDON")
|
||||
#undef XRPL_ABANDON
|
||||
|
||||
#define XRPL_FEATURE(name, supported, vote) extern uint256 const feature##name;
|
||||
#define XRPL_FIX(name, supported, vote) extern uint256 const fix##name;
|
||||
#define XRPL_RETIRE(name)
|
||||
#define XRPL_ABANDON(name)
|
||||
|
||||
#include <xrpl/protocol/detail/features.macro>
|
||||
|
||||
@@ -362,6 +382,8 @@ foreachFeature(FeatureBitset bs, F&& f)
|
||||
#pragma pop_macro("XRPL_FIX")
|
||||
#undef XRPL_FEATURE
|
||||
#pragma pop_macro("XRPL_FEATURE")
|
||||
#undef XRPL_ABANDON
|
||||
#pragma pop_macro("XRPL_ABANDON")
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
|
||||
namespace ripple {
|
||||
|
||||
namespace RPC {
|
||||
|
||||
/**
|
||||
Adds common synthetic fields to transaction-related JSON responses
|
||||
|
||||
@@ -40,6 +42,7 @@ insertNFTSyntheticInJson(
|
||||
TxMeta const&);
|
||||
/** @} */
|
||||
|
||||
} // namespace RPC
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
#include <xrpl/basics/ByteUtilities.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/partitioned_unordered_map.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
#if !defined(XRPL_RETIRE)
|
||||
#error "undefined macro: XRPL_RETIRE"
|
||||
#endif
|
||||
#if !defined(XRPL_ABANDON)
|
||||
#error "undefined macro: XRPL_ABANDON"
|
||||
#endif
|
||||
|
||||
// Add new amendments to the top of this list.
|
||||
// Keep it sorted in reverse chronological order.
|
||||
@@ -114,7 +117,6 @@ XRPL_FEATURE(DepositAuth, Supported::yes, VoteBehavior::DefaultYe
|
||||
XRPL_FIX (1513, Supported::yes, VoteBehavior::DefaultYes)
|
||||
XRPL_FEATURE(FlowCross, Supported::yes, VoteBehavior::DefaultYes)
|
||||
XRPL_FEATURE(Flow, Supported::yes, VoteBehavior::DefaultYes)
|
||||
XRPL_FEATURE(OwnerPaysFee, Supported::no, VoteBehavior::DefaultNo)
|
||||
|
||||
// The following amendments are obsolete, but must remain supported
|
||||
// because they could potentially get enabled.
|
||||
@@ -132,6 +134,11 @@ XRPL_FIX (NFTokenDirV1, Supported::yes, VoteBehavior::Obsolete)
|
||||
XRPL_FEATURE(NonFungibleTokensV1, Supported::yes, VoteBehavior::Obsolete)
|
||||
XRPL_FEATURE(CryptoConditionsSuite, Supported::yes, VoteBehavior::Obsolete)
|
||||
|
||||
// The following amendments were never supported, never enabled, and
|
||||
// we've abanded them. These features should never be in the ledger,
|
||||
// and we've removed all the related code.
|
||||
XRPL_ABANDON(OwnerPaysFee)
|
||||
|
||||
// The following amendments have been active for at least two years. Their
|
||||
// pre-amendment code has been removed and the identifiers are deprecated.
|
||||
// All known amendments and amendments that may appear in a validated
|
||||
|
||||
@@ -254,7 +254,7 @@ FeatureCollections::registerFeature(
|
||||
{
|
||||
check(!readOnly, "Attempting to register a feature after startup.");
|
||||
check(
|
||||
support == Supported::yes || vote == VoteBehavior::DefaultNo,
|
||||
support == Supported::yes || vote != VoteBehavior::DefaultYes,
|
||||
"Invalid feature parameters. Must be supported to be up-voted.");
|
||||
Feature const* i = getByName(name);
|
||||
if (!i)
|
||||
@@ -268,7 +268,7 @@ FeatureCollections::registerFeature(
|
||||
features.emplace_back(name, f);
|
||||
|
||||
auto const getAmendmentSupport = [=]() {
|
||||
if (vote == VoteBehavior::Obsolete)
|
||||
if (vote == VoteBehavior::Obsolete && support == Supported::yes)
|
||||
return AmendmentSupport::Retired;
|
||||
return support == Supported::yes ? AmendmentSupport::Supported
|
||||
: AmendmentSupport::Unsupported;
|
||||
@@ -398,6 +398,14 @@ retireFeature(std::string const& name)
|
||||
return registerFeature(name, Supported::yes, VoteBehavior::Obsolete);
|
||||
}
|
||||
|
||||
// Abandoned features are not in the ledger and have no code controlled by the
|
||||
// feature. They were never supported, and cannot be voted on.
|
||||
uint256
|
||||
abandonFeature(std::string const& name)
|
||||
{
|
||||
return registerFeature(name, Supported::no, VoteBehavior::Obsolete);
|
||||
}
|
||||
|
||||
/** Tell FeatureCollections when registration is complete. */
|
||||
bool
|
||||
registrationIsDone()
|
||||
@@ -432,6 +440,8 @@ featureToName(uint256 const& f)
|
||||
#undef XRPL_FIX
|
||||
#pragma push_macro("XRPL_RETIRE")
|
||||
#undef XRPL_RETIRE
|
||||
#pragma push_macro("XRPL_ABANDON")
|
||||
#undef XRPL_ABANDON
|
||||
|
||||
#define XRPL_FEATURE(name, supported, vote) \
|
||||
uint256 const feature##name = registerFeature(#name, supported, vote);
|
||||
@@ -443,6 +453,11 @@ featureToName(uint256 const& f)
|
||||
[[deprecated("The referenced amendment has been retired")]] \
|
||||
[[maybe_unused]] \
|
||||
uint256 const retired##name = retireFeature(#name);
|
||||
|
||||
#define XRPL_ABANDON(name) \
|
||||
[[deprecated("The referenced amendment has been abandoned")]] \
|
||||
[[maybe_unused]] \
|
||||
uint256 const abandoned##name = abandonFeature(#name);
|
||||
// clang-format on
|
||||
|
||||
#include <xrpl/protocol/detail/features.macro>
|
||||
@@ -453,6 +468,8 @@ featureToName(uint256 const& f)
|
||||
#pragma pop_macro("XRPL_FIX")
|
||||
#undef XRPL_FEATURE
|
||||
#pragma pop_macro("XRPL_FEATURE")
|
||||
#undef XRPL_ABANDON
|
||||
#pragma pop_macro("XRPL_ABANDON")
|
||||
|
||||
// All of the features should now be registered, since variables in a cpp file
|
||||
// are initialized from top to bottom.
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <memory>
|
||||
|
||||
namespace ripple {
|
||||
namespace RPC {
|
||||
|
||||
void
|
||||
insertNFTSyntheticInJson(
|
||||
@@ -39,4 +40,5 @@ insertNFTSyntheticInJson(
|
||||
insertNFTokenOfferID(response[jss::meta], transaction, transactionMeta);
|
||||
}
|
||||
|
||||
} // namespace RPC
|
||||
} // namespace ripple
|
||||
|
||||
@@ -1183,9 +1183,7 @@ private:
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
// The problem was identified when featureOwnerPaysFee was enabled,
|
||||
// so make sure that gets included.
|
||||
Env env{*this, features | featureOwnerPaysFee};
|
||||
Env env{*this, features};
|
||||
|
||||
// The fee that's charged for transactions.
|
||||
auto const fee = env.current()->fees().base;
|
||||
@@ -2217,271 +2215,6 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testTransferRate(FeatureBitset features)
|
||||
{
|
||||
testcase("Transfer Rate");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
{
|
||||
// transfer fee on AMM
|
||||
Env env(*this, features);
|
||||
|
||||
fund(env, gw, {alice, bob, carol}, XRP(10'000), {USD(1'000)});
|
||||
env(rate(gw, 1.25));
|
||||
env.close();
|
||||
|
||||
AMM ammBob(env, bob, XRP(100), USD(150));
|
||||
// no transfer fee on create
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(1000 - 150)));
|
||||
|
||||
env(pay(alice, carol, USD(50)), path(~USD), sendmax(XRP(50)));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(1'000 - 150)));
|
||||
BEAST_EXPECT(
|
||||
ammBob.expectBalances(XRP(150), USD(100), ammBob.tokens()));
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env, alice, xrpMinusFee(env, 10'000 - 50)));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(1'050)));
|
||||
}
|
||||
|
||||
{
|
||||
// Transfer fee AMM and offer
|
||||
Env env(*this, features);
|
||||
|
||||
fund(
|
||||
env,
|
||||
gw,
|
||||
{alice, bob, carol},
|
||||
XRP(10'000),
|
||||
{USD(1'000), EUR(1'000)});
|
||||
env(rate(gw, 1.25));
|
||||
env.close();
|
||||
|
||||
AMM ammBob(env, bob, XRP(100), USD(140));
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(1'000 - 140)));
|
||||
|
||||
env(offer(bob, USD(50), EUR(50)));
|
||||
|
||||
// alice buys 40EUR with 40XRP
|
||||
env(pay(alice, carol, EUR(40)), path(~USD, ~EUR), sendmax(XRP(40)));
|
||||
|
||||
// 40XRP is swapped in for 40USD
|
||||
BEAST_EXPECT(
|
||||
ammBob.expectBalances(XRP(140), USD(100), ammBob.tokens()));
|
||||
// 40USD buys 40EUR via bob's offer. 40EUR delivered to carol
|
||||
// and bob pays 25% on 40EUR, 40EUR*0.25=10EUR
|
||||
BEAST_EXPECT(expectLine(env, bob, EUR(1'000 - 40 - 40 * 0.25)));
|
||||
// bob gets 40USD back from the offer
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(1'000 - 140 + 40)));
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env, alice, xrpMinusFee(env, 10'000 - 40)));
|
||||
BEAST_EXPECT(expectLine(env, carol, EUR(1'040)));
|
||||
BEAST_EXPECT(expectOffers(env, bob, 1, {{USD(10), EUR(10)}}));
|
||||
}
|
||||
|
||||
{
|
||||
// Transfer fee two consecutive AMM
|
||||
Env env(*this, features);
|
||||
|
||||
fund(
|
||||
env,
|
||||
gw,
|
||||
{alice, bob, carol},
|
||||
XRP(10'000),
|
||||
{USD(1'000), EUR(1'000)});
|
||||
env(rate(gw, 1.25));
|
||||
env.close();
|
||||
|
||||
AMM ammBobXRP_USD(env, bob, XRP(100), USD(140));
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(1'000 - 140)));
|
||||
|
||||
AMM ammBobUSD_EUR(env, bob, USD(100), EUR(140));
|
||||
BEAST_EXPECT(expectLine(env, bob, EUR(1'000 - 140)));
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(1'000 - 140 - 100)));
|
||||
|
||||
// alice buys 40EUR with 40XRP
|
||||
env(pay(alice, carol, EUR(40)), path(~USD, ~EUR), sendmax(XRP(40)));
|
||||
|
||||
// 40XRP is swapped in for 40USD
|
||||
BEAST_EXPECT(ammBobXRP_USD.expectBalances(
|
||||
XRP(140), USD(100), ammBobXRP_USD.tokens()));
|
||||
// 40USD is swapped in for 40EUR
|
||||
BEAST_EXPECT(ammBobUSD_EUR.expectBalances(
|
||||
USD(140), EUR(100), ammBobUSD_EUR.tokens()));
|
||||
// no other charges on bob
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(1'000 - 140 - 100)));
|
||||
BEAST_EXPECT(expectLine(env, bob, EUR(1'000 - 140)));
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env, alice, xrpMinusFee(env, 10'000 - 40)));
|
||||
BEAST_EXPECT(expectLine(env, carol, EUR(1'040)));
|
||||
}
|
||||
|
||||
{
|
||||
// Payment via AMM with limit quality, deliver less
|
||||
// than requested
|
||||
Env env(*this, features);
|
||||
|
||||
fund(
|
||||
env,
|
||||
gw,
|
||||
{alice, bob, carol},
|
||||
XRP(1'000),
|
||||
{USD(1'200), GBP(1'200)});
|
||||
env(rate(gw, 1.25));
|
||||
env.close();
|
||||
|
||||
AMM amm(env, bob, GBP(1'000), USD(1'100));
|
||||
|
||||
// requested quality limit is 90USD/110GBP = 0.8181
|
||||
// trade quality is 77.2727USD/94.4444GBP = 0.8181
|
||||
env(pay(alice, carol, USD(90)),
|
||||
path(~USD),
|
||||
sendmax(GBP(110)),
|
||||
txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
|
||||
env.close();
|
||||
|
||||
if (!features[fixAMMv1_1])
|
||||
{
|
||||
// alice buys 77.2727USD with 75.5555GBP and pays 25% tr fee
|
||||
// on 75.5555GBP
|
||||
// 1,200 - 75.55555*1.25 = 1200 - 94.4444 = 1105.55555GBP
|
||||
BEAST_EXPECT(expectLine(
|
||||
env,
|
||||
alice,
|
||||
STAmount{GBP, UINT64_C(1'105'555555555555), -12}));
|
||||
// 75.5555GBP is swapped in for 77.7272USD
|
||||
BEAST_EXPECT(amm.expectBalances(
|
||||
STAmount{GBP, UINT64_C(1'075'555555555556), -12},
|
||||
STAmount{USD, UINT64_C(1'022'727272727272), -12},
|
||||
amm.tokens()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// alice buys 77.2727USD with 75.5555GBP and pays 25% tr fee
|
||||
// on 75.5555GBP
|
||||
// 1,200 - 75.55555*1.25 = 1200 - 94.4444 = 1105.55555GBP
|
||||
BEAST_EXPECT(expectLine(
|
||||
env,
|
||||
alice,
|
||||
STAmount{GBP, UINT64_C(1'105'555555555554), -12}));
|
||||
// 75.5555GBP is swapped in for 77.7272USD
|
||||
BEAST_EXPECT(amm.expectBalances(
|
||||
STAmount{GBP, UINT64_C(1'075'555555555557), -12},
|
||||
STAmount{USD, UINT64_C(1'022'727272727272), -12},
|
||||
amm.tokens()));
|
||||
}
|
||||
BEAST_EXPECT(expectLine(
|
||||
env, carol, STAmount{USD, UINT64_C(1'277'272727272728), -12}));
|
||||
}
|
||||
|
||||
{
|
||||
// AMM offer crossing
|
||||
Env env(*this, features);
|
||||
|
||||
fund(env, gw, {alice, bob}, XRP(1'000), {USD(1'200), EUR(1'200)});
|
||||
env(rate(gw, 1.25));
|
||||
env.close();
|
||||
|
||||
AMM amm(env, bob, USD(1'000), EUR(1'150));
|
||||
|
||||
env(offer(alice, EUR(100), USD(100)));
|
||||
env.close();
|
||||
|
||||
if (!features[fixAMMv1_1])
|
||||
{
|
||||
// 95.2380USD is swapped in for 100EUR
|
||||
BEAST_EXPECT(amm.expectBalances(
|
||||
STAmount{USD, UINT64_C(1'095'238095238095), -12},
|
||||
EUR(1'050),
|
||||
amm.tokens()));
|
||||
// alice pays 25% tr fee on 95.2380USD
|
||||
// 1200-95.2380*1.25 = 1200 - 119.0477 = 1080.9523USD
|
||||
BEAST_EXPECT(expectLine(
|
||||
env,
|
||||
alice,
|
||||
STAmount{USD, UINT64_C(1'080'952380952381), -12},
|
||||
EUR(1'300)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 95.2380USD is swapped in for 100EUR
|
||||
BEAST_EXPECT(amm.expectBalances(
|
||||
STAmount{USD, UINT64_C(1'095'238095238096), -12},
|
||||
EUR(1'050),
|
||||
amm.tokens()));
|
||||
// alice pays 25% tr fee on 95.2380USD
|
||||
// 1200-95.2380*1.25 = 1200 - 119.0477 = 1080.9523USD
|
||||
BEAST_EXPECT(expectLine(
|
||||
env,
|
||||
alice,
|
||||
STAmount{USD, UINT64_C(1'080'95238095238), -11},
|
||||
EUR(1'300)));
|
||||
}
|
||||
BEAST_EXPECT(expectOffers(env, alice, 0));
|
||||
}
|
||||
|
||||
{
|
||||
// First pass through a strand redeems, second pass issues,
|
||||
// through an offer limiting step is not an endpoint
|
||||
Env env(*this, features);
|
||||
auto const USDA = alice["USD"];
|
||||
auto const USDB = bob["USD"];
|
||||
Account const dan("dan");
|
||||
|
||||
env.fund(XRP(10'000), bob, carol, dan, gw);
|
||||
fund(env, {alice}, XRP(10'000));
|
||||
env(rate(gw, 1.25));
|
||||
env.trust(USD(2'000), alice, bob, carol, dan);
|
||||
env.trust(EUR(2'000), carol, dan);
|
||||
env.trust(USDA(1'000), bob);
|
||||
env.trust(USDB(1'000), gw);
|
||||
env(pay(gw, bob, USD(50)));
|
||||
env(pay(gw, dan, EUR(1'050)));
|
||||
env(pay(gw, dan, USD(1'000)));
|
||||
AMM ammDan(env, dan, USD(1'000), EUR(1'050));
|
||||
|
||||
if (!features[fixAMMv1_1])
|
||||
{
|
||||
// alice -> bob -> gw -> carol. $50 should have transfer fee;
|
||||
// $10, no fee
|
||||
env(pay(alice, carol, EUR(50)),
|
||||
path(bob, gw, ~EUR),
|
||||
sendmax(USDA(60)),
|
||||
txflags(tfNoRippleDirect));
|
||||
BEAST_EXPECT(ammDan.expectBalances(
|
||||
USD(1'050), EUR(1'000), ammDan.tokens()));
|
||||
BEAST_EXPECT(expectLine(env, dan, USD(0)));
|
||||
BEAST_EXPECT(expectLine(env, dan, EUR(0)));
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(-10)));
|
||||
BEAST_EXPECT(expectLine(env, bob, USDA(60)));
|
||||
BEAST_EXPECT(expectLine(env, carol, EUR(50)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// alice -> bob -> gw -> carol. $50 should have transfer fee;
|
||||
// $10, no fee
|
||||
env(pay(alice, carol, EUR(50)),
|
||||
path(bob, gw, ~EUR),
|
||||
sendmax(USDA(60.1)),
|
||||
txflags(tfNoRippleDirect));
|
||||
BEAST_EXPECT(ammDan.expectBalances(
|
||||
STAmount{USD, UINT64_C(1'050'000000000001), -12},
|
||||
EUR(1'000),
|
||||
ammDan.tokens()));
|
||||
BEAST_EXPECT(expectLine(env, dan, USD(0)));
|
||||
BEAST_EXPECT(expectLine(env, dan, EUR(0)));
|
||||
BEAST_EXPECT(expectLine(
|
||||
env, bob, STAmount{USD, INT64_C(-10'000000000001), -12}));
|
||||
BEAST_EXPECT(expectLine(
|
||||
env, bob, STAmount{USDA, UINT64_C(60'000000000001), -12}));
|
||||
BEAST_EXPECT(expectLine(env, carol, EUR(50)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testTransferRateNoOwnerFee(FeatureBitset features)
|
||||
{
|
||||
@@ -4057,13 +3790,9 @@ private:
|
||||
{
|
||||
using namespace jtx;
|
||||
FeatureBitset const all{supported_amendments()};
|
||||
FeatureBitset const ownerPaysFee{featureOwnerPaysFee};
|
||||
|
||||
testFalseDry(all);
|
||||
testBookStep(all);
|
||||
testBookStep(all | ownerPaysFee);
|
||||
testTransferRate(all | ownerPaysFee);
|
||||
testTransferRate((all - fixAMMv1_1 - fixAMMv1_3) | ownerPaysFee);
|
||||
testTransferRateNoOwnerFee(all);
|
||||
testTransferRateNoOwnerFee(all - fixAMMv1_1 - fixAMMv1_3);
|
||||
testLimitQuality();
|
||||
|
||||
@@ -599,158 +599,18 @@ struct Flow_test : public beast::unit_test::suite
|
||||
Account const bob("bob");
|
||||
Account const carol("carol");
|
||||
|
||||
{
|
||||
// Simple payment through a gateway with a
|
||||
// transfer rate
|
||||
Env env(*this, features);
|
||||
// Offer where the owner is also the issuer, sender pays fee
|
||||
Env env(*this, features);
|
||||
|
||||
env.fund(XRP(10000), alice, bob, carol, gw);
|
||||
env.close();
|
||||
env(rate(gw, 1.25));
|
||||
env.trust(USD(1000), alice, bob, carol);
|
||||
env(pay(gw, alice, USD(50)));
|
||||
env.require(balance(alice, USD(50)));
|
||||
env(pay(alice, bob, USD(40)), sendmax(USD(50)));
|
||||
env.require(balance(bob, USD(40)), balance(alice, USD(0)));
|
||||
}
|
||||
{
|
||||
// transfer rate is not charged when issuer is src or dst
|
||||
Env env(*this, features);
|
||||
|
||||
env.fund(XRP(10000), alice, bob, carol, gw);
|
||||
env.close();
|
||||
env(rate(gw, 1.25));
|
||||
env.trust(USD(1000), alice, bob, carol);
|
||||
env(pay(gw, alice, USD(50)));
|
||||
env.require(balance(alice, USD(50)));
|
||||
env(pay(alice, gw, USD(40)), sendmax(USD(40)));
|
||||
env.require(balance(alice, USD(10)));
|
||||
}
|
||||
{
|
||||
// transfer fee on an offer
|
||||
Env env(*this, features);
|
||||
|
||||
env.fund(XRP(10000), alice, bob, carol, gw);
|
||||
env.close();
|
||||
env(rate(gw, 1.25));
|
||||
env.trust(USD(1000), alice, bob, carol);
|
||||
env(pay(gw, bob, USD(65)));
|
||||
|
||||
env(offer(bob, XRP(50), USD(50)));
|
||||
|
||||
env(pay(alice, carol, USD(50)), path(~USD), sendmax(XRP(50)));
|
||||
env.require(
|
||||
balance(alice, xrpMinusFee(env, 10000 - 50)),
|
||||
balance(bob, USD(2.5)), // owner pays transfer fee
|
||||
balance(carol, USD(50)));
|
||||
}
|
||||
|
||||
{
|
||||
// Transfer fee two consecutive offers
|
||||
Env env(*this, features);
|
||||
|
||||
env.fund(XRP(10000), alice, bob, carol, gw);
|
||||
env.close();
|
||||
env(rate(gw, 1.25));
|
||||
env.trust(USD(1000), alice, bob, carol);
|
||||
env.trust(EUR(1000), alice, bob, carol);
|
||||
env(pay(gw, bob, USD(50)));
|
||||
env(pay(gw, bob, EUR(50)));
|
||||
|
||||
env(offer(bob, XRP(50), USD(50)));
|
||||
env(offer(bob, USD(50), EUR(50)));
|
||||
|
||||
env(pay(alice, carol, EUR(40)), path(~USD, ~EUR), sendmax(XRP(40)));
|
||||
env.require(
|
||||
balance(alice, xrpMinusFee(env, 10000 - 40)),
|
||||
balance(bob, USD(40)),
|
||||
balance(bob, EUR(0)),
|
||||
balance(carol, EUR(40)));
|
||||
}
|
||||
|
||||
{
|
||||
// First pass through a strand redeems, second pass issues, no
|
||||
// offers limiting step is not an endpoint
|
||||
Env env(*this, features);
|
||||
auto const USDA = alice["USD"];
|
||||
auto const USDB = bob["USD"];
|
||||
|
||||
env.fund(XRP(10000), alice, bob, carol, gw);
|
||||
env.close();
|
||||
env(rate(gw, 1.25));
|
||||
env.trust(USD(1000), alice, bob, carol);
|
||||
env.trust(USDA(1000), bob);
|
||||
env.trust(USDB(1000), gw);
|
||||
env(pay(gw, bob, USD(50)));
|
||||
// alice -> bob -> gw -> carol. $50 should have transfer fee; $10,
|
||||
// no fee
|
||||
env(pay(alice, carol, USD(50)), path(bob), sendmax(USDA(60)));
|
||||
env.require(
|
||||
balance(bob, USD(-10)),
|
||||
balance(bob, USDA(60)),
|
||||
balance(carol, USD(50)));
|
||||
}
|
||||
{
|
||||
// First pass through a strand redeems, second pass issues, through
|
||||
// an offer limiting step is not an endpoint
|
||||
Env env(*this, features);
|
||||
auto const USDA = alice["USD"];
|
||||
auto const USDB = bob["USD"];
|
||||
Account const dan("dan");
|
||||
|
||||
env.fund(XRP(10000), alice, bob, carol, dan, gw);
|
||||
env.close();
|
||||
env(rate(gw, 1.25));
|
||||
env.trust(USD(1000), alice, bob, carol, dan);
|
||||
env.trust(EUR(1000), carol, dan);
|
||||
env.trust(USDA(1000), bob);
|
||||
env.trust(USDB(1000), gw);
|
||||
env(pay(gw, bob, USD(50)));
|
||||
env(pay(gw, dan, EUR(100)));
|
||||
env(offer(dan, USD(100), EUR(100)));
|
||||
// alice -> bob -> gw -> carol. $50 should have transfer fee; $10,
|
||||
// no fee
|
||||
env(pay(alice, carol, EUR(50)),
|
||||
path(bob, gw, ~EUR),
|
||||
sendmax(USDA(60)),
|
||||
txflags(tfNoRippleDirect));
|
||||
env.require(
|
||||
balance(bob, USD(-10)),
|
||||
balance(bob, USDA(60)),
|
||||
balance(dan, USD(50)),
|
||||
balance(dan, EUR(37.5)),
|
||||
balance(carol, EUR(50)));
|
||||
}
|
||||
|
||||
{
|
||||
// Offer where the owner is also the issuer, owner pays fee
|
||||
Env env(*this, features);
|
||||
|
||||
env.fund(XRP(10000), alice, bob, gw);
|
||||
env.close();
|
||||
env(rate(gw, 1.25));
|
||||
env.trust(USD(1000), alice, bob);
|
||||
env(offer(gw, XRP(100), USD(100)));
|
||||
env(pay(alice, bob, USD(100)), sendmax(XRP(100)));
|
||||
env.require(
|
||||
balance(alice, xrpMinusFee(env, 10000 - 100)),
|
||||
balance(bob, USD(100)));
|
||||
}
|
||||
if (!features[featureOwnerPaysFee])
|
||||
{
|
||||
// Offer where the owner is also the issuer, sender pays fee
|
||||
Env env(*this, features);
|
||||
|
||||
env.fund(XRP(10000), alice, bob, gw);
|
||||
env.close();
|
||||
env(rate(gw, 1.25));
|
||||
env.trust(USD(1000), alice, bob);
|
||||
env(offer(gw, XRP(125), USD(125)));
|
||||
env(pay(alice, bob, USD(100)), sendmax(XRP(200)));
|
||||
env.require(
|
||||
balance(alice, xrpMinusFee(env, 10000 - 125)),
|
||||
balance(bob, USD(100)));
|
||||
}
|
||||
env.fund(XRP(10000), alice, bob, gw);
|
||||
env.close();
|
||||
env(rate(gw, 1.25));
|
||||
env.trust(USD(1000), alice, bob);
|
||||
env(offer(gw, XRP(125), USD(125)));
|
||||
env(pay(alice, bob, USD(100)), sendmax(XRP(200)));
|
||||
env.require(
|
||||
balance(alice, xrpMinusFee(env, 10000 - 125)),
|
||||
balance(bob, USD(100)));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1445,7 +1305,6 @@ struct Flow_test : public beast::unit_test::suite
|
||||
testWithFeats(FeatureBitset features)
|
||||
{
|
||||
using namespace jtx;
|
||||
FeatureBitset const ownerPaysFee{featureOwnerPaysFee};
|
||||
FeatureBitset const reducedOffersV2(fixReducedOffersV2);
|
||||
|
||||
testLineQuality(features);
|
||||
@@ -1453,9 +1312,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
testBookStep(features - reducedOffersV2);
|
||||
testDirectStep(features);
|
||||
testBookStep(features);
|
||||
testDirectStep(features | ownerPaysFee);
|
||||
testBookStep(features | ownerPaysFee);
|
||||
testTransferRate(features | ownerPaysFee);
|
||||
testTransferRate(features);
|
||||
testSelfPayment1(features);
|
||||
testSelfPayment2(features);
|
||||
testSelfFundedXRPEndpoint(false, features);
|
||||
|
||||
@@ -3643,9 +3643,7 @@ public:
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
// The problem was identified when featureOwnerPaysFee was enabled,
|
||||
// so make sure that gets included.
|
||||
Env env{*this, features | featureOwnerPaysFee};
|
||||
Env env{*this, features};
|
||||
|
||||
// The fee that's charged for transactions.
|
||||
auto const fee = env.current()->fees().base;
|
||||
|
||||
@@ -264,7 +264,7 @@ class TheoreticalQuality_test : public beast::unit_test::suite
|
||||
sendMaxIssue,
|
||||
rcp.paths,
|
||||
/*defaultPaths*/ rcp.paths.empty(),
|
||||
sb.rules().enabled(featureOwnerPaysFee),
|
||||
false,
|
||||
OfferCrossing::no,
|
||||
ammContext,
|
||||
std::nullopt,
|
||||
|
||||
@@ -1,144 +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/RangeSet.h>
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
|
||||
namespace ripple {
|
||||
class RangeSet_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
testPrevMissing()
|
||||
{
|
||||
testcase("prevMissing");
|
||||
|
||||
// Set will include:
|
||||
// [ 0, 5]
|
||||
// [10,15]
|
||||
// [20,25]
|
||||
// etc...
|
||||
|
||||
RangeSet<std::uint32_t> set;
|
||||
for (std::uint32_t i = 0; i < 10; ++i)
|
||||
set.insert(range(10 * i, 10 * i + 5));
|
||||
|
||||
for (std::uint32_t i = 1; i < 100; ++i)
|
||||
{
|
||||
std::optional<std::uint32_t> expected;
|
||||
// no prev missing in domain for i <= 6
|
||||
if (i > 6)
|
||||
{
|
||||
std::uint32_t const oneBelowRange = (10 * (i / 10)) - 1;
|
||||
|
||||
expected = ((i % 10) > 6) ? (i - 1) : oneBelowRange;
|
||||
}
|
||||
BEAST_EXPECT(prevMissing(set, i) == expected);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testToString()
|
||||
{
|
||||
testcase("toString");
|
||||
|
||||
RangeSet<std::uint32_t> set;
|
||||
BEAST_EXPECT(to_string(set) == "empty");
|
||||
|
||||
set.insert(1);
|
||||
BEAST_EXPECT(to_string(set) == "1");
|
||||
|
||||
set.insert(range(4u, 6u));
|
||||
BEAST_EXPECT(to_string(set) == "1,4-6");
|
||||
|
||||
set.insert(2);
|
||||
BEAST_EXPECT(to_string(set) == "1-2,4-6");
|
||||
|
||||
set.erase(range(4u, 5u));
|
||||
BEAST_EXPECT(to_string(set) == "1-2,6");
|
||||
}
|
||||
|
||||
void
|
||||
testFromString()
|
||||
{
|
||||
testcase("fromString");
|
||||
|
||||
RangeSet<std::uint32_t> set;
|
||||
|
||||
BEAST_EXPECT(!from_string(set, ""));
|
||||
BEAST_EXPECT(boost::icl::length(set) == 0);
|
||||
|
||||
BEAST_EXPECT(!from_string(set, "#"));
|
||||
BEAST_EXPECT(boost::icl::length(set) == 0);
|
||||
|
||||
BEAST_EXPECT(!from_string(set, ","));
|
||||
BEAST_EXPECT(boost::icl::length(set) == 0);
|
||||
|
||||
BEAST_EXPECT(!from_string(set, ",-"));
|
||||
BEAST_EXPECT(boost::icl::length(set) == 0);
|
||||
|
||||
BEAST_EXPECT(!from_string(set, "1,,2"));
|
||||
BEAST_EXPECT(boost::icl::length(set) == 0);
|
||||
|
||||
BEAST_EXPECT(from_string(set, "1"));
|
||||
BEAST_EXPECT(boost::icl::length(set) == 1);
|
||||
BEAST_EXPECT(boost::icl::first(set) == 1);
|
||||
|
||||
BEAST_EXPECT(from_string(set, "1,1"));
|
||||
BEAST_EXPECT(boost::icl::length(set) == 1);
|
||||
BEAST_EXPECT(boost::icl::first(set) == 1);
|
||||
|
||||
BEAST_EXPECT(from_string(set, "1-1"));
|
||||
BEAST_EXPECT(boost::icl::length(set) == 1);
|
||||
BEAST_EXPECT(boost::icl::first(set) == 1);
|
||||
|
||||
BEAST_EXPECT(from_string(set, "1,4-6"));
|
||||
BEAST_EXPECT(boost::icl::length(set) == 4);
|
||||
BEAST_EXPECT(boost::icl::first(set) == 1);
|
||||
BEAST_EXPECT(!boost::icl::contains(set, 2));
|
||||
BEAST_EXPECT(!boost::icl::contains(set, 3));
|
||||
BEAST_EXPECT(boost::icl::contains(set, 4));
|
||||
BEAST_EXPECT(boost::icl::contains(set, 5));
|
||||
BEAST_EXPECT(boost::icl::last(set) == 6);
|
||||
|
||||
BEAST_EXPECT(from_string(set, "1-2,4-6"));
|
||||
BEAST_EXPECT(boost::icl::length(set) == 5);
|
||||
BEAST_EXPECT(boost::icl::first(set) == 1);
|
||||
BEAST_EXPECT(boost::icl::contains(set, 2));
|
||||
BEAST_EXPECT(boost::icl::contains(set, 4));
|
||||
BEAST_EXPECT(boost::icl::last(set) == 6);
|
||||
|
||||
BEAST_EXPECT(from_string(set, "1-2,6"));
|
||||
BEAST_EXPECT(boost::icl::length(set) == 3);
|
||||
BEAST_EXPECT(boost::icl::first(set) == 1);
|
||||
BEAST_EXPECT(boost::icl::contains(set, 2));
|
||||
BEAST_EXPECT(boost::icl::last(set) == 6);
|
||||
}
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testPrevMissing();
|
||||
testToString();
|
||||
testFromString();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(RangeSet, ripple_basics, ripple);
|
||||
|
||||
} // namespace ripple
|
||||
@@ -1,116 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github0.com/ripple/rippled
|
||||
Copyright (c) 2012-2016 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/Slice.h>
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
struct Slice_test : beast::unit_test::suite
|
||||
{
|
||||
void
|
||||
run() override
|
||||
{
|
||||
std::uint8_t const data[] = {
|
||||
0xa8, 0xa1, 0x38, 0x45, 0x23, 0xec, 0xe4, 0x23, 0x71, 0x6d, 0x2a,
|
||||
0x18, 0xb4, 0x70, 0xcb, 0xf5, 0xac, 0x2d, 0x89, 0x4d, 0x19, 0x9c,
|
||||
0xf0, 0x2c, 0x15, 0xd1, 0xf9, 0x9b, 0x66, 0xd2, 0x30, 0xd3};
|
||||
|
||||
{
|
||||
testcase("Equality & Inequality");
|
||||
|
||||
Slice const s0{};
|
||||
|
||||
BEAST_EXPECT(s0.size() == 0);
|
||||
BEAST_EXPECT(s0.data() == nullptr);
|
||||
BEAST_EXPECT(s0 == s0);
|
||||
|
||||
// Test slices of equal and unequal size pointing to same data:
|
||||
for (std::size_t i = 0; i != sizeof(data); ++i)
|
||||
{
|
||||
Slice const s1{data, i};
|
||||
|
||||
BEAST_EXPECT(s1.size() == i);
|
||||
BEAST_EXPECT(s1.data() != nullptr);
|
||||
|
||||
if (i == 0)
|
||||
BEAST_EXPECT(s1 == s0);
|
||||
else
|
||||
BEAST_EXPECT(s1 != s0);
|
||||
|
||||
for (std::size_t j = 0; j != sizeof(data); ++j)
|
||||
{
|
||||
Slice const s2{data, j};
|
||||
|
||||
if (i == j)
|
||||
BEAST_EXPECT(s1 == s2);
|
||||
else
|
||||
BEAST_EXPECT(s1 != s2);
|
||||
}
|
||||
}
|
||||
|
||||
// Test slices of equal size but pointing to different data:
|
||||
std::array<std::uint8_t, sizeof(data)> a;
|
||||
std::array<std::uint8_t, sizeof(data)> b;
|
||||
|
||||
for (std::size_t i = 0; i != sizeof(data); ++i)
|
||||
a[i] = b[i] = data[i];
|
||||
|
||||
BEAST_EXPECT(makeSlice(a) == makeSlice(b));
|
||||
b[7]++;
|
||||
BEAST_EXPECT(makeSlice(a) != makeSlice(b));
|
||||
a[7]++;
|
||||
BEAST_EXPECT(makeSlice(a) == makeSlice(b));
|
||||
}
|
||||
|
||||
{
|
||||
testcase("Indexing");
|
||||
|
||||
Slice const s{data, sizeof(data)};
|
||||
|
||||
for (std::size_t i = 0; i != sizeof(data); ++i)
|
||||
BEAST_EXPECT(s[i] == data[i]);
|
||||
}
|
||||
|
||||
{
|
||||
testcase("Advancing");
|
||||
|
||||
for (std::size_t i = 0; i < sizeof(data); ++i)
|
||||
{
|
||||
for (std::size_t j = 0; i + j < sizeof(data); ++j)
|
||||
{
|
||||
Slice s(data + i, sizeof(data) - i);
|
||||
s += j;
|
||||
|
||||
BEAST_EXPECT(s.data() == data + i + j);
|
||||
BEAST_EXPECT(s.size() == sizeof(data) - i - j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Slice, ripple_basics, ripple);
|
||||
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
@@ -1,82 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2018 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
//
|
||||
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#include <xrpl/basics/base64.h>
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class base64_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
check(std::string const& in, std::string const& out)
|
||||
{
|
||||
auto const encoded = base64_encode(in);
|
||||
BEAST_EXPECT(encoded == out);
|
||||
BEAST_EXPECT(base64_decode(encoded) == in);
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
check("", "");
|
||||
check("f", "Zg==");
|
||||
check("fo", "Zm8=");
|
||||
check("foo", "Zm9v");
|
||||
check("foob", "Zm9vYg==");
|
||||
check("fooba", "Zm9vYmE=");
|
||||
check("foobar", "Zm9vYmFy");
|
||||
|
||||
check(
|
||||
"Man is distinguished, not only by his reason, but by this "
|
||||
"singular passion from "
|
||||
"other animals, which is a lust of the mind, that by a "
|
||||
"perseverance of delight "
|
||||
"in the continued and indefatigable generation of knowledge, "
|
||||
"exceeds the short "
|
||||
"vehemence of any carnal pleasure.",
|
||||
"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dC"
|
||||
"BieSB0aGlz"
|
||||
"IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIG"
|
||||
"x1c3Qgb2Yg"
|
||||
"dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aG"
|
||||
"UgY29udGlu"
|
||||
"dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleG"
|
||||
"NlZWRzIHRo"
|
||||
"ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=");
|
||||
|
||||
std::string const notBase64 = "not_base64!!";
|
||||
std::string const truncated = "not";
|
||||
BEAST_EXPECT(base64_decode(notBase64) == base64_decode(truncated));
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(base64, ripple_basics, ripple);
|
||||
|
||||
} // namespace ripple
|
||||
@@ -1,62 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2016 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/mulDiv.h>
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
struct mulDiv_test : beast::unit_test::suite
|
||||
{
|
||||
void
|
||||
run() override
|
||||
{
|
||||
auto const max = std::numeric_limits<std::uint64_t>::max();
|
||||
std::uint64_t const max32 = std::numeric_limits<std::uint32_t>::max();
|
||||
|
||||
auto result = mulDiv(85, 20, 5);
|
||||
BEAST_EXPECT(result && *result == 340);
|
||||
result = mulDiv(20, 85, 5);
|
||||
BEAST_EXPECT(result && *result == 340);
|
||||
|
||||
result = mulDiv(0, max - 1, max - 3);
|
||||
BEAST_EXPECT(result && *result == 0);
|
||||
result = mulDiv(max - 1, 0, max - 3);
|
||||
BEAST_EXPECT(result && *result == 0);
|
||||
|
||||
result = mulDiv(max, 2, max / 2);
|
||||
BEAST_EXPECT(result && *result == 4);
|
||||
result = mulDiv(max, 1000, max / 1000);
|
||||
BEAST_EXPECT(result && *result == 1000000);
|
||||
result = mulDiv(max, 1000, max / 1001);
|
||||
BEAST_EXPECT(result && *result == 1001000);
|
||||
result = mulDiv(max32 + 1, max32 + 1, 5);
|
||||
BEAST_EXPECT(result && *result == 3689348814741910323);
|
||||
|
||||
// Overflow
|
||||
result = mulDiv(max - 1, max - 2, 5);
|
||||
BEAST_EXPECT(!result);
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(mulDiv, ripple_basics, ripple);
|
||||
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
@@ -1,193 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github0.com/ripple/rippled
|
||||
Copyright (c) 2021 Ripple 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/scope.h>
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
struct scope_test : beast::unit_test::suite
|
||||
{
|
||||
void
|
||||
test_scope_exit()
|
||||
{
|
||||
// scope_exit always executes the functor on destruction,
|
||||
// unless release() is called
|
||||
int i = 0;
|
||||
{
|
||||
scope_exit x{[&i]() { i = 1; }};
|
||||
}
|
||||
BEAST_EXPECT(i == 1);
|
||||
{
|
||||
scope_exit x{[&i]() { i = 2; }};
|
||||
x.release();
|
||||
}
|
||||
BEAST_EXPECT(i == 1);
|
||||
{
|
||||
scope_exit x{[&i]() { i += 2; }};
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
BEAST_EXPECT(i == 3);
|
||||
{
|
||||
scope_exit x{[&i]() { i = 4; }};
|
||||
x.release();
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
BEAST_EXPECT(i == 3);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_exit x{[&i]() { i = 5; }};
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
BEAST_EXPECT(i == 5);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_exit x{[&i]() { i = 6; }};
|
||||
x.release();
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
BEAST_EXPECT(i == 5);
|
||||
}
|
||||
|
||||
void
|
||||
test_scope_fail()
|
||||
{
|
||||
// scope_fail executes the functor on destruction only
|
||||
// if an exception is unwinding, unless release() is called
|
||||
int i = 0;
|
||||
{
|
||||
scope_fail x{[&i]() { i = 1; }};
|
||||
}
|
||||
BEAST_EXPECT(i == 0);
|
||||
{
|
||||
scope_fail x{[&i]() { i = 2; }};
|
||||
x.release();
|
||||
}
|
||||
BEAST_EXPECT(i == 0);
|
||||
{
|
||||
scope_fail x{[&i]() { i = 3; }};
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
BEAST_EXPECT(i == 0);
|
||||
{
|
||||
scope_fail x{[&i]() { i = 4; }};
|
||||
x.release();
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
BEAST_EXPECT(i == 0);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_fail x{[&i]() { i = 5; }};
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
BEAST_EXPECT(i == 5);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_fail x{[&i]() { i = 6; }};
|
||||
x.release();
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
BEAST_EXPECT(i == 5);
|
||||
}
|
||||
|
||||
void
|
||||
test_scope_success()
|
||||
{
|
||||
// scope_success executes the functor on destruction only
|
||||
// if an exception is not unwinding, unless release() is called
|
||||
int i = 0;
|
||||
{
|
||||
scope_success x{[&i]() { i = 1; }};
|
||||
}
|
||||
BEAST_EXPECT(i == 1);
|
||||
{
|
||||
scope_success x{[&i]() { i = 2; }};
|
||||
x.release();
|
||||
}
|
||||
BEAST_EXPECT(i == 1);
|
||||
{
|
||||
scope_success x{[&i]() { i += 2; }};
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
BEAST_EXPECT(i == 3);
|
||||
{
|
||||
scope_success x{[&i]() { i = 4; }};
|
||||
x.release();
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
BEAST_EXPECT(i == 3);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_success x{[&i]() { i = 5; }};
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
BEAST_EXPECT(i == 3);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_success x{[&i]() { i = 6; }};
|
||||
x.release();
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
BEAST_EXPECT(i == 3);
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
test_scope_exit();
|
||||
test_scope_fail();
|
||||
test_scope_success();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(scope, ripple_basics, ripple);
|
||||
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
@@ -1,258 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright 2014, Nikolaos D. Bougalis <nikb@bougalis.net>
|
||||
|
||||
|
||||
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/tagged_integer.h>
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
class tagged_integer_test : public beast::unit_test::suite
|
||||
{
|
||||
private:
|
||||
struct Tag1
|
||||
{
|
||||
};
|
||||
struct Tag2
|
||||
{
|
||||
};
|
||||
|
||||
// Static checks that types are not interoperable
|
||||
|
||||
using TagUInt1 = tagged_integer<std::uint32_t, Tag1>;
|
||||
using TagUInt2 = tagged_integer<std::uint32_t, Tag2>;
|
||||
using TagUInt3 = tagged_integer<std::uint64_t, Tag1>;
|
||||
|
||||
// Check construction of tagged_integers
|
||||
static_assert(
|
||||
std::is_constructible<TagUInt1, std::uint32_t>::value,
|
||||
"TagUInt1 should be constructible using a std::uint32_t");
|
||||
|
||||
static_assert(
|
||||
!std::is_constructible<TagUInt1, std::uint64_t>::value,
|
||||
"TagUInt1 should not be constructible using a std::uint64_t");
|
||||
|
||||
static_assert(
|
||||
std::is_constructible<TagUInt3, std::uint32_t>::value,
|
||||
"TagUInt3 should be constructible using a std::uint32_t");
|
||||
|
||||
static_assert(
|
||||
std::is_constructible<TagUInt3, std::uint64_t>::value,
|
||||
"TagUInt3 should be constructible using a std::uint64_t");
|
||||
|
||||
// Check assignment of tagged_integers
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt1, std::uint32_t>::value,
|
||||
"TagUInt1 should not be assignable with a std::uint32_t");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt1, std::uint64_t>::value,
|
||||
"TagUInt1 should not be assignable with a std::uint64_t");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt3, std::uint32_t>::value,
|
||||
"TagUInt3 should not be assignable with a std::uint32_t");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt3, std::uint64_t>::value,
|
||||
"TagUInt3 should not be assignable with a std::uint64_t");
|
||||
|
||||
static_assert(
|
||||
std::is_assignable<TagUInt1, TagUInt1>::value,
|
||||
"TagUInt1 should be assignable with a TagUInt1");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt1, TagUInt2>::value,
|
||||
"TagUInt1 should not be assignable with a TagUInt2");
|
||||
|
||||
static_assert(
|
||||
std::is_assignable<TagUInt3, TagUInt3>::value,
|
||||
"TagUInt3 should be assignable with a TagUInt1");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt1, TagUInt3>::value,
|
||||
"TagUInt1 should not be assignable with a TagUInt3");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt3, TagUInt1>::value,
|
||||
"TagUInt3 should not be assignable with a TagUInt1");
|
||||
|
||||
// Check convertibility of tagged_integers
|
||||
static_assert(
|
||||
!std::is_convertible<std::uint32_t, TagUInt1>::value,
|
||||
"std::uint32_t should not be convertible to a TagUInt1");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<std::uint32_t, TagUInt3>::value,
|
||||
"std::uint32_t should not be convertible to a TagUInt3");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<std::uint64_t, TagUInt3>::value,
|
||||
"std::uint64_t should not be convertible to a TagUInt3");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<std::uint64_t, TagUInt2>::value,
|
||||
"std::uint64_t should not be convertible to a TagUInt2");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<TagUInt1, TagUInt2>::value,
|
||||
"TagUInt1 should not be convertible to TagUInt2");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<TagUInt1, TagUInt3>::value,
|
||||
"TagUInt1 should not be convertible to TagUInt3");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<TagUInt2, TagUInt3>::value,
|
||||
"TagUInt2 should not be convertible to a TagUInt3");
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
using TagInt = tagged_integer<std::int32_t, Tag1>;
|
||||
|
||||
{
|
||||
testcase("Comparison Operators");
|
||||
|
||||
TagInt const zero(0);
|
||||
TagInt const one(1);
|
||||
|
||||
BEAST_EXPECT(one == one);
|
||||
BEAST_EXPECT(!(one == zero));
|
||||
|
||||
BEAST_EXPECT(one != zero);
|
||||
BEAST_EXPECT(!(one != one));
|
||||
|
||||
BEAST_EXPECT(zero < one);
|
||||
BEAST_EXPECT(!(one < zero));
|
||||
|
||||
BEAST_EXPECT(one > zero);
|
||||
BEAST_EXPECT(!(zero > one));
|
||||
|
||||
BEAST_EXPECT(one >= one);
|
||||
BEAST_EXPECT(one >= zero);
|
||||
BEAST_EXPECT(!(zero >= one));
|
||||
|
||||
BEAST_EXPECT(zero <= one);
|
||||
BEAST_EXPECT(zero <= zero);
|
||||
BEAST_EXPECT(!(one <= zero));
|
||||
}
|
||||
|
||||
{
|
||||
testcase("Increment/Decrement Operators");
|
||||
TagInt const zero(0);
|
||||
TagInt const one(1);
|
||||
TagInt a{0};
|
||||
++a;
|
||||
BEAST_EXPECT(a == one);
|
||||
--a;
|
||||
BEAST_EXPECT(a == zero);
|
||||
a++;
|
||||
BEAST_EXPECT(a == one);
|
||||
a--;
|
||||
BEAST_EXPECT(a == zero);
|
||||
}
|
||||
|
||||
{
|
||||
testcase("Arithmetic Operators");
|
||||
TagInt a{-2};
|
||||
BEAST_EXPECT(+a == TagInt{-2});
|
||||
BEAST_EXPECT(-a == TagInt{2});
|
||||
BEAST_EXPECT(TagInt{-3} + TagInt{4} == TagInt{1});
|
||||
BEAST_EXPECT(TagInt{-3} - TagInt{4} == TagInt{-7});
|
||||
BEAST_EXPECT(TagInt{-3} * TagInt{4} == TagInt{-12});
|
||||
BEAST_EXPECT(TagInt{8} / TagInt{4} == TagInt{2});
|
||||
BEAST_EXPECT(TagInt{7} % TagInt{4} == TagInt{3});
|
||||
|
||||
BEAST_EXPECT(~TagInt{8} == TagInt{~TagInt::value_type{8}});
|
||||
BEAST_EXPECT((TagInt{6} & TagInt{3}) == TagInt{2});
|
||||
BEAST_EXPECT((TagInt{6} | TagInt{3}) == TagInt{7});
|
||||
BEAST_EXPECT((TagInt{6} ^ TagInt{3}) == TagInt{5});
|
||||
|
||||
BEAST_EXPECT((TagInt{4} << TagInt{2}) == TagInt{16});
|
||||
BEAST_EXPECT((TagInt{16} >> TagInt{2}) == TagInt{4});
|
||||
}
|
||||
{
|
||||
testcase("Assignment Operators");
|
||||
TagInt a{-2};
|
||||
TagInt b{0};
|
||||
b = a;
|
||||
BEAST_EXPECT(b == TagInt{-2});
|
||||
|
||||
// -3 + 4 == 1
|
||||
a = TagInt{-3};
|
||||
a += TagInt{4};
|
||||
BEAST_EXPECT(a == TagInt{1});
|
||||
|
||||
// -3 - 4 == -7
|
||||
a = TagInt{-3};
|
||||
a -= TagInt{4};
|
||||
BEAST_EXPECT(a == TagInt{-7});
|
||||
|
||||
// -3 * 4 == -12
|
||||
a = TagInt{-3};
|
||||
a *= TagInt{4};
|
||||
BEAST_EXPECT(a == TagInt{-12});
|
||||
|
||||
// 8/4 == 2
|
||||
a = TagInt{8};
|
||||
a /= TagInt{4};
|
||||
BEAST_EXPECT(a == TagInt{2});
|
||||
|
||||
// 7 % 4 == 3
|
||||
a = TagInt{7};
|
||||
a %= TagInt{4};
|
||||
BEAST_EXPECT(a == TagInt{3});
|
||||
|
||||
// 6 & 3 == 2
|
||||
a = TagInt{6};
|
||||
a /= TagInt{3};
|
||||
BEAST_EXPECT(a == TagInt{2});
|
||||
|
||||
// 6 | 3 == 7
|
||||
a = TagInt{6};
|
||||
a |= TagInt{3};
|
||||
BEAST_EXPECT(a == TagInt{7});
|
||||
|
||||
// 6 ^ 3 == 5
|
||||
a = TagInt{6};
|
||||
a ^= TagInt{3};
|
||||
BEAST_EXPECT(a == TagInt{5});
|
||||
|
||||
// 4 << 2 == 16
|
||||
a = TagInt{4};
|
||||
a <<= TagInt{2};
|
||||
BEAST_EXPECT(a == TagInt{16});
|
||||
|
||||
// 16 >> 2 == 4
|
||||
a = TagInt{16};
|
||||
a >>= TagInt{2};
|
||||
BEAST_EXPECT(a == TagInt{4});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(tagged_integer, ripple_basics, ripple);
|
||||
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
@@ -139,7 +139,8 @@ class Feature_test : public beast::unit_test::suite
|
||||
|
||||
// Test a random sampling of the variables. If any of these get retired
|
||||
// or removed, swap out for any other feature.
|
||||
BEAST_EXPECT(featureToName(featureOwnerPaysFee) == "OwnerPaysFee");
|
||||
BEAST_EXPECT(
|
||||
featureToName(fixTrustLinesToSelf) == "fixTrustLinesToSelf");
|
||||
BEAST_EXPECT(featureToName(featureFlow) == "Flow");
|
||||
BEAST_EXPECT(featureToName(featureNegativeUNL) == "NegativeUNL");
|
||||
BEAST_EXPECT(featureToName(fix1578) == "fix1578");
|
||||
|
||||
@@ -1354,6 +1354,225 @@ public:
|
||||
}));
|
||||
}
|
||||
|
||||
void
|
||||
testNFToken(FeatureBitset features)
|
||||
{
|
||||
// `nftoken_id` is added for `transaction` stream in the `subscribe`
|
||||
// response for NFTokenMint and NFTokenAcceptOffer.
|
||||
//
|
||||
// `nftoken_ids` is added for `transaction` stream in the `subscribe`
|
||||
// response for NFTokenCancelOffer
|
||||
//
|
||||
// `offer_id` is added for `transaction` stream in the `subscribe`
|
||||
// response for NFTokenCreateOffer
|
||||
//
|
||||
// The values of these fields are dependent on the NFTokenID/OfferID
|
||||
// changed in its corresponding transaction. We want to validate each
|
||||
// response to make sure the synethic fields hold the right values.
|
||||
|
||||
testcase("Test synthetic fields from Subscribe response");
|
||||
|
||||
using namespace test::jtx;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
Account const alice{"alice"};
|
||||
Account const bob{"bob"};
|
||||
Account const broker{"broker"};
|
||||
|
||||
Env env{*this, features};
|
||||
env.fund(XRP(10000), alice, bob, broker);
|
||||
env.close();
|
||||
|
||||
auto wsc = test::makeWSClient(env.app().config());
|
||||
Json::Value stream;
|
||||
stream[jss::streams] = Json::arrayValue;
|
||||
stream[jss::streams].append("transactions");
|
||||
auto jv = wsc->invoke("subscribe", stream);
|
||||
|
||||
// Verify `nftoken_id` value equals to the NFTokenID that was
|
||||
// changed in the most recent NFTokenMint or NFTokenAcceptOffer
|
||||
// transaction
|
||||
auto verifyNFTokenID = [&](uint256 const& actualNftID) {
|
||||
BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
|
||||
uint256 nftID;
|
||||
BEAST_EXPECT(
|
||||
nftID.parseHex(jv[jss::meta][jss::nftoken_id].asString()));
|
||||
return nftID == actualNftID;
|
||||
}));
|
||||
};
|
||||
|
||||
// Verify `nftoken_ids` value equals to the NFTokenIDs that were
|
||||
// changed in the most recent NFTokenCancelOffer transaction
|
||||
auto verifyNFTokenIDsInCancelOffer =
|
||||
[&](std::vector<uint256> actualNftIDs) {
|
||||
BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
|
||||
std::vector<uint256> metaIDs;
|
||||
std::transform(
|
||||
jv[jss::meta][jss::nftoken_ids].begin(),
|
||||
jv[jss::meta][jss::nftoken_ids].end(),
|
||||
std::back_inserter(metaIDs),
|
||||
[this](Json::Value id) {
|
||||
uint256 nftID;
|
||||
BEAST_EXPECT(nftID.parseHex(id.asString()));
|
||||
return nftID;
|
||||
});
|
||||
// Sort both array to prepare for comparison
|
||||
std::sort(metaIDs.begin(), metaIDs.end());
|
||||
std::sort(actualNftIDs.begin(), actualNftIDs.end());
|
||||
|
||||
// Make sure the expect number of NFTs is correct
|
||||
BEAST_EXPECT(metaIDs.size() == actualNftIDs.size());
|
||||
|
||||
// Check the value of NFT ID in the meta with the
|
||||
// actual values
|
||||
for (size_t i = 0; i < metaIDs.size(); ++i)
|
||||
BEAST_EXPECT(metaIDs[i] == actualNftIDs[i]);
|
||||
return true;
|
||||
}));
|
||||
};
|
||||
|
||||
// Verify `offer_id` value equals to the offerID that was
|
||||
// changed in the most recent NFTokenCreateOffer tx
|
||||
auto verifyNFTokenOfferID = [&](uint256 const& offerID) {
|
||||
BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
|
||||
uint256 metaOfferID;
|
||||
BEAST_EXPECT(metaOfferID.parseHex(
|
||||
jv[jss::meta][jss::offer_id].asString()));
|
||||
return metaOfferID == offerID;
|
||||
}));
|
||||
};
|
||||
|
||||
// Check new fields in tx meta when for all NFTtransactions
|
||||
{
|
||||
// Alice mints 2 NFTs
|
||||
// Verify the NFTokenIDs are correct in the NFTokenMint tx meta
|
||||
uint256 const nftId1{
|
||||
token::getNextID(env, alice, 0u, tfTransferable)};
|
||||
env(token::mint(alice, 0u), txflags(tfTransferable));
|
||||
env.close();
|
||||
verifyNFTokenID(nftId1);
|
||||
|
||||
uint256 const nftId2{
|
||||
token::getNextID(env, alice, 0u, tfTransferable)};
|
||||
env(token::mint(alice, 0u), txflags(tfTransferable));
|
||||
env.close();
|
||||
verifyNFTokenID(nftId2);
|
||||
|
||||
// Alice creates one sell offer for each NFT
|
||||
// Verify the offer indexes are correct in the NFTokenCreateOffer tx
|
||||
// meta
|
||||
uint256 const aliceOfferIndex1 =
|
||||
keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::createOffer(alice, nftId1, drops(1)),
|
||||
txflags(tfSellNFToken));
|
||||
env.close();
|
||||
verifyNFTokenOfferID(aliceOfferIndex1);
|
||||
|
||||
uint256 const aliceOfferIndex2 =
|
||||
keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::createOffer(alice, nftId2, drops(1)),
|
||||
txflags(tfSellNFToken));
|
||||
env.close();
|
||||
verifyNFTokenOfferID(aliceOfferIndex2);
|
||||
|
||||
// Alice cancels two offers she created
|
||||
// Verify the NFTokenIDs are correct in the NFTokenCancelOffer tx
|
||||
// meta
|
||||
env(token::cancelOffer(
|
||||
alice, {aliceOfferIndex1, aliceOfferIndex2}));
|
||||
env.close();
|
||||
verifyNFTokenIDsInCancelOffer({nftId1, nftId2});
|
||||
|
||||
// Bobs creates a buy offer for nftId1
|
||||
// Verify the offer id is correct in the NFTokenCreateOffer tx meta
|
||||
auto const bobBuyOfferIndex =
|
||||
keylet::nftoffer(bob, env.seq(bob)).key;
|
||||
env(token::createOffer(bob, nftId1, drops(1)), token::owner(alice));
|
||||
env.close();
|
||||
verifyNFTokenOfferID(bobBuyOfferIndex);
|
||||
|
||||
// Alice accepts bob's buy offer
|
||||
// Verify the NFTokenID is correct in the NFTokenAcceptOffer tx meta
|
||||
env(token::acceptBuyOffer(alice, bobBuyOfferIndex));
|
||||
env.close();
|
||||
verifyNFTokenID(nftId1);
|
||||
}
|
||||
|
||||
// Check `nftoken_ids` in brokered mode
|
||||
{
|
||||
// Alice mints a NFT
|
||||
uint256 const nftId{
|
||||
token::getNextID(env, alice, 0u, tfTransferable)};
|
||||
env(token::mint(alice, 0u), txflags(tfTransferable));
|
||||
env.close();
|
||||
verifyNFTokenID(nftId);
|
||||
|
||||
// Alice creates sell offer and set broker as destination
|
||||
uint256 const offerAliceToBroker =
|
||||
keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::createOffer(alice, nftId, drops(1)),
|
||||
token::destination(broker),
|
||||
txflags(tfSellNFToken));
|
||||
env.close();
|
||||
verifyNFTokenOfferID(offerAliceToBroker);
|
||||
|
||||
// Bob creates buy offer
|
||||
uint256 const offerBobToBroker =
|
||||
keylet::nftoffer(bob, env.seq(bob)).key;
|
||||
env(token::createOffer(bob, nftId, drops(1)), token::owner(alice));
|
||||
env.close();
|
||||
verifyNFTokenOfferID(offerBobToBroker);
|
||||
|
||||
// Check NFTokenID meta for NFTokenAcceptOffer in brokered mode
|
||||
env(token::brokerOffers(
|
||||
broker, offerBobToBroker, offerAliceToBroker));
|
||||
env.close();
|
||||
verifyNFTokenID(nftId);
|
||||
}
|
||||
|
||||
// Check if there are no duplicate nft id in Cancel transactions where
|
||||
// multiple offers are cancelled for the same NFT
|
||||
{
|
||||
// Alice mints a NFT
|
||||
uint256 const nftId{
|
||||
token::getNextID(env, alice, 0u, tfTransferable)};
|
||||
env(token::mint(alice, 0u), txflags(tfTransferable));
|
||||
env.close();
|
||||
verifyNFTokenID(nftId);
|
||||
|
||||
// Alice creates 2 sell offers for the same NFT
|
||||
uint256 const aliceOfferIndex1 =
|
||||
keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::createOffer(alice, nftId, drops(1)),
|
||||
txflags(tfSellNFToken));
|
||||
env.close();
|
||||
verifyNFTokenOfferID(aliceOfferIndex1);
|
||||
|
||||
uint256 const aliceOfferIndex2 =
|
||||
keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::createOffer(alice, nftId, drops(1)),
|
||||
txflags(tfSellNFToken));
|
||||
env.close();
|
||||
verifyNFTokenOfferID(aliceOfferIndex2);
|
||||
|
||||
// Make sure the metadata only has 1 nft id, since both offers are
|
||||
// for the same nft
|
||||
env(token::cancelOffer(
|
||||
alice, {aliceOfferIndex1, aliceOfferIndex2}));
|
||||
env.close();
|
||||
verifyNFTokenIDsInCancelOffer({nftId});
|
||||
}
|
||||
|
||||
if (features[featureNFTokenMintOffer])
|
||||
{
|
||||
uint256 const aliceMintWithOfferIndex1 =
|
||||
keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::mint(alice), token::amount(XRP(0)));
|
||||
env.close();
|
||||
verifyNFTokenOfferID(aliceMintWithOfferIndex1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
@@ -1373,6 +1592,8 @@ public:
|
||||
testSubByUrl();
|
||||
testHistoryTxStream();
|
||||
testSubBookChanges();
|
||||
testNFToken(all);
|
||||
testNFToken(all - featureNFTokenMintOffer);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
4
src/tests/README.md
Normal file
4
src/tests/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Unit tests
|
||||
This directory contains unit tests for the project. The difference from existing `src/test` folder
|
||||
is that we switch to 3rd party testing framework (doctest). We intend to gradually move existing tests
|
||||
from our own framework to doctest and such tests will be moved to this new folder.
|
||||
14
src/tests/libxrpl/CMakeLists.txt
Normal file
14
src/tests/libxrpl/CMakeLists.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
include(xrpl_add_test)
|
||||
|
||||
# Test requirements.
|
||||
find_package(doctest REQUIRED)
|
||||
|
||||
# Common library dependencies for the rest of the tests.
|
||||
add_library(xrpl.imports.test INTERFACE)
|
||||
target_link_libraries(xrpl.imports.test INTERFACE doctest::doctest xrpl.libxrpl)
|
||||
|
||||
# One test for each module.
|
||||
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)
|
||||
129
src/tests/libxrpl/basics/RangeSet.cpp
Normal file
129
src/tests/libxrpl/basics/RangeSet.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012 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/RangeSet.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
using namespace ripple;
|
||||
|
||||
TEST_SUITE_BEGIN("RangeSet");
|
||||
|
||||
TEST_CASE("prevMissing")
|
||||
{
|
||||
// Set will include:
|
||||
// [ 0, 5]
|
||||
// [10,15]
|
||||
// [20,25]
|
||||
// etc...
|
||||
|
||||
RangeSet<std::uint32_t> set;
|
||||
for (std::uint32_t i = 0; i < 10; ++i)
|
||||
set.insert(range(10 * i, 10 * i + 5));
|
||||
|
||||
for (std::uint32_t i = 1; i < 100; ++i)
|
||||
{
|
||||
std::optional<std::uint32_t> expected;
|
||||
// no prev missing in domain for i <= 6
|
||||
if (i > 6)
|
||||
{
|
||||
std::uint32_t const oneBelowRange = (10 * (i / 10)) - 1;
|
||||
|
||||
expected = ((i % 10) > 6) ? (i - 1) : oneBelowRange;
|
||||
}
|
||||
CHECK(prevMissing(set, i) == expected);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("toString")
|
||||
{
|
||||
RangeSet<std::uint32_t> set;
|
||||
CHECK(to_string(set) == "empty");
|
||||
|
||||
set.insert(1);
|
||||
CHECK(to_string(set) == "1");
|
||||
|
||||
set.insert(range(4u, 6u));
|
||||
CHECK(to_string(set) == "1,4-6");
|
||||
|
||||
set.insert(2);
|
||||
CHECK(to_string(set) == "1-2,4-6");
|
||||
|
||||
set.erase(range(4u, 5u));
|
||||
CHECK(to_string(set) == "1-2,6");
|
||||
}
|
||||
|
||||
TEST_CASE("fromString")
|
||||
{
|
||||
RangeSet<std::uint32_t> set;
|
||||
|
||||
CHECK(!from_string(set, ""));
|
||||
CHECK(boost::icl::length(set) == 0);
|
||||
|
||||
CHECK(!from_string(set, "#"));
|
||||
CHECK(boost::icl::length(set) == 0);
|
||||
|
||||
CHECK(!from_string(set, ","));
|
||||
CHECK(boost::icl::length(set) == 0);
|
||||
|
||||
CHECK(!from_string(set, ",-"));
|
||||
CHECK(boost::icl::length(set) == 0);
|
||||
|
||||
CHECK(!from_string(set, "1,,2"));
|
||||
CHECK(boost::icl::length(set) == 0);
|
||||
|
||||
CHECK(from_string(set, "1"));
|
||||
CHECK(boost::icl::length(set) == 1);
|
||||
CHECK(boost::icl::first(set) == 1);
|
||||
|
||||
CHECK(from_string(set, "1,1"));
|
||||
CHECK(boost::icl::length(set) == 1);
|
||||
CHECK(boost::icl::first(set) == 1);
|
||||
|
||||
CHECK(from_string(set, "1-1"));
|
||||
CHECK(boost::icl::length(set) == 1);
|
||||
CHECK(boost::icl::first(set) == 1);
|
||||
|
||||
CHECK(from_string(set, "1,4-6"));
|
||||
CHECK(boost::icl::length(set) == 4);
|
||||
CHECK(boost::icl::first(set) == 1);
|
||||
CHECK(!boost::icl::contains(set, 2));
|
||||
CHECK(!boost::icl::contains(set, 3));
|
||||
CHECK(boost::icl::contains(set, 4));
|
||||
CHECK(boost::icl::contains(set, 5));
|
||||
CHECK(boost::icl::last(set) == 6);
|
||||
|
||||
CHECK(from_string(set, "1-2,4-6"));
|
||||
CHECK(boost::icl::length(set) == 5);
|
||||
CHECK(boost::icl::first(set) == 1);
|
||||
CHECK(boost::icl::contains(set, 2));
|
||||
CHECK(boost::icl::contains(set, 4));
|
||||
CHECK(boost::icl::last(set) == 6);
|
||||
|
||||
CHECK(from_string(set, "1-2,6"));
|
||||
CHECK(boost::icl::length(set) == 3);
|
||||
CHECK(boost::icl::first(set) == 1);
|
||||
CHECK(boost::icl::contains(set, 2));
|
||||
CHECK(boost::icl::last(set) == 6);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
105
src/tests/libxrpl/basics/Slice.cpp
Normal file
105
src/tests/libxrpl/basics/Slice.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012 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/Slice.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
using namespace ripple;
|
||||
|
||||
static std::uint8_t const data[] = {
|
||||
0xa8, 0xa1, 0x38, 0x45, 0x23, 0xec, 0xe4, 0x23, 0x71, 0x6d, 0x2a,
|
||||
0x18, 0xb4, 0x70, 0xcb, 0xf5, 0xac, 0x2d, 0x89, 0x4d, 0x19, 0x9c,
|
||||
0xf0, 0x2c, 0x15, 0xd1, 0xf9, 0x9b, 0x66, 0xd2, 0x30, 0xd3};
|
||||
|
||||
TEST_SUITE_BEGIN("Slice");
|
||||
|
||||
TEST_CASE("equality & inequality")
|
||||
{
|
||||
Slice const s0{};
|
||||
|
||||
CHECK(s0.size() == 0);
|
||||
CHECK(s0.data() == nullptr);
|
||||
CHECK(s0 == s0);
|
||||
|
||||
// Test slices of equal and unequal size pointing to same data:
|
||||
for (std::size_t i = 0; i != sizeof(data); ++i)
|
||||
{
|
||||
Slice const s1{data, i};
|
||||
|
||||
CHECK(s1.size() == i);
|
||||
CHECK(s1.data() != nullptr);
|
||||
|
||||
if (i == 0)
|
||||
CHECK(s1 == s0);
|
||||
else
|
||||
CHECK(s1 != s0);
|
||||
|
||||
for (std::size_t j = 0; j != sizeof(data); ++j)
|
||||
{
|
||||
Slice const s2{data, j};
|
||||
|
||||
if (i == j)
|
||||
CHECK(s1 == s2);
|
||||
else
|
||||
CHECK(s1 != s2);
|
||||
}
|
||||
}
|
||||
|
||||
// Test slices of equal size but pointing to different data:
|
||||
std::array<std::uint8_t, sizeof(data)> a;
|
||||
std::array<std::uint8_t, sizeof(data)> b;
|
||||
|
||||
for (std::size_t i = 0; i != sizeof(data); ++i)
|
||||
a[i] = b[i] = data[i];
|
||||
|
||||
CHECK(makeSlice(a) == makeSlice(b));
|
||||
b[7]++;
|
||||
CHECK(makeSlice(a) != makeSlice(b));
|
||||
a[7]++;
|
||||
CHECK(makeSlice(a) == makeSlice(b));
|
||||
}
|
||||
|
||||
TEST_CASE("indexing")
|
||||
{
|
||||
Slice const s{data, sizeof(data)};
|
||||
|
||||
for (std::size_t i = 0; i != sizeof(data); ++i)
|
||||
CHECK(s[i] == data[i]);
|
||||
}
|
||||
|
||||
TEST_CASE("advancing")
|
||||
{
|
||||
for (std::size_t i = 0; i < sizeof(data); ++i)
|
||||
{
|
||||
for (std::size_t j = 0; i + j < sizeof(data); ++j)
|
||||
{
|
||||
Slice s(data + i, sizeof(data) - i);
|
||||
s += j;
|
||||
|
||||
CHECK(s.data() == data + i + j);
|
||||
CHECK(s.size() == sizeof(data) - i - j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
67
src/tests/libxrpl/basics/base64.cpp
Normal file
67
src/tests/libxrpl/basics/base64.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012 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/base64.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace ripple;
|
||||
|
||||
static void
|
||||
check(std::string const& in, std::string const& out)
|
||||
{
|
||||
auto const encoded = base64_encode(in);
|
||||
CHECK(encoded == out);
|
||||
CHECK(base64_decode(encoded) == in);
|
||||
}
|
||||
|
||||
TEST_CASE("base64")
|
||||
{
|
||||
check("", "");
|
||||
check("f", "Zg==");
|
||||
check("fo", "Zm8=");
|
||||
check("foo", "Zm9v");
|
||||
check("foob", "Zm9vYg==");
|
||||
check("fooba", "Zm9vYmE=");
|
||||
check("foobar", "Zm9vYmFy");
|
||||
|
||||
check(
|
||||
"Man is distinguished, not only by his reason, but by this "
|
||||
"singular passion from "
|
||||
"other animals, which is a lust of the mind, that by a "
|
||||
"perseverance of delight "
|
||||
"in the continued and indefatigable generation of knowledge, "
|
||||
"exceeds the short "
|
||||
"vehemence of any carnal pleasure.",
|
||||
"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dC"
|
||||
"BieSB0aGlz"
|
||||
"IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIG"
|
||||
"x1c3Qgb2Yg"
|
||||
"dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aG"
|
||||
"UgY29udGlu"
|
||||
"dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleG"
|
||||
"NlZWRzIHRo"
|
||||
"ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=");
|
||||
|
||||
std::string const notBase64 = "not_base64!!";
|
||||
std::string const truncated = "not";
|
||||
CHECK(base64_decode(notBase64) == base64_decode(truncated));
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
Copyright (c) 2012 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
|
||||
@@ -18,46 +18,39 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace ripple {
|
||||
using namespace ripple;
|
||||
|
||||
class contract_test : public beast::unit_test::suite
|
||||
TEST_CASE("contract")
|
||||
{
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
try
|
||||
{
|
||||
Throw<std::runtime_error>("Throw test");
|
||||
}
|
||||
catch (std::runtime_error const& e1)
|
||||
{
|
||||
CHECK(std::string(e1.what()) == "Throw test");
|
||||
|
||||
try
|
||||
{
|
||||
Throw<std::runtime_error>("Throw test");
|
||||
Rethrow();
|
||||
}
|
||||
catch (std::runtime_error const& e1)
|
||||
catch (std::runtime_error const& e2)
|
||||
{
|
||||
BEAST_EXPECT(std::string(e1.what()) == "Throw test");
|
||||
|
||||
try
|
||||
{
|
||||
Rethrow();
|
||||
}
|
||||
catch (std::runtime_error const& e2)
|
||||
{
|
||||
BEAST_EXPECT(std::string(e2.what()) == "Throw test");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
BEAST_EXPECT(false);
|
||||
}
|
||||
CHECK(std::string(e2.what()) == "Throw test");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
BEAST_EXPECT(false);
|
||||
CHECK(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(contract, basics, ripple);
|
||||
|
||||
} // namespace ripple
|
||||
catch (...)
|
||||
{
|
||||
CHECK(false);
|
||||
}
|
||||
}
|
||||
2
src/tests/libxrpl/basics/main.cpp
Normal file
2
src/tests/libxrpl/basics/main.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include <doctest/doctest.h>
|
||||
64
src/tests/libxrpl/basics/mulDiv.cpp
Normal file
64
src/tests/libxrpl/basics/mulDiv.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012 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/mulDiv.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
using namespace ripple;
|
||||
|
||||
TEST_CASE("mulDiv")
|
||||
{
|
||||
auto const max = std::numeric_limits<std::uint64_t>::max();
|
||||
std::uint64_t const max32 = std::numeric_limits<std::uint32_t>::max();
|
||||
|
||||
auto result = mulDiv(85, 20, 5);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 340);
|
||||
result = mulDiv(20, 85, 5);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 340);
|
||||
|
||||
result = mulDiv(0, max - 1, max - 3);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 0);
|
||||
result = mulDiv(max - 1, 0, max - 3);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 0);
|
||||
|
||||
result = mulDiv(max, 2, max / 2);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 4);
|
||||
result = mulDiv(max, 1000, max / 1000);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 1000000);
|
||||
result = mulDiv(max, 1000, max / 1001);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 1001000);
|
||||
result = mulDiv(max32 + 1, max32 + 1, 5);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 3689348814741910323);
|
||||
|
||||
// Overflow
|
||||
result = mulDiv(max - 1, max - 2, 5);
|
||||
CHECK(!result);
|
||||
}
|
||||
174
src/tests/libxrpl/basics/scope.cpp
Normal file
174
src/tests/libxrpl/basics/scope.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2021 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/scope.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
using namespace ripple;
|
||||
|
||||
TEST_CASE("scope_exit")
|
||||
{
|
||||
// scope_exit always executes the functor on destruction,
|
||||
// unless release() is called
|
||||
int i = 0;
|
||||
{
|
||||
scope_exit x{[&i]() { i = 1; }};
|
||||
}
|
||||
CHECK(i == 1);
|
||||
{
|
||||
scope_exit x{[&i]() { i = 2; }};
|
||||
x.release();
|
||||
}
|
||||
CHECK(i == 1);
|
||||
{
|
||||
scope_exit x{[&i]() { i += 2; }};
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
CHECK(i == 3);
|
||||
{
|
||||
scope_exit x{[&i]() { i = 4; }};
|
||||
x.release();
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
CHECK(i == 3);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_exit x{[&i]() { i = 5; }};
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
CHECK(i == 5);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_exit x{[&i]() { i = 6; }};
|
||||
x.release();
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
CHECK(i == 5);
|
||||
}
|
||||
|
||||
TEST_CASE("scope_fail")
|
||||
{
|
||||
// scope_fail executes the functor on destruction only
|
||||
// if an exception is unwinding, unless release() is called
|
||||
int i = 0;
|
||||
{
|
||||
scope_fail x{[&i]() { i = 1; }};
|
||||
}
|
||||
CHECK(i == 0);
|
||||
{
|
||||
scope_fail x{[&i]() { i = 2; }};
|
||||
x.release();
|
||||
}
|
||||
CHECK(i == 0);
|
||||
{
|
||||
scope_fail x{[&i]() { i = 3; }};
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
CHECK(i == 0);
|
||||
{
|
||||
scope_fail x{[&i]() { i = 4; }};
|
||||
x.release();
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
CHECK(i == 0);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_fail x{[&i]() { i = 5; }};
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
CHECK(i == 5);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_fail x{[&i]() { i = 6; }};
|
||||
x.release();
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
CHECK(i == 5);
|
||||
}
|
||||
|
||||
TEST_CASE("scope_success")
|
||||
{
|
||||
// scope_success executes the functor on destruction only
|
||||
// if an exception is not unwinding, unless release() is called
|
||||
int i = 0;
|
||||
{
|
||||
scope_success x{[&i]() { i = 1; }};
|
||||
}
|
||||
CHECK(i == 1);
|
||||
{
|
||||
scope_success x{[&i]() { i = 2; }};
|
||||
x.release();
|
||||
}
|
||||
CHECK(i == 1);
|
||||
{
|
||||
scope_success x{[&i]() { i += 2; }};
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
CHECK(i == 3);
|
||||
{
|
||||
scope_success x{[&i]() { i = 4; }};
|
||||
x.release();
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
CHECK(i == 3);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_success x{[&i]() { i = 5; }};
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
CHECK(i == 3);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_success x{[&i]() { i = 6; }};
|
||||
x.release();
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
CHECK(i == 3);
|
||||
}
|
||||
247
src/tests/libxrpl/basics/tagged_integer.cpp
Normal file
247
src/tests/libxrpl/basics/tagged_integer.cpp
Normal file
@@ -0,0 +1,247 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2014 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/tagged_integer.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
using namespace ripple;
|
||||
|
||||
struct Tag1
|
||||
{
|
||||
};
|
||||
struct Tag2
|
||||
{
|
||||
};
|
||||
|
||||
// Static checks that types are not interoperable
|
||||
|
||||
using TagUInt1 = tagged_integer<std::uint32_t, Tag1>;
|
||||
using TagUInt2 = tagged_integer<std::uint32_t, Tag2>;
|
||||
using TagUInt3 = tagged_integer<std::uint64_t, Tag1>;
|
||||
|
||||
// Check construction of tagged_integers
|
||||
static_assert(
|
||||
std::is_constructible<TagUInt1, std::uint32_t>::value,
|
||||
"TagUInt1 should be constructible using a std::uint32_t");
|
||||
|
||||
static_assert(
|
||||
!std::is_constructible<TagUInt1, std::uint64_t>::value,
|
||||
"TagUInt1 should not be constructible using a std::uint64_t");
|
||||
|
||||
static_assert(
|
||||
std::is_constructible<TagUInt3, std::uint32_t>::value,
|
||||
"TagUInt3 should be constructible using a std::uint32_t");
|
||||
|
||||
static_assert(
|
||||
std::is_constructible<TagUInt3, std::uint64_t>::value,
|
||||
"TagUInt3 should be constructible using a std::uint64_t");
|
||||
|
||||
// Check assignment of tagged_integers
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt1, std::uint32_t>::value,
|
||||
"TagUInt1 should not be assignable with a std::uint32_t");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt1, std::uint64_t>::value,
|
||||
"TagUInt1 should not be assignable with a std::uint64_t");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt3, std::uint32_t>::value,
|
||||
"TagUInt3 should not be assignable with a std::uint32_t");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt3, std::uint64_t>::value,
|
||||
"TagUInt3 should not be assignable with a std::uint64_t");
|
||||
|
||||
static_assert(
|
||||
std::is_assignable<TagUInt1, TagUInt1>::value,
|
||||
"TagUInt1 should be assignable with a TagUInt1");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt1, TagUInt2>::value,
|
||||
"TagUInt1 should not be assignable with a TagUInt2");
|
||||
|
||||
static_assert(
|
||||
std::is_assignable<TagUInt3, TagUInt3>::value,
|
||||
"TagUInt3 should be assignable with a TagUInt1");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt1, TagUInt3>::value,
|
||||
"TagUInt1 should not be assignable with a TagUInt3");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt3, TagUInt1>::value,
|
||||
"TagUInt3 should not be assignable with a TagUInt1");
|
||||
|
||||
// Check convertibility of tagged_integers
|
||||
static_assert(
|
||||
!std::is_convertible<std::uint32_t, TagUInt1>::value,
|
||||
"std::uint32_t should not be convertible to a TagUInt1");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<std::uint32_t, TagUInt3>::value,
|
||||
"std::uint32_t should not be convertible to a TagUInt3");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<std::uint64_t, TagUInt3>::value,
|
||||
"std::uint64_t should not be convertible to a TagUInt3");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<std::uint64_t, TagUInt2>::value,
|
||||
"std::uint64_t should not be convertible to a TagUInt2");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<TagUInt1, TagUInt2>::value,
|
||||
"TagUInt1 should not be convertible to TagUInt2");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<TagUInt1, TagUInt3>::value,
|
||||
"TagUInt1 should not be convertible to TagUInt3");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<TagUInt2, TagUInt3>::value,
|
||||
"TagUInt2 should not be convertible to a TagUInt3");
|
||||
|
||||
TEST_SUITE_BEGIN("tagged_integer");
|
||||
|
||||
using TagInt = tagged_integer<std::int32_t, Tag1>;
|
||||
|
||||
TEST_CASE("comparison operators")
|
||||
{
|
||||
TagInt const zero(0);
|
||||
TagInt const one(1);
|
||||
|
||||
CHECK(one == one);
|
||||
CHECK(!(one == zero));
|
||||
|
||||
CHECK(one != zero);
|
||||
CHECK(!(one != one));
|
||||
|
||||
CHECK(zero < one);
|
||||
CHECK(!(one < zero));
|
||||
|
||||
CHECK(one > zero);
|
||||
CHECK(!(zero > one));
|
||||
|
||||
CHECK(one >= one);
|
||||
CHECK(one >= zero);
|
||||
CHECK(!(zero >= one));
|
||||
|
||||
CHECK(zero <= one);
|
||||
CHECK(zero <= zero);
|
||||
CHECK(!(one <= zero));
|
||||
}
|
||||
|
||||
TEST_CASE("increment / decrement operators")
|
||||
{
|
||||
TagInt const zero(0);
|
||||
TagInt const one(1);
|
||||
TagInt a{0};
|
||||
++a;
|
||||
CHECK(a == one);
|
||||
--a;
|
||||
CHECK(a == zero);
|
||||
a++;
|
||||
CHECK(a == one);
|
||||
a--;
|
||||
CHECK(a == zero);
|
||||
}
|
||||
|
||||
TEST_CASE("arithmetic operators")
|
||||
{
|
||||
TagInt a{-2};
|
||||
CHECK(+a == TagInt{-2});
|
||||
CHECK(-a == TagInt{2});
|
||||
CHECK(TagInt{-3} + TagInt{4} == TagInt{1});
|
||||
CHECK(TagInt{-3} - TagInt{4} == TagInt{-7});
|
||||
CHECK(TagInt{-3} * TagInt{4} == TagInt{-12});
|
||||
CHECK(TagInt{8} / TagInt{4} == TagInt{2});
|
||||
CHECK(TagInt{7} % TagInt{4} == TagInt{3});
|
||||
|
||||
CHECK(~TagInt{8} == TagInt{~TagInt::value_type{8}});
|
||||
CHECK((TagInt{6} & TagInt{3}) == TagInt{2});
|
||||
CHECK((TagInt{6} | TagInt{3}) == TagInt{7});
|
||||
CHECK((TagInt{6} ^ TagInt{3}) == TagInt{5});
|
||||
|
||||
CHECK((TagInt{4} << TagInt{2}) == TagInt{16});
|
||||
CHECK((TagInt{16} >> TagInt{2}) == TagInt{4});
|
||||
}
|
||||
|
||||
TEST_CASE("assignment operators")
|
||||
{
|
||||
TagInt a{-2};
|
||||
TagInt b{0};
|
||||
b = a;
|
||||
CHECK(b == TagInt{-2});
|
||||
|
||||
// -3 + 4 == 1
|
||||
a = TagInt{-3};
|
||||
a += TagInt{4};
|
||||
CHECK(a == TagInt{1});
|
||||
|
||||
// -3 - 4 == -7
|
||||
a = TagInt{-3};
|
||||
a -= TagInt{4};
|
||||
CHECK(a == TagInt{-7});
|
||||
|
||||
// -3 * 4 == -12
|
||||
a = TagInt{-3};
|
||||
a *= TagInt{4};
|
||||
CHECK(a == TagInt{-12});
|
||||
|
||||
// 8/4 == 2
|
||||
a = TagInt{8};
|
||||
a /= TagInt{4};
|
||||
CHECK(a == TagInt{2});
|
||||
|
||||
// 7 % 4 == 3
|
||||
a = TagInt{7};
|
||||
a %= TagInt{4};
|
||||
CHECK(a == TagInt{3});
|
||||
|
||||
// 6 & 3 == 2
|
||||
a = TagInt{6};
|
||||
a /= TagInt{3};
|
||||
CHECK(a == TagInt{2});
|
||||
|
||||
// 6 | 3 == 7
|
||||
a = TagInt{6};
|
||||
a |= TagInt{3};
|
||||
CHECK(a == TagInt{7});
|
||||
|
||||
// 6 ^ 3 == 5
|
||||
a = TagInt{6};
|
||||
a ^= TagInt{3};
|
||||
CHECK(a == TagInt{5});
|
||||
|
||||
// 4 << 2 == 16
|
||||
a = TagInt{4};
|
||||
a <<= TagInt{2};
|
||||
CHECK(a == TagInt{16});
|
||||
|
||||
// 16 >> 2 == 4
|
||||
a = TagInt{16};
|
||||
a >>= TagInt{2};
|
||||
CHECK(a == TagInt{4});
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
@@ -17,44 +17,18 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <test/jtx/Env.h>
|
||||
|
||||
#include <xrpl/beast/utility/temp_dir.h>
|
||||
#include <xrpl/crypto/csprng.h>
|
||||
|
||||
namespace ripple {
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
class CryptoPRNG_test : public beast::unit_test::suite
|
||||
using namespace ripple;
|
||||
|
||||
TEST_CASE("get values")
|
||||
{
|
||||
void
|
||||
testGetValues()
|
||||
{
|
||||
testcase("Get Values");
|
||||
try
|
||||
{
|
||||
auto& engine = crypto_prng();
|
||||
auto rand_val = engine();
|
||||
BEAST_EXPECT(rand_val >= engine.min());
|
||||
BEAST_EXPECT(rand_val <= engine.max());
|
||||
|
||||
uint16_t twoByte{0};
|
||||
engine(&twoByte, sizeof(uint16_t));
|
||||
pass();
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testGetValues();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(CryptoPRNG, core, ripple);
|
||||
|
||||
} // namespace ripple
|
||||
auto& engine = crypto_prng();
|
||||
auto rand_val = engine();
|
||||
CHECK(rand_val >= engine.min());
|
||||
CHECK(rand_val <= engine.max());
|
||||
uint16_t twoByte{0};
|
||||
engine(&twoByte, sizeof(uint16_t));
|
||||
}
|
||||
2
src/tests/libxrpl/crypto/main.cpp
Normal file
2
src/tests/libxrpl/crypto/main.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include <doctest/doctest.h>
|
||||
@@ -63,8 +63,8 @@ LedgerHistory::insert(
|
||||
ledger->stateMap().getHash().isNonZero(),
|
||||
"ripple::LedgerHistory::insert : nonzero hash");
|
||||
|
||||
std::unique_lock sl(m_ledgers_by_hash.peekMutex());
|
||||
|
||||
// TODOL merge the below into a single call to avoid lock and race
|
||||
// conditions, i.e. - return alreadyHad on assignment somehow.
|
||||
bool const alreadyHad = m_ledgers_by_hash.canonicalize_replace_cache(
|
||||
ledger->info().hash, ledger);
|
||||
if (validated)
|
||||
@@ -76,7 +76,7 @@ LedgerHistory::insert(
|
||||
LedgerHash
|
||||
LedgerHistory::getLedgerHash(LedgerIndex index)
|
||||
{
|
||||
std::unique_lock sl(m_ledgers_by_hash.peekMutex());
|
||||
// TODO: is it safe to get iterator without lock here?
|
||||
if (auto it = mLedgersByIndex.find(index); it != mLedgersByIndex.end())
|
||||
return it->second;
|
||||
return {};
|
||||
@@ -86,13 +86,12 @@ std::shared_ptr<Ledger const>
|
||||
LedgerHistory::getLedgerBySeq(LedgerIndex index)
|
||||
{
|
||||
{
|
||||
std::unique_lock sl(m_ledgers_by_hash.peekMutex());
|
||||
// TODO: this lock is not needed
|
||||
auto it = mLedgersByIndex.find(index);
|
||||
|
||||
if (it != mLedgersByIndex.end())
|
||||
{
|
||||
uint256 hash = it->second;
|
||||
sl.unlock();
|
||||
return getLedgerByHash(hash);
|
||||
}
|
||||
}
|
||||
@@ -108,7 +107,8 @@ LedgerHistory::getLedgerBySeq(LedgerIndex index)
|
||||
|
||||
{
|
||||
// Add this ledger to the local tracking by index
|
||||
std::unique_lock sl(m_ledgers_by_hash.peekMutex());
|
||||
// std::unique_lock sl(m_ledgers_by_hash.peekMutex());
|
||||
// TODO: make sure that canonicalize_replace_client lock the partition
|
||||
|
||||
XRPL_ASSERT(
|
||||
ret->isImmutable(),
|
||||
@@ -458,7 +458,8 @@ LedgerHistory::builtLedger(
|
||||
XRPL_ASSERT(
|
||||
!hash.isZero(), "ripple::LedgerHistory::builtLedger : nonzero hash");
|
||||
|
||||
std::unique_lock sl(m_consensus_validated.peekMutex());
|
||||
// std::unique_lock sl(m_consensus_validated.peekMutex());
|
||||
// TODO: make sure that canonicalize_replace_client lock the partition
|
||||
|
||||
auto entry = std::make_shared<cv_entry>();
|
||||
m_consensus_validated.canonicalize_replace_client(index, entry);
|
||||
@@ -500,7 +501,8 @@ LedgerHistory::validatedLedger(
|
||||
!hash.isZero(),
|
||||
"ripple::LedgerHistory::validatedLedger : nonzero hash");
|
||||
|
||||
std::unique_lock sl(m_consensus_validated.peekMutex());
|
||||
// std::unique_lock sl(m_consensus_validated.peekMutex());
|
||||
// TODO: make sure that canonicalize_replace_client lock the partition
|
||||
|
||||
auto entry = std::make_shared<cv_entry>();
|
||||
m_consensus_validated.canonicalize_replace_client(index, entry);
|
||||
@@ -535,7 +537,9 @@ LedgerHistory::validatedLedger(
|
||||
bool
|
||||
LedgerHistory::fixIndex(LedgerIndex ledgerIndex, LedgerHash const& ledgerHash)
|
||||
{
|
||||
std::unique_lock sl(m_ledgers_by_hash.peekMutex());
|
||||
// std::unique_lock sl(m_ledgers_by_hash.peekMutex());
|
||||
// TODO: how to ensure that? "Ensure m_ledgers_by_hash doesn't have the
|
||||
// wrong hash for a particular index"
|
||||
auto it = mLedgersByIndex.find(ledgerIndex);
|
||||
|
||||
if ((it != mLedgersByIndex.end()) && (it->second != ledgerHash))
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
#include <xrpl/protocol/BuildInfo.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/MultiApiJson.h>
|
||||
#include <xrpl/protocol/NFTSyntheticSerializer.h>
|
||||
#include <xrpl/protocol/RPCErr.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
@@ -3258,6 +3259,7 @@ NetworkOPsImp::transJson(
|
||||
jvObj[jss::meta] = meta->get().getJson(JsonOptions::none);
|
||||
RPC::insertDeliveredAmount(
|
||||
jvObj[jss::meta], *ledger, transaction, meta->get());
|
||||
RPC::insertNFTSyntheticInJson(jvObj, transaction, meta->get());
|
||||
RPC::insertMPTokenIssuanceID(
|
||||
jvObj[jss::meta], transaction, meta->get());
|
||||
}
|
||||
|
||||
@@ -95,9 +95,6 @@ RippleCalc::rippleCalculate(
|
||||
return std::nullopt;
|
||||
}();
|
||||
|
||||
bool const ownerPaysTransferFee =
|
||||
view.rules().enabled(featureOwnerPaysFee);
|
||||
|
||||
try
|
||||
{
|
||||
flowOut = flow(
|
||||
@@ -108,7 +105,7 @@ RippleCalc::rippleCalculate(
|
||||
spsPaths,
|
||||
defaultPaths,
|
||||
partialPayment,
|
||||
ownerPaysTransferFee,
|
||||
false,
|
||||
OfferCrossing::no,
|
||||
limitQuality,
|
||||
sendMax,
|
||||
|
||||
@@ -348,7 +348,7 @@ populateJsonResponse(
|
||||
txnMeta->getJson(JsonOptions::include_date);
|
||||
insertDeliveredAmount(
|
||||
jvObj[jss::meta], context, txn, *txnMeta);
|
||||
insertNFTSyntheticInJson(jvObj, sttx, *txnMeta);
|
||||
RPC::insertNFTSyntheticInJson(jvObj, sttx, *txnMeta);
|
||||
RPC::insertMPTokenIssuanceID(
|
||||
jvObj[jss::meta], sttx, *txnMeta);
|
||||
}
|
||||
|
||||
@@ -270,7 +270,7 @@ populateJsonResponse(
|
||||
response[jss::meta] = meta->getJson(JsonOptions::none);
|
||||
insertDeliveredAmount(
|
||||
response[jss::meta], context, result.txn, *meta);
|
||||
insertNFTSyntheticInJson(response, sttx, *meta);
|
||||
RPC::insertNFTSyntheticInJson(response, sttx, *meta);
|
||||
RPC::insertMPTokenIssuanceID(response[jss::meta], sttx, *meta);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user