Compare commits

...

9 Commits

Author SHA1 Message Date
Denis Angell
eaaab2309c Merge branch 'dev' into fix-delivered-amt 2024-06-05 12:17:12 +02:00
Wietse Wind
849a4435e0 CI Split jobs with prev job dependency & CI on jshooks (#320)
* CI on `jshooks` branch

* CI Split jobs with prev job dependency

* No multi branch worker in parallel

---------

Co-authored-by: Denis Angell <dangell@transia.co>
2024-05-29 13:45:59 +02:00
Wietse Wind
247e9d98bf CI on jshooks branch (#317) 2024-05-24 10:10:40 +10:00
Denis Angell
dde03634f8 remove print 2024-05-14 06:37:26 +02:00
Denis Angell
49b766181b update tests 2024-05-14 06:14:40 +02:00
Denis Angell
75ab16e989 fix delivered amount 2024-05-13 16:15:41 +02:00
Denis Angell
acd455f5df Fix: Server Definitions Typo (#306) 2024-04-19 07:39:21 +10:00
Denis Angell
6636e3b6fd Add RPC Tests (#295)
Co-authored-by: RichardAH <richard.holland@starstone.co.nz>
2024-04-18 18:22:46 +10:00
Denis Angell
3c5f118b59 Fix: Add Tx Flags To Server Definitions (#304) 2024-04-18 15:43:48 +10:00
27 changed files with 1021 additions and 379 deletions

View File

@@ -2,25 +2,37 @@ name: Build using Docker
on:
push:
branches: [ "dev", "candidate", "release" ]
branches: [ "dev", "candidate", "release", "jshooks" ]
pull_request:
branches: [ "dev", "candidate", "release" ]
branches: [ "dev", "candidate", "release", "jshooks" ]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
group: ${{ github.workflow }}
cancel-in-progress: false
jobs:
builder:
checkout:
runs-on: [self-hosted, vanity]
steps:
- uses: actions/checkout@v3
with:
clean: false
checkpatterns:
runs-on: [self-hosted, vanity]
needs: checkout
steps:
- name: Check for suspicious patterns
run: /bin/bash suspicious_patterns.sh
build:
runs-on: [self-hosted, vanity]
needs: checkpatterns
steps:
- name: Build using Docker
run: /bin/bash release-builder.sh
tests:
runs-on: [self-hosted, vanity]
needs: build
steps:
- name: Unit tests
run: /bin/bash docker-unit-tests.sh

View File

@@ -983,6 +983,7 @@ if (tests)
src/test/rpc/AccountLinesRPC_test.cpp
src/test/rpc/AccountObjects_test.cpp
src/test/rpc/AccountOffers_test.cpp
src/test/rpc/AccountNamespace_test.cpp
src/test/rpc/AccountSet_test.cpp
src/test/rpc/AccountTx_test.cpp
src/test/rpc/AmendmentBlocked_test.cpp

View File

@@ -52,6 +52,9 @@ Loop: ripple.overlay ripple.rpc
Loop: test.app test.jtx
test.app > test.jtx
Loop: test.app test.rpc
test.rpc == test.app
Loop: test.jtx test.toplevel
test.toplevel > test.jtx

View File

@@ -90,7 +90,6 @@ test.app > ripple.overlay
test.app > ripple.protocol
test.app > ripple.resource
test.app > ripple.rpc
test.app > test.rpc
test.app > test.toplevel
test.app > test.unit_test
test.basics > ripple.basics

View File

@@ -5,11 +5,11 @@
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
// __/ | https://github.com/Neargye/magic_enum
// |___/ version 0.9.3
// |___/ version 0.9.5
//
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT
// Copyright (c) 2019 - 2023 Daniil Goncharov <neargye@gmail.com>.
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -34,7 +34,7 @@
#define MAGIC_ENUM_VERSION_MAJOR 0
#define MAGIC_ENUM_VERSION_MINOR 9
#define MAGIC_ENUM_VERSION_PATCH 3
#define MAGIC_ENUM_VERSION_PATCH 5
#include <array>
#include <cstddef>
@@ -60,7 +60,7 @@
#if defined(MAGIC_ENUM_NO_ASSERT)
# define MAGIC_ENUM_ASSERT(...) static_cast<void>(0)
#else
#elif !defined(MAGIC_ENUM_ASSERT)
# include <cassert>
# define MAGIC_ENUM_ASSERT(...) assert((__VA_ARGS__))
#endif
@@ -69,9 +69,11 @@
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wunknown-warning-option"
# pragma clang diagnostic ignored "-Wenum-constexpr-conversion"
# pragma clang diagnostic ignored "-Wuseless-cast" // suppresses 'static_cast<char_type>('\0')' for char_type = char (common on Linux).
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // May be used uninitialized 'return {};'.
# pragma GCC diagnostic ignored "-Wuseless-cast" // suppresses 'static_cast<char_type>('\0')' for char_type = char (common on Linux).
#elif defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable : 26495) // Variable 'static_str<N>::chars_' is uninitialized.
@@ -164,14 +166,11 @@ namespace customize {
// If need another range for specific enum type, add specialization enum_range for necessary enum type.
template <typename E>
struct enum_range {
static_assert(std::is_enum_v<E>, "magic_enum::customize::enum_range requires enum type.");
static constexpr int min = MAGIC_ENUM_RANGE_MIN;
static constexpr int max = MAGIC_ENUM_RANGE_MAX;
static_assert(max > min, "magic_enum::customize::enum_range requires max > min.");
};
static_assert(MAGIC_ENUM_RANGE_MAX > MAGIC_ENUM_RANGE_MIN, "MAGIC_ENUM_RANGE_MAX must be greater than MAGIC_ENUM_RANGE_MIN.");
static_assert((MAGIC_ENUM_RANGE_MAX - MAGIC_ENUM_RANGE_MIN) < (std::numeric_limits<std::uint16_t>::max)(), "MAGIC_ENUM_RANGE must be less than UINT16_MAX.");
namespace detail {
@@ -216,9 +215,9 @@ namespace detail {
template <typename T>
struct supported
#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED || defined(MAGIC_ENUM_NO_CHECK_SUPPORT)
: std::true_type {};
: std::true_type {};
#else
: std::false_type {};
: std::false_type {};
#endif
template <auto V, typename E = std::decay_t<decltype(V)>, std::enable_if_t<std::is_enum_v<E>, int> = 0>
@@ -423,10 +422,20 @@ constexpr auto n() noexcept {
constexpr auto name_ptr = MAGIC_ENUM_GET_TYPE_NAME_BUILTIN(E);
constexpr auto name = name_ptr ? str_view{name_ptr, std::char_traits<char>::length(name_ptr)} : str_view{};
#elif defined(__clang__)
auto name = str_view{__PRETTY_FUNCTION__ + 34, sizeof(__PRETTY_FUNCTION__) - 36};
str_view name;
if constexpr (sizeof(__PRETTY_FUNCTION__) == sizeof(__FUNCTION__)) {
static_assert(always_false_v<E>, "magic_enum::detail::n requires __PRETTY_FUNCTION__.");
return str_view{};
} else {
name.size_ = sizeof(__PRETTY_FUNCTION__) - 36;
name.str_ = __PRETTY_FUNCTION__ + 34;
}
#elif defined(__GNUC__)
auto name = str_view{__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1};
if (name.str_[name.size_ - 1] == ']') {
if constexpr (sizeof(__PRETTY_FUNCTION__) == sizeof(__FUNCTION__)) {
static_assert(always_false_v<E>, "magic_enum::detail::n requires __PRETTY_FUNCTION__.");
return str_view{};
} else if (name.str_[name.size_ - 1] == ']') {
name.size_ -= 50;
name.str_ += 49;
} else {
@@ -489,7 +498,14 @@ constexpr auto n() noexcept {
constexpr auto name_ptr = MAGIC_ENUM_GET_ENUM_NAME_BUILTIN(V);
auto name = name_ptr ? str_view{name_ptr, std::char_traits<char>::length(name_ptr)} : str_view{};
#elif defined(__clang__)
auto name = str_view{__PRETTY_FUNCTION__ + 34, sizeof(__PRETTY_FUNCTION__) - 36};
str_view name;
if constexpr (sizeof(__PRETTY_FUNCTION__) == sizeof(__FUNCTION__)) {
static_assert(always_false_v<decltype(V)>, "magic_enum::detail::n requires __PRETTY_FUNCTION__.");
return str_view{};
} else {
name.size_ = sizeof(__PRETTY_FUNCTION__) - 36;
name.str_ = __PRETTY_FUNCTION__ + 34;
}
if (name.size_ > 22 && name.str_[0] == '(' && name.str_[1] == 'a' && name.str_[10] == ' ' && name.str_[22] == ':') {
name.size_ -= 23;
name.str_ += 23;
@@ -499,7 +515,10 @@ constexpr auto n() noexcept {
}
#elif defined(__GNUC__)
auto name = str_view{__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1};
if (name.str_[name.size_ - 1] == ']') {
if constexpr (sizeof(__PRETTY_FUNCTION__) == sizeof(__FUNCTION__)) {
static_assert(always_false_v<decltype(V)>, "magic_enum::detail::n requires __PRETTY_FUNCTION__.");
return str_view{};
} else if (name.str_[name.size_ - 1] == ']') {
name.size_ -= 55;
name.str_ += 54;
} else {
@@ -698,7 +717,7 @@ constexpr void valid_count(bool* valid, std::size_t& count) noexcept {
} \
}
MAGIC_ENUM_FOR_EACH_256(MAGIC_ENUM_V);
MAGIC_ENUM_FOR_EACH_256(MAGIC_ENUM_V)
if constexpr ((I + 256) < Size) {
valid_count<E, S, Size, Min, I + 256>(valid, count);
@@ -750,7 +769,6 @@ constexpr auto values() noexcept {
constexpr auto max = reflected_max<E, S>();
constexpr auto range_size = max - min + 1;
static_assert(range_size > 0, "magic_enum::enum_range requires valid size.");
static_assert(range_size < (std::numeric_limits<std::uint16_t>::max)(), "magic_enum::enum_range requires valid size.");
return values<E, S, range_size, min>();
}
@@ -807,7 +825,8 @@ inline constexpr auto max_v = (count_v<E, S> > 0) ? static_cast<U>(values_v<E, S
template <typename E, enum_subtype S, std::size_t... I>
constexpr auto names(std::index_sequence<I...>) noexcept {
return std::array<string_view, sizeof...(I)>{{enum_name_v<E, values_v<E, S>[I]>...}};
constexpr auto names = std::array<string_view, sizeof...(I)>{{enum_name_v<E, values_v<E, S>[I]>...}};
return names;
}
template <typename E, enum_subtype S>
@@ -818,7 +837,8 @@ using names_t = decltype((names_v<D, S>));
template <typename E, enum_subtype S, std::size_t... I>
constexpr auto entries(std::index_sequence<I...>) noexcept {
return std::array<std::pair<E, string_view>, sizeof...(I)>{{{values_v<E, S>[I], enum_name_v<E, values_v<E, S>[I]>}...}};
constexpr auto entries = std::array<std::pair<E, string_view>, sizeof...(I)>{{{values_v<E, S>[I], enum_name_v<E, values_v<E, S>[I]>}...}};
return entries;
}
template <typename E, enum_subtype S>
@@ -845,17 +865,16 @@ constexpr bool is_sparse() noexcept {
template <typename E, enum_subtype S = subtype_v<E>>
inline constexpr bool is_sparse_v = is_sparse<E, S>();
template <typename E, enum_subtype S, typename U = std::underlying_type_t<E>>
constexpr U values_ors() noexcept {
static_assert(S == enum_subtype::flags, "magic_enum::detail::values_ors requires valid subtype.");
template <typename E, enum_subtype S>
struct is_reflected
#if defined(MAGIC_ENUM_NO_CHECK_REFLECTED_ENUM)
: std::true_type {};
#else
: std::bool_constant<std::is_enum_v<E> && (count_v<E, S> != 0)> {};
#endif
auto ors = U{0};
for (std::size_t i = 0; i < count_v<E, S>; ++i) {
ors |= static_cast<U>(values_v<E, S>[i]);
}
return ors;
}
template <typename E, enum_subtype S>
inline constexpr bool is_reflected_v = is_reflected<std::decay_t<E>, S>{};
template <bool, typename R>
struct enable_if_enum {};
@@ -1156,6 +1175,7 @@ template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_t<E, std::decay_t<E>> {
using D = std::decay_t<E>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
if constexpr (detail::is_sparse_v<D, S>) {
return MAGIC_ENUM_ASSERT(index < detail::count_v<D, S>), detail::values_v<D, S>[index];
@@ -1170,6 +1190,7 @@ template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
template <typename E, std::size_t I, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_value() noexcept -> detail::enable_if_t<E, std::decay_t<E>> {
using D = std::decay_t<E>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
static_assert(I < detail::count_v<D, S>, "magic_enum::enum_value out of range.");
return enum_value<D, S>(I);
@@ -1178,7 +1199,10 @@ template <typename E, std::size_t I, detail::enum_subtype S = detail::subtype_v<
// Returns std::array with enum values, sorted by enum value.
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_t<E, detail::values_t<E, S>> {
return detail::values_v<std::decay_t<E>, S>;
using D = std::decay_t<E>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
return detail::values_v<D, S>;
}
// Returns integer value from enum value.
@@ -1199,11 +1223,9 @@ template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_t<E, optional<std::size_t>> {
using D = std::decay_t<E>;
using U = underlying_type_t<D>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
if constexpr (detail::count_v<D, S> == 0) {
static_cast<void>(value);
return {}; // Empty enum.
} else if constexpr (detail::is_sparse_v<D, S> || (S == detail::enum_subtype::flags)) {
if constexpr (detail::is_sparse_v<D, S> || (S == detail::enum_subtype::flags)) {
#if defined(MAGIC_ENUM_ENABLE_HASH)
return detail::constexpr_switch<&detail::values_v<D, S>, detail::case_call_t::index>(
[](std::size_t i) { return optional<std::size_t>{i}; },
@@ -1231,14 +1253,17 @@ template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
template <detail::enum_subtype S, typename E>
[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_t<E, optional<std::size_t>> {
using D = std::decay_t<E>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
return enum_index<D, S>(value);
}
// Obtains index in enum values from static storage enum variable.
template <auto V, detail::enum_subtype S = detail::subtype_v<std::decay_t<decltype(V)>>>
[[nodiscard]] constexpr auto enum_index() noexcept -> detail::enable_if_t<decltype(V), std::size_t> {
constexpr auto index = enum_index<std::decay_t<decltype(V)>, S>(V);
[[nodiscard]] constexpr auto enum_index() noexcept -> detail::enable_if_t<decltype(V), std::size_t> {\
using D = std::decay_t<decltype(V)>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
constexpr auto index = enum_index<D, S>(V);
static_assert(index, "magic_enum::enum_index enum value does not have a index.");
return *index;
@@ -1259,6 +1284,7 @@ template <auto V>
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_t<E, string_view> {
using D = std::decay_t<E>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
if (const auto i = enum_index<D, S>(value)) {
return detail::names_v<D, S>[*i];
@@ -1271,6 +1297,7 @@ template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
template <detail::enum_subtype S, typename E>
[[nodiscard]] constexpr auto enum_name(E value) -> detail::enable_if_t<E, string_view> {
using D = std::decay_t<E>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
return enum_name<D, S>(value);
}
@@ -1278,13 +1305,19 @@ template <detail::enum_subtype S, typename E>
// Returns std::array with names, sorted by enum value.
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_t<E, detail::names_t<E, S>> {
return detail::names_v<std::decay_t<E>, S>;
using D = std::decay_t<E>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
return detail::names_v<D, S>;
}
// Returns std::array with pairs (value, name), sorted by enum value.
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_t<E, detail::entries_t<E, S>> {
return detail::entries_v<std::decay_t<E>, S>;
using D = std::decay_t<E>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
return detail::entries_v<D, S>;
}
// Allows you to write magic_enum::enum_cast<foo>("bar", magic_enum::case_insensitive);
@@ -1295,31 +1328,27 @@ inline constexpr auto case_insensitive = detail::case_insensitive<>{};
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_cast(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> {
using D = std::decay_t<E>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
if constexpr (detail::count_v<D, S> == 0) {
static_cast<void>(value);
return {}; // Empty enum.
} else {
if constexpr (detail::is_sparse_v<D, S> || (S == detail::enum_subtype::flags)) {
if constexpr (detail::is_sparse_v<D, S> || (S == detail::enum_subtype::flags)) {
#if defined(MAGIC_ENUM_ENABLE_HASH)
return detail::constexpr_switch<&detail::values_v<D, S>, detail::case_call_t::value>(
[](D v) { return optional<D>{v}; },
static_cast<D>(value),
detail::default_result_type_lambda<optional<D>>);
return detail::constexpr_switch<&detail::values_v<D, S>, detail::case_call_t::value>(
[](D v) { return optional<D>{v}; },
static_cast<D>(value),
detail::default_result_type_lambda<optional<D>>);
#else
for (std::size_t i = 0; i < detail::count_v<D, S>; ++i) {
if (value == static_cast<underlying_type_t<D>>(enum_value<D, S>(i))) {
return static_cast<D>(value);
}
}
return {}; // Invalid value or out of range.
#endif
} else {
if (value >= detail::min_v<D, S> && value <= detail::max_v<D, S>) {
for (std::size_t i = 0; i < detail::count_v<D, S>; ++i) {
if (value == static_cast<underlying_type_t<D>>(enum_value<D, S>(i))) {
return static_cast<D>(value);
}
return {}; // Invalid value or out of range.
}
return {}; // Invalid value or out of range.
#endif
} else {
if (value >= detail::min_v<D, S> && value <= detail::max_v<D, S>) {
return static_cast<D>(value);
}
return {}; // Invalid value or out of range.
}
}
@@ -1328,26 +1357,23 @@ template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
template <typename E, detail::enum_subtype S = detail::subtype_v<E>, typename BinaryPredicate = std::equal_to<>>
[[nodiscard]] constexpr auto enum_cast(string_view value, [[maybe_unused]] BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, optional<std::decay_t<E>>, BinaryPredicate> {
using D = std::decay_t<E>;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
if constexpr (detail::count_v<D, S> == 0) {
static_cast<void>(value);
return {}; // Empty enum.
#if defined(MAGIC_ENUM_ENABLE_HASH)
} else if constexpr (detail::is_default_predicate<BinaryPredicate>()) {
return detail::constexpr_switch<&detail::names_v<D, S>, detail::case_call_t::index>(
[](std::size_t i) { return optional<D>{detail::values_v<D, S>[i]}; },
value,
detail::default_result_type_lambda<optional<D>>,
[&p](string_view lhs, string_view rhs) { return detail::cmp_equal(lhs, rhs, p); });
#endif
} else {
for (std::size_t i = 0; i < detail::count_v<D, S>; ++i) {
if (detail::cmp_equal(value, detail::names_v<D, S>[i], p)) {
return enum_value<D, S>(i);
}
}
return {}; // Invalid value or out of range.
if constexpr (detail::is_default_predicate<BinaryPredicate>()) {
return detail::constexpr_switch<&detail::names_v<D, S>, detail::case_call_t::index>(
[](std::size_t i) { return optional<D>{detail::values_v<D, S>[i]}; },
value,
detail::default_result_type_lambda<optional<D>>,
[&p](string_view lhs, string_view rhs) { return detail::cmp_equal(lhs, rhs, p); });
}
#endif
for (std::size_t i = 0; i < detail::count_v<D, S>; ++i) {
if (detail::cmp_equal(value, detail::names_v<D, S>[i], p)) {
return enum_value<D, S>(i);
}
}
return {}; // Invalid value or out of range.
}
// Checks whether enum contains value with such value.

View File

@@ -55,80 +55,98 @@ namespace ripple {
// wrapped lines
// clang-format off
// Universal Transaction flags:
constexpr std::uint32_t tfFullyCanonicalSig = 0x80000000;
enum UniversalFlags : uint32_t {
tfFullyCanonicalSig = 0x80000000,
};
constexpr std::uint32_t tfUniversal = tfFullyCanonicalSig;
constexpr std::uint32_t tfUniversalMask = ~tfUniversal;
// AccountSet flags:
constexpr std::uint32_t tfRequireDestTag = 0x00010000;
constexpr std::uint32_t tfOptionalDestTag = 0x00020000;
constexpr std::uint32_t tfRequireAuth = 0x00040000;
constexpr std::uint32_t tfOptionalAuth = 0x00080000;
constexpr std::uint32_t tfDisallowXRP = 0x00100000;
constexpr std::uint32_t tfAllowXRP = 0x00200000;
enum AccountSetFlags : uint32_t {
tfRequireDestTag = 0x00010000,
tfOptionalDestTag = 0x00020000,
tfRequireAuth = 0x00040000,
tfOptionalAuth = 0x00080000,
tfDisallowXRP = 0x00100000,
tfAllowXRP = 0x00200000,
};
constexpr std::uint32_t tfAccountSetMask =
~(tfUniversal | tfRequireDestTag | tfOptionalDestTag | tfRequireAuth |
tfOptionalAuth | tfDisallowXRP | tfAllowXRP);
// AccountSet SetFlag/ClearFlag values
constexpr std::uint32_t asfRequireDest = 1;
constexpr std::uint32_t asfRequireAuth = 2;
constexpr std::uint32_t asfDisallowXRP = 3;
constexpr std::uint32_t asfDisableMaster = 4;
constexpr std::uint32_t asfAccountTxnID = 5;
constexpr std::uint32_t asfNoFreeze = 6;
constexpr std::uint32_t asfGlobalFreeze = 7;
constexpr std::uint32_t asfDefaultRipple = 8;
constexpr std::uint32_t asfDepositAuth = 9;
constexpr std::uint32_t asfAuthorizedNFTokenMinter = 10;
constexpr std::uint32_t asfTshCollect = 11;
constexpr std::uint32_t asfDisallowIncomingNFTokenOffer = 12;
constexpr std::uint32_t asfDisallowIncomingCheck = 13;
constexpr std::uint32_t asfDisallowIncomingPayChan = 14;
constexpr std::uint32_t asfDisallowIncomingTrustline = 15;
constexpr std::uint32_t asfDisallowIncomingRemit = 16;
enum AccountFlags : uint32_t {
asfRequireDest = 1,
asfRequireAuth = 2,
asfDisallowXRP = 3,
asfDisableMaster = 4,
asfAccountTxnID = 5,
asfNoFreeze = 6,
asfGlobalFreeze = 7,
asfDefaultRipple = 8,
asfDepositAuth = 9,
asfAuthorizedNFTokenMinter = 10,
asfTshCollect = 11,
asfDisallowIncomingNFTokenOffer = 12,
asfDisallowIncomingCheck = 13,
asfDisallowIncomingPayChan = 14,
asfDisallowIncomingTrustline = 15,
asfDisallowIncomingRemit = 16,
};
// OfferCreate flags:
constexpr std::uint32_t tfPassive = 0x00010000;
constexpr std::uint32_t tfImmediateOrCancel = 0x00020000;
constexpr std::uint32_t tfFillOrKill = 0x00040000;
constexpr std::uint32_t tfSell = 0x00080000;
enum OfferCreateFlags : uint32_t {
tfPassive = 0x00010000,
tfImmediateOrCancel = 0x00020000,
tfFillOrKill = 0x00040000,
tfSell = 0x00080000,
};
constexpr std::uint32_t tfOfferCreateMask =
~(tfUniversal | tfPassive | tfImmediateOrCancel | tfFillOrKill | tfSell);
// Payment flags:
constexpr std::uint32_t tfNoRippleDirect = 0x00010000;
constexpr std::uint32_t tfPartialPayment = 0x00020000;
constexpr std::uint32_t tfLimitQuality = 0x00040000;
enum PaymentFlags : uint32_t {
tfNoRippleDirect = 0x00010000,
tfPartialPayment = 0x00020000,
tfLimitQuality = 0x00040000,
};
constexpr std::uint32_t tfPaymentMask =
~(tfUniversal | tfPartialPayment | tfLimitQuality | tfNoRippleDirect);
// TrustSet flags:
constexpr std::uint32_t tfSetfAuth = 0x00010000;
constexpr std::uint32_t tfSetNoRipple = 0x00020000;
constexpr std::uint32_t tfClearNoRipple = 0x00040000;
constexpr std::uint32_t tfSetFreeze = 0x00100000;
constexpr std::uint32_t tfClearFreeze = 0x00200000;
enum TrustSetFlags : uint32_t {
tfSetfAuth = 0x00010000,
tfSetNoRipple = 0x00020000,
tfClearNoRipple = 0x00040000,
tfSetFreeze = 0x00100000,
tfClearFreeze = 0x00200000,
};
constexpr std::uint32_t tfTrustSetMask =
~(tfUniversal | tfSetfAuth | tfSetNoRipple | tfClearNoRipple | tfSetFreeze |
tfClearFreeze);
// EnableAmendment flags:
constexpr std::uint32_t tfGotMajority = 0x00010000;
constexpr std::uint32_t tfLostMajority = 0x00020000;
constexpr std::uint32_t tfTestSuite = 0x80000000;
enum EnableAmendmentFlags : std::uint32_t {
tfGotMajority = 0x00010000,
tfLostMajority = 0x00020000,
tfTestSuite = 0x80000000,
};
// PaymentChannelClaim flags:
constexpr std::uint32_t tfRenew = 0x00010000;
constexpr std::uint32_t tfClose = 0x00020000;
enum PaymentChannelClaimFlags : uint32_t {
tfRenew = 0x00010000,
tfClose = 0x00020000,
};
constexpr std::uint32_t tfPayChanClaimMask = ~(tfUniversal | tfRenew | tfClose);
// NFTokenMint flags:
constexpr std::uint32_t const tfBurnable = 0x00000001;
constexpr std::uint32_t const tfOnlyXRP = 0x00000002;
constexpr std::uint32_t const tfTrustLine = 0x00000004;
constexpr std::uint32_t const tfTransferable = 0x00000008;
constexpr std::uint32_t const tfStrongTSH = 0x00008000;
enum NFTokenMintFlags : uint32_t {
tfBurnable = 0x00000001,
tfOnlyXRP = 0x00000002,
tfTrustLine = 0x00000004,
tfTransferable = 0x00000008,
tfStrongTSH = 0x00008000,
};
constexpr std::uint32_t const tfNFTokenMintOldMask =
~(tfUniversal | tfBurnable | tfOnlyXRP | tfTrustLine | tfTransferable | tfStrongTSH);
@@ -151,7 +169,9 @@ constexpr std::uint32_t const tfNFTokenMintMask =
~(tfUniversal | tfBurnable | tfOnlyXRP | tfTransferable | tfStrongTSH);
// NFTokenCreateOffer flags:
constexpr std::uint32_t const tfSellNFToken = 0x00000001;
enum NFTokenCreateOfferFlags : uint32_t {
tfSellNFToken = 0x00000001,
};
constexpr std::uint32_t const tfNFTokenCreateOfferMask =
~(tfUniversal | tfSellNFToken);
@@ -166,7 +186,9 @@ constexpr std::uint32_t const tfURITokenMintMask = ~(tfUniversal | tfBurnable);
constexpr std::uint32_t const tfURITokenNonMintMask = ~tfUniversal;
// ClaimReward flags:
constexpr std::uint32_t const tfOptOut = 0x00000001;
enum ClaimRewardFlags : uint32_t {
tfOptOut = 0x00000001,
};
// clang-format on

View File

@@ -108,6 +108,7 @@ constexpr static ErrorInfo unorderedErrorInfos[]{
{rpcSTREAM_MALFORMED, "malformedStream", "Stream malformed.", 400},
{rpcTOO_BUSY, "tooBusy", "The server is too busy to help you now.", 503},
{rpcTXN_NOT_FOUND, "txnNotFound", "Transaction not found.", 404},
{rpcNAMESPACE_NOT_FOUND, "namespaceNotFound", "Namespace not found.", 404},
{rpcUNKNOWN_COMMAND, "unknownCmd", "Unknown method.", 405}};
// clang-format on

View File

@@ -161,6 +161,7 @@ JSS(account_data); // out: AccountInfo
JSS(account_flags); // out: AccountInfo
JSS(account_hash); // out: LedgerToJson
JSS(account_id); // out: WalletPropose
JSS(account_namespace); // out: AccountNamespace
JSS(account_nfts); // out: AccountNFTs
JSS(account_objects); // out: AccountObjects
JSS(account_root); // in: LedgerEntry
@@ -353,6 +354,7 @@ JSS(id); // websocket.
JSS(ident); // in: AccountCurrencies, AccountInfo,
// OwnerInfo
JSS(ignore_default); // in: AccountLines
JSS(import_vlseq); // in: LedgerEntry
JSS(inLedger); // out: tx/Transaction
JSS(in_queue);
JSS(inbound); // out: PeerImp
@@ -665,50 +667,53 @@ JSS(trusted); // out: UnlList
JSS(trusted_validator_keys); // out: ValidatorList
JSS(tx); // out: STTx, AccountTx*
JSS(txroot);
JSS(tx_blob); // in/out: Submit,
// in: TransactionSign, AccountTx*
JSS(tx_hash); // in: TransactionEntry
JSS(tx_json); // in/out: TransactionSign
// out: TransactionEntry
JSS(tx_signing_hash); // out: TransactionSign
JSS(tx_unsigned); // out: TransactionSign
JSS(txn_count); // out: NetworkOPs
JSS(txr_tx_cnt); // out: protocol message tx's count
JSS(txr_tx_sz); // out: protocol message tx's size
JSS(txr_have_txs_cnt); // out: protocol message have tx count
JSS(txr_have_txs_sz); // out: protocol message have tx size
JSS(txr_get_ledger_cnt); // out: protocol message get ledger count
JSS(txr_get_ledger_sz); // out: protocol message get ledger size
JSS(txr_ledger_data_cnt); // out: protocol message ledger data count
JSS(txr_ledger_data_sz); // out: protocol message ledger data size
JSS(txr_transactions_cnt); // out: protocol message get object count
JSS(txr_transactions_sz); // out: protocol message get object size
JSS(txr_selected_cnt); // out: selected peers count
JSS(txr_suppressed_cnt); // out: suppressed peers count
JSS(txr_not_enabled_cnt); // out: peers with tx reduce-relay disabled count
JSS(txr_missing_tx_freq); // out: missing tx frequency average
JSS(txs); // out: TxHistory
JSS(type); // in: AccountObjects
// out: NetworkOPs RPC server_definitions
// OverlayImpl, Logic
JSS(TRANSACTION_RESULTS); // out: RPC server_definitions
JSS(TRANSACTION_TYPES); // out: RPC server_definitions
JSS(TYPES); // out: RPC server_definitions
JSS(type_hex); // out: STPathSet
JSS(unl); // out: UnlList
JSS(unlimited); // out: Connection.h
JSS(uptime); // out: GetCounts
JSS(uri); // out: ValidatorSites
JSS(uri_token); // in: LedgerEntry
JSS(url); // in/out: Subscribe, Unsubscribe
JSS(url_password); // in: Subscribe
JSS(url_username); // in: Subscribe
JSS(urlgravatar); //
JSS(username); // in: Subscribe
JSS(validated); // out: NetworkOPs, RPCHelpers, AccountTx*
// Tx
JSS(validator_list_expires); // out: NetworkOps, ValidatorList
JSS(validator_list); // out: NetworkOps, ValidatorList
JSS(tx_blob); // in/out: Submit,
// in: TransactionSign, AccountTx*
JSS(tx_hash); // in: TransactionEntry
JSS(tx_json); // in/out: TransactionSign
// out: TransactionEntry
JSS(tx_signing_hash); // out: TransactionSign
JSS(tx_unsigned); // out: TransactionSign
JSS(txn_count); // out: NetworkOPs
JSS(txr_tx_cnt); // out: protocol message tx's count
JSS(txr_tx_sz); // out: protocol message tx's size
JSS(txr_have_txs_cnt); // out: protocol message have tx count
JSS(txr_have_txs_sz); // out: protocol message have tx size
JSS(txr_get_ledger_cnt); // out: protocol message get ledger count
JSS(txr_get_ledger_sz); // out: protocol message get ledger size
JSS(txr_ledger_data_cnt); // out: protocol message ledger data count
JSS(txr_ledger_data_sz); // out: protocol message ledger data size
JSS(txr_transactions_cnt); // out: protocol message get object count
JSS(txr_transactions_sz); // out: protocol message get object size
JSS(txr_selected_cnt); // out: selected peers count
JSS(txr_suppressed_cnt); // out: suppressed peers count
JSS(txr_not_enabled_cnt); // out: peers with tx reduce-relay disabled count
JSS(txr_missing_tx_freq); // out: missing tx frequency average
JSS(txs); // out: TxHistory
JSS(type); // in: AccountObjects
// out: NetworkOPs RPC server_definitions
// OverlayImpl, Logic
JSS(TRANSACTION_RESULTS); // out: RPC server_definitions
JSS(TRANSACTION_TYPES); // out: RPC server_definitions
JSS(TYPES); // out: RPC server_definitions
JSS(TRANSACTION_FLAGS); // out: RPC server_definitions
JSS(TRANSACTION_FLAGS_INDICES); // out: RPC server_definitions
JSS(type_hex); // out: STPathSet
JSS(unl); // out: UnlList
JSS(unlimited); // out: Connection.h
JSS(unl_report); // in: LedgerEntry
JSS(uptime); // out: GetCounts
JSS(uri); // out: ValidatorSites
JSS(uri_token); // in: LedgerEntry
JSS(url); // in/out: Subscribe, Unsubscribe
JSS(url_password); // in: Subscribe
JSS(url_username); // in: Subscribe
JSS(urlgravatar); //
JSS(username); // in: Subscribe
JSS(validated); // out: NetworkOPs, RPCHelpers, AccountTx*
// Tx
JSS(validator_list_expires); // out: NetworkOps, ValidatorList
JSS(validator_list); // out: NetworkOps, ValidatorList
JSS(validators);
JSS(validated_hash); // out: NetworkOPs
JSS(validated_ledger); // out: NetworkOPs

View File

@@ -78,7 +78,7 @@ doAccountInfo(RPC::JsonContext& context)
return jvAccepted;
static constexpr std::
array<std::pair<std::string_view, LedgerSpecificFlags>, 9>
array<std::pair<std::string_view, LedgerSpecificFlags>, 10>
lsFlags{
{{"defaultRipple", lsfDefaultRipple},
{"depositAuth", lsfDepositAuth},
@@ -88,6 +88,7 @@ doAccountInfo(RPC::JsonContext& context)
{"noFreeze", lsfNoFreeze},
{"passwordSpent", lsfPasswordSpent},
{"requireAuthorization", lsfRequireAuth},
{"tshCollect", lsfTshCollect},
{"requireDestinationTag", lsfRequireDestTag}}};
static constexpr std::

View File

@@ -42,7 +42,6 @@ namespace ripple {
namespace_id: <namespace hex>
ledger_hash: <string> // optional
ledger_index: <string | unsigned integer> // optional
type: <string> // optional, defaults to all account objects types
limit: <integer> // optional
marker: <opaque> // optional, resume previous query
}

View File

@@ -24,6 +24,7 @@
#include <ripple/net/RPCErr.h>
#include <ripple/protocol/ErrorCodes.h>
#include <ripple/protocol/Indexes.h>
#include <ripple/protocol/PublicKey.h>
#include <ripple/protocol/jss.h>
#include <ripple/rpc/Context.h>
#include <ripple/rpc/GRPCHandlers.h>
@@ -231,6 +232,41 @@ doLedgerEntry(RPC::JsonContext& context)
uNodeIndex = keylet::emittedTxn(uNodeIndex).key;
}
}
else if (context.params.isMember(jss::import_vlseq))
{
expectedType = ltIMPORT_VLSEQ;
if (!context.params[jss::import_vlseq].isObject())
{
if (!uNodeIndex.parseHex(
context.params[jss::import_vlseq].asString()))
{
uNodeIndex = beast::zero;
jvResult[jss::error] = "malformedRequest";
}
}
else if (
!context.params[jss::import_vlseq].isMember(jss::public_key) ||
!context.params[jss::import_vlseq][jss::public_key].isString())
{
jvResult[jss::error] = "malformedRequest";
}
else
{
auto const pkHex = strUnHex(
context.params[jss::import_vlseq][jss::public_key].asString());
auto const pkSlice = makeSlice(*pkHex);
if (!publicKeyType(pkSlice))
{
uNodeIndex = beast::zero;
jvResult[jss::error] = "malformedRequest";
}
else
{
auto const pk = PublicKey(pkSlice);
uNodeIndex = keylet::import_vlseq(pk).key;
}
}
}
else if (context.params.isMember(jss::offer))
{
expectedType = ltOFFER;
@@ -277,11 +313,32 @@ doLedgerEntry(RPC::JsonContext& context)
{
expectedType = ltURI_TOKEN;
if (!uNodeIndex.parseHex(context.params[jss::uri_token].asString()))
if (!context.params[jss::uri_token].isObject())
{
if (!uNodeIndex.parseHex(context.params[jss::uri_token].asString()))
{
uNodeIndex = beast::zero;
jvResult[jss::error] = "malformedRequest";
}
}
else if (
!context.params[jss::uri_token].isMember(jss::account) ||
!context.params[jss::uri_token].isMember(jss::uri))
{
uNodeIndex = beast::zero;
jvResult[jss::error] = "malformedRequest";
}
else
{
auto const id = parseBase58<AccountID>(
context.params[jss::uri_token][jss::account].asString());
auto const strUri =
context.params[jss::uri_token][jss::uri].asString();
Blob raw = Blob(strUri.begin(), strUri.end());
if (!id)
jvResult[jss::error] = "malformedAddress";
else
uNodeIndex = keylet::uritoken(*id, raw).key;
}
}
else if (context.params.isMember(jss::ripple_state))
{

View File

@@ -17,6 +17,8 @@
*/
//==============================================================================
#define MAGIC_ENUM_NO_CHECK_REFLECTED_ENUM
#include <ripple/app/main/Application.h>
#include <ripple/app/misc/AmendmentTable.h>
#include <ripple/app/misc/NetworkOPs.h>
@@ -26,6 +28,7 @@
#include <ripple/net/RPCErr.h>
#include <ripple/protocol/LedgerFormats.h>
#include <ripple/protocol/SField.h>
#include <ripple/protocol/TxFlags.h>
#include <ripple/protocol/digest.h>
#include <ripple/protocol/jss.h>
#include <ripple/rpc/impl/TransactionSign.h>
@@ -41,6 +44,21 @@
static constexpr int max = 20000; \
};
#define MAGIC_ENUM_16(x) \
template <> \
struct magic_enum::customize::enum_range<x> \
{ \
static constexpr int min = -128; \
static constexpr int max = 127; \
};
#define MAGIC_ENUM_FLAG(x) \
template <> \
struct magic_enum::customize::enum_range<x> \
{ \
static constexpr bool is_flags = true; \
};
MAGIC_ENUM(ripple::SerializedTypeID);
MAGIC_ENUM(ripple::LedgerEntryType);
MAGIC_ENUM(ripple::TELcodes);
@@ -49,22 +67,45 @@ MAGIC_ENUM(ripple::TEFcodes);
MAGIC_ENUM(ripple::TERcodes);
MAGIC_ENUM(ripple::TEScodes);
MAGIC_ENUM(ripple::TECcodes);
MAGIC_ENUM_16(ripple::TxType);
MAGIC_ENUM_FLAG(ripple::UniversalFlags);
MAGIC_ENUM_FLAG(ripple::AccountSetFlags);
MAGIC_ENUM_FLAG(ripple::OfferCreateFlags);
MAGIC_ENUM_FLAG(ripple::PaymentFlags);
MAGIC_ENUM_FLAG(ripple::TrustSetFlags);
MAGIC_ENUM_FLAG(ripple::EnableAmendmentFlags);
MAGIC_ENUM_FLAG(ripple::PaymentChannelClaimFlags);
MAGIC_ENUM_FLAG(ripple::NFTokenMintFlags);
MAGIC_ENUM_FLAG(ripple::NFTokenCreateOfferFlags);
MAGIC_ENUM_FLAG(ripple::ClaimRewardFlags);
MAGIC_ENUM_16(ripple::AccountFlags);
namespace ripple {
class Definitions
{
private:
Json::Value
generate()
{
// RH TODO: probably a better way to do this?
#define STR(x) \
([&] { \
std::ostringstream ss; \
return ss << (x), ss.str(); \
}())
template <typename EnumType>
void
addFlagsToJson(Json::Value& json, std::string const& key)
{
for (auto const& entry : magic_enum::enum_entries<EnumType>())
{
const auto name = entry.second;
json[jss::TRANSACTION_FLAGS][key][STR(name)] =
static_cast<uint32_t>(entry.first);
}
}
Json::Value
generate()
{
Json::Value ret{Json::objectValue};
ret[jss::TYPES] = Json::objectValue;
@@ -368,6 +409,39 @@ private:
ret[jss::TRANSACTION_TYPES][type_name] = type_value;
}
// Transaction Flags:
ret[jss::TRANSACTION_FLAGS] = Json::objectValue;
addFlagsToJson<UniversalFlags>(ret, "Universal");
addFlagsToJson<AccountSetFlags>(ret, "AccountSet");
addFlagsToJson<OfferCreateFlags>(ret, "OfferCreate");
addFlagsToJson<PaymentFlags>(ret, "Payment");
addFlagsToJson<TrustSetFlags>(ret, "TrustSet");
addFlagsToJson<EnableAmendmentFlags>(ret, "EnableAmendment");
addFlagsToJson<PaymentChannelClaimFlags>(ret, "PaymentChannelClaim");
addFlagsToJson<NFTokenMintFlags>(ret, "NFTokenMint");
addFlagsToJson<NFTokenCreateOfferFlags>(ret, "NFTokenCreateOffer");
addFlagsToJson<ClaimRewardFlags>(ret, "ClaimReward");
struct FlagData
{
std::string name;
std::uint32_t value;
};
std::array<FlagData, 1> uriTokenMintFlags{{{"tfBurnable", tfBurnable}}};
for (auto const& entry : uriTokenMintFlags)
{
ret[jss::TRANSACTION_FLAGS]["URITokenMint"][entry.name] =
static_cast<uint32_t>(entry.value);
}
// Transaction Indicies Flags:
ret[jss::TRANSACTION_FLAGS_INDICES] = Json::objectValue;
for (auto const& entry : magic_enum::enum_entries<AccountFlags>())
{
const auto name = entry.second;
ret[jss::TRANSACTION_FLAGS_INDICES]["AccountSet"][STR(name)] =
static_cast<uint32_t>(entry.first);
}
ret[jss::native_currency_code] = systemCurrencyCode();
// generate hash

View File

@@ -61,22 +61,7 @@ getDeliveredAmount(
if (serializedTx->isFieldPresent(sfAmount))
{
using namespace std::chrono_literals;
// Ledger 4594095 is the first ledger in which the DeliveredAmount field
// was present when a partial payment was made and its absence indicates
// that the amount delivered is listed in the Amount field.
//
// If the ledger closed long after the DeliveredAmount code was deployed
// then its absence indicates that the amount delivered is listed in the
// Amount field. DeliveredAmount went live January 24, 2014.
// 446000000 is in Feb 2014, well after DeliveredAmount went live
if (getLedgerIndex() >= 4594095 ||
getCloseTime() > NetClock::time_point{446000000s} ||
(serializedTx && serializedTx->isFieldPresent(sfNetworkID)))
{
return serializedTx->getFieldAmount(sfAmount);
}
return serializedTx->getFieldAmount(sfAmount);
}
return {};

View File

@@ -1067,7 +1067,7 @@ chooseLedgerEntryType(Json::Value const& params)
std::pair<RPC::Status, LedgerEntryType> result{RPC::Status::OK, ltANY};
if (params.isMember(jss::type))
{
static constexpr std::array<std::pair<char const*, LedgerEntryType>, 19>
static constexpr std::array<std::pair<char const*, LedgerEntryType>, 22>
types{
{{jss::account, ltACCOUNT_ROOT},
{jss::amendments, ltAMENDMENTS},
@@ -1075,11 +1075,13 @@ chooseLedgerEntryType(Json::Value const& params)
{jss::deposit_preauth, ltDEPOSIT_PREAUTH},
{jss::directory, ltDIR_NODE},
{jss::escrow, ltESCROW},
{jss::emitted_txn, ltEMITTED_TXN},
{jss::hook, ltHOOK},
{jss::hook_definition, ltHOOK_DEFINITION},
{jss::hook_state, ltHOOK_STATE},
{jss::fee, ltFEE_SETTINGS},
{jss::hashes, ltLEDGER_HASHES},
{jss::import_vlseq, ltIMPORT_VLSEQ},
{jss::offer, ltOFFER},
{jss::payment_channel, ltPAYCHAN},
{jss::uri_token, ltURI_TOKEN},
@@ -1087,7 +1089,8 @@ chooseLedgerEntryType(Json::Value const& params)
{jss::state, ltRIPPLE_STATE},
{jss::ticket, ltTICKET},
{jss::nft_offer, ltNFTOKEN_OFFER},
{jss::nft_page, ltNFTOKEN_PAGE}}};
{jss::nft_page, ltNFTOKEN_PAGE},
{jss::unl_report, ltUNL_REPORT}}};
auto const& p = params[jss::type];
if (!p.isString())

View File

@@ -84,28 +84,6 @@ class Import_test : public beast::unit_test::suite
return 0;
}
STTx
createUNLReportTx(
LedgerIndex seq,
PublicKey const& importKey,
PublicKey const& valKey)
{
auto fill = [&](auto& obj) {
obj.setFieldU32(sfLedgerSequence, seq);
obj.set(([&]() {
auto inner = std::make_unique<STObject>(sfActiveValidator);
inner->setFieldVL(sfPublicKey, valKey);
return inner;
})());
obj.set(([&]() {
auto inner = std::make_unique<STObject>(sfImportVLKey);
inner->setFieldVL(sfPublicKey, importKey);
return inner;
})());
};
return STTx(ttUNL_REPORT, fill);
}
bool
hasUNLReport(jtx::Env const& env)
{
@@ -5320,7 +5298,7 @@ class Import_test : public beast::unit_test::suite
// insert a ttUNL_REPORT pseudo into the open ledger
env.app().openLedger().modify(
[&](OpenView& view, beast::Journal j) -> bool {
STTx tx = createUNLReportTx(
STTx tx = unl::createUNLReportTx(
env.current()->seq() + 1, ivlKeys[0], vlKeys[0]);
uint256 txID = tx.getTransactionID();
auto s = std::make_shared<ripple::Serializer>();

View File

@@ -5466,6 +5466,7 @@ private:
params[jss::transaction] = txIds[i];
auto const jrr = env.rpc("json", "tx", to_string(params));
auto const meta = jrr[jss::result][jss::meta];
BEAST_EXPECT(meta[jss::delivered_amount] == "1000000");
for (auto const& node : meta[sfAffectedNodes.jsonName])
{
auto const nodeType = node[sfLedgerEntryType.jsonName];

View File

@@ -3598,28 +3598,6 @@ struct XahauGenesis_test : public beast::unit_test::suite
return slep != nullptr;
}
STTx
createUNLReportTx(
LedgerIndex seq,
PublicKey const& importKey,
PublicKey const& valKey)
{
auto fill = [&](auto& obj) {
obj.setFieldU32(sfLedgerSequence, seq);
obj.set(([&]() {
auto inner = std::make_unique<STObject>(sfActiveValidator);
inner->setFieldVL(sfPublicKey, valKey);
return inner;
})());
obj.set(([&]() {
auto inner = std::make_unique<STObject>(sfImportVLKey);
inner->setFieldVL(sfPublicKey, importKey);
return inner;
})());
};
return STTx(ttUNL_REPORT, fill);
}
void
activateUNLReport(
jtx::Env& env,
@@ -3631,7 +3609,7 @@ struct XahauGenesis_test : public beast::unit_test::suite
{
env.app().openLedger().modify(
[&](OpenView& view, beast::Journal j) -> bool {
STTx tx = createUNLReportTx(
STTx tx = unl::createUNLReportTx(
env.current()->seq() + 1, ivlKeys[0], vlKey);
uint256 txID = tx.getTransactionID();
auto s = std::make_shared<ripple::Serializer>();

View File

@@ -90,6 +90,28 @@ createTx(bool disabling, LedgerIndex seq, PublicKey const& txKey)
return STTx(ttUNL_MODIFY, fill);
}
STTx
createUNLReportTx(
LedgerIndex seq,
PublicKey const& importKey,
PublicKey const& valKey)
{
auto fill = [&](auto& obj) {
obj.setFieldU32(sfLedgerSequence, seq);
obj.set(([&]() {
auto inner = std::make_unique<STObject>(sfActiveValidator);
inner->setFieldVL(sfPublicKey, valKey);
return inner;
})());
obj.set(([&]() {
auto inner = std::make_unique<STObject>(sfImportVLKey);
inner->setFieldVL(sfPublicKey, importKey);
return inner;
})());
};
return STTx(ttUNL_REPORT, fill);
}
} // namespace unl
} // namespace test
} // namespace ripple

View File

@@ -78,6 +78,20 @@ countTx(std::shared_ptr<SHAMap> const& txSet);
STTx
createTx(bool disabling, LedgerIndex seq, PublicKey const& txKey);
/**
* Create ttUNL_REPORT Tx
*
* @param seq current ledger seq
* @param importKey the public key of the import network
* @param txKey the public key of the validator
* @return the ttUNL_REPORT Tx
*/
STTx
createUNLReportTx(
LedgerIndex seq,
PublicKey const& importKey,
PublicKey const& valKey);
} // namespace unl
} // namespace test

View File

@@ -516,7 +516,7 @@ public:
};
static constexpr std::
array<std::pair<std::string_view, std::uint32_t>, 7>
array<std::pair<std::string_view, std::uint32_t>, 8>
asFlags{
{{"defaultRipple", asfDefaultRipple},
{"depositAuth", asfDepositAuth},
@@ -524,6 +524,7 @@ public:
{"globalFreeze", asfGlobalFreeze},
{"noFreeze", asfNoFreeze},
{"requireAuthorization", asfRequireAuth},
{"tshCollect", asfTshCollect},
{"requireDestinationTag", asfRequireDest}}};
for (auto& asf : asFlags)

View File

@@ -0,0 +1,250 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2024 XRPL-Labs
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 <ripple/protocol/Feature.h>
#include <ripple/protocol/jss.h>
#include <test/jtx.h>
namespace ripple {
namespace test {
class AccountNamespace_test : public beast::unit_test::suite
{
public:
void
testErrors(FeatureBitset features)
{
testcase("error cases");
using namespace jtx;
Env env(*this);
Account const alice{"alice"};
Account const bob{"bob"};
Account const carol{"carol"};
env.fund(XRP(1000), alice, bob);
env.close();
auto const ns = uint256::fromVoid(
(std::array<uint8_t, 32>{
0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU,
0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU,
0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU,
0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU})
.data());
{
// account_namespace with no account.
auto const info = env.rpc("json", "account_namespace", "{ }");
BEAST_EXPECT(
info[jss::result][jss::error_message] ==
"Missing field 'account'.");
}
{
// account_namespace missing filed namespace_id.
Json::Value params;
params[jss::account] = alice.human();
auto const info =
env.rpc("json", "account_namespace", to_string(params));
BEAST_EXPECT(
info[jss::result][jss::error_message] ==
"Missing field 'namespace_id'.");
}
{
// account_namespace with a malformed account string.
Json::Value params;
params[jss::account] =
"n94JNrQYkDrpt62bbSR7nVEhdyAvcJXRAsjEkFYyqRkh9SUTYEqV";
params[jss::namespace_id] = "";
auto const info =
env.rpc("json", "account_namespace", to_string(params));
BEAST_EXPECT(
info[jss::result][jss::error_message] == "Disallowed seed.");
}
{
// account_namespace with an invalid namespace_id.
Json::Value params;
params[jss::account] = alice.human();
params[jss::namespace_id] = "DEADBEEF";
auto const info =
env.rpc("json", "account_namespace", to_string(params));
BEAST_EXPECT(
info[jss::result][jss::error_message] == "Invalid parameters.");
}
{
// account_namespace with an account that's not in the ledger.
Json::Value params;
params[jss::account] = carol.human();
params[jss::namespace_id] = to_string(ns);
auto const info =
env.rpc("json", "account_namespace", to_string(params));
BEAST_EXPECT(
info[jss::result][jss::error_message] == "Account not found.");
}
{
// account_namespace with a namespace that doesnt exist.
Json::Value params;
params[jss::account] = alice.human();
params[jss::namespace_id] = to_string(ns);
auto const info =
env.rpc("json", "account_namespace", to_string(params));
BEAST_EXPECT(
info[jss::result][jss::error_message] ==
"Namespace not found.");
}
// test errors on marker
{
auto const key = uint256::fromVoid(
(std::array<uint8_t, 32>{
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
0x00U, 0x00U, 0x00U, 0x00U, 'k', 'e', 'y', 0x00U})
.data());
auto const ns = uint256::fromVoid(
(std::array<uint8_t, 32>{
0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU,
0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU,
0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU,
0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU})
.data());
auto const nons = uint256::fromVoid(
(std::array<uint8_t, 32>{
0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU,
0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU,
0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU,
0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFFU})
.data());
// Lambda to create a hook.
auto setHook = [](test::jtx::Account const& account) {
std::string const createCodeHex =
"0061736D01000000011B0460027F7F017F60047F7F7F7F017E60037F7F"
"7E01"
"7E60017F017E02270303656E76025F67000003656E760973746174655F"
"7365"
"74000103656E76066163636570740002030201030503010002062B077F"
"0141"
"9088040B7F004180080B7F00418A080B7F004180080B7F00419088040B"
"7F00"
"41000B7F0041010B07080104686F6F6B00030AE7800001E3800002017F"
"017E"
"230041106B220124002001200036020C41012200200010001A20014180"
"0828"
"0000360208200141046A410022002F0088083B01002001200028008408"
"3602"
"004100200020014106200141086A4104100110022102200141106A2400"
"2002"
"0B0B1001004180080B096B65790076616C7565";
std::string ns_str =
"CAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECA"
"FECA"
"FE";
Json::Value jv =
ripple::test::jtx::hook(account, {{hso(createCodeHex)}}, 0);
jv[jss::Hooks][0U][jss::Hook][jss::HookNamespace] = ns_str;
return jv;
};
env(setHook(bob), fee(XRP(1)), ter(tesSUCCESS));
env.close();
env(pay(alice, bob, XRP(1)), fee(XRP(1)), ter(tesSUCCESS));
env.close();
Json::Value params;
params[jss::account] = bob.human();
params[jss::namespace_id] = to_string(ns);
params[jss::limit] = 1;
auto resp = env.rpc("json", "account_namespace", to_string(params));
auto resume_marker = resp[jss::result][jss::marker];
std::string mark = to_string(resume_marker);
params[jss::marker] = 10;
resp = env.rpc("json", "account_namespace", to_string(params));
BEAST_EXPECT(
resp[jss::result][jss::error_message] ==
"Invalid field 'marker', not string.");
params[jss::marker] = "This is a string with no comma";
resp = env.rpc("json", "account_namespace", to_string(params));
BEAST_EXPECT(
resp[jss::result][jss::error_message] ==
"Invalid field 'marker'.");
params[jss::marker] = "This string has a comma, but is not hex";
resp = env.rpc("json", "account_namespace", to_string(params));
BEAST_EXPECT(
resp[jss::result][jss::error_message] ==
"Invalid field 'marker'.");
params[jss::marker] = std::string(&mark[1U], 64);
resp = env.rpc("json", "account_namespace", to_string(params));
BEAST_EXPECT(
resp[jss::result][jss::error_message] ==
"Invalid field 'marker'.");
params[jss::marker] = std::string(&mark[1U], 65);
resp = env.rpc("json", "account_namespace", to_string(params));
BEAST_EXPECT(
resp[jss::result][jss::error_message] ==
"Invalid field 'marker'.");
params[jss::marker] = std::string(&mark[1U], 65) + "not hex";
resp = env.rpc("json", "account_namespace", to_string(params));
BEAST_EXPECT(
resp[jss::result][jss::error_message] ==
"Invalid field 'marker'.");
params[jss::marker] = std::string(&mark[1U], 128);
resp = env.rpc("json", "account_namespace", to_string(params));
BEAST_EXPECT(
resp[jss::result][jss::error_message] ==
"Invalid field 'marker'.");
}
// test error on limit -ve
{
Account const bob{"bob"};
Json::Value params;
params[jss::account] = bob.human();
params[jss::namespace_id] = to_string(ns);
params[jss::limit] = -1;
auto resp = env.rpc("json", "account_namespace", to_string(params));
BEAST_EXPECT(
resp[jss::result][jss::error_message] ==
"Invalid field 'limit', not unsigned integer.");
}
}
void
run() override
{
using namespace test::jtx;
FeatureBitset const all{supported_amendments()};
testErrors(all);
}
};
BEAST_DEFINE_TESTSUITE(AccountNamespace, rpc, ripple);
} // namespace test
} // namespace ripple

View File

@@ -34,19 +34,19 @@ namespace test {
static char const* bobs_account_objects[] = {
R"json({
"Account" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
"BookDirectory" : "50AD0A9E54D2B381288D535EB724E4275FFBF41580D28A925D038D7EA4C68000",
"BookDirectory" : "B025997A323F5C3E03DDF1334471F5984ABDE31C59D463525D038D7EA4C68000",
"BookNode" : "0",
"Flags" : 65536,
"LedgerEntryType" : "Offer",
"OwnerNode" : "0",
"Sequence" : 6,
"Sequence" : 4,
"TakerGets" : {
"currency" : "USD",
"issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
"issuer" : "r32rQHyesiTtdWFU7UJVtff4nCR5SHCbJW",
"value" : "1"
},
"TakerPays" : "100000000",
"index" : "29665262716C19830E26AEEC0916E476FC7D8EF195FF3B4F06829E64F82A3B3E"
"index" : "A984D036A0E562433A8377CA57D1A1E056E58C0D04818F8DFD3A1AA3F217DD82"
})json",
R"json({
"Balance" : {
@@ -94,19 +94,19 @@ static char const* bobs_account_objects[] = {
})json",
R"json({
"Account" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
"BookDirectory" : "B025997A323F5C3E03DDF1334471F5984ABDE31C59D463525D038D7EA4C68000",
"BookDirectory" : "50AD0A9E54D2B381288D535EB724E4275FFBF41580D28A925D038D7EA4C68000",
"BookNode" : "0",
"Flags" : 65536,
"LedgerEntryType" : "Offer",
"OwnerNode" : "0",
"Sequence" : 7,
"Sequence" : 3,
"TakerGets" : {
"currency" : "USD",
"issuer" : "r32rQHyesiTtdWFU7UJVtff4nCR5SHCbJW",
"issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
"value" : "1"
},
"TakerPays" : "100000000",
"index" : "F03ABE26CB8C5F4AFB31A86590BD25C64C5756FCE5CE9704C27AFE291A4A29A1"
"index" : "E11029302EE744401427793A4F37BCB18F698D55C96851BEC5ABBD6242CF03D7"
})json"};
class AccountObjects_test : public beast::unit_test::suite
@@ -328,9 +328,7 @@ public:
aobj.removeMember("PreviousTxnID");
aobj.removeMember("PreviousTxnLgrSeq");
BEAST_EXPECT(aobj == bobj[i]);
params[jss::marker] = resp[jss::result][jss::marker];
}
}
@@ -734,7 +732,7 @@ public:
auto const& ticket = resp[jss::result][jss::account_objects][0u];
BEAST_EXPECT(ticket[sfAccount.jsonName] == gw.human());
BEAST_EXPECT(ticket[sfLedgerEntryType.jsonName] == jss::Ticket);
BEAST_EXPECT(ticket[sfTicketSequence.jsonName].asUInt() == 13);
BEAST_EXPECT(ticket[sfTicketSequence.jsonName].asUInt() == 10);
}
{
// Create a uri token.
@@ -856,7 +854,7 @@ public:
run() override
{
using namespace jtx;
FeatureBitset const all{supported_amendments() - featureXahauGenesis};
FeatureBitset const all{supported_amendments()};
testErrors(all);
testUnsteppedThenStepped(all);
testUnsteppedThenSteppedWithNFTs(all);

View File

@@ -119,23 +119,23 @@ public:
BEAST_EXPECT(jroOuter[0u][jss::quality] == "100000000");
BEAST_EXPECT(jroOuter[0u][jss::taker_gets][jss::currency] == "USD");
BEAST_EXPECT(
jroOuter[0u][jss::taker_gets][jss::issuer] == gw.human());
BEAST_EXPECT(jroOuter[0u][jss::taker_gets][jss::value] == "2");
BEAST_EXPECT(jroOuter[0u][jss::taker_pays] == "200000000");
jroOuter[0u][jss::taker_gets][jss::issuer] == bob.human());
BEAST_EXPECT(jroOuter[0u][jss::taker_gets][jss::value] == "1");
BEAST_EXPECT(jroOuter[0u][jss::taker_pays] == "100000000");
BEAST_EXPECT(jroOuter[1u][jss::quality] == "100000000");
BEAST_EXPECT(jroOuter[1u][jss::quality] == "5000000");
BEAST_EXPECT(jroOuter[1u][jss::taker_gets][jss::currency] == "USD");
BEAST_EXPECT(
jroOuter[1u][jss::taker_gets][jss::issuer] == bob.human());
BEAST_EXPECT(jroOuter[1u][jss::taker_gets][jss::value] == "1");
BEAST_EXPECT(jroOuter[1u][jss::taker_pays] == "100000000");
jroOuter[1u][jss::taker_gets][jss::issuer] == gw.human());
BEAST_EXPECT(jroOuter[1u][jss::taker_gets][jss::value] == "6");
BEAST_EXPECT(jroOuter[1u][jss::taker_pays] == "30000000");
BEAST_EXPECT(jroOuter[2u][jss::quality] == "5000000");
BEAST_EXPECT(jroOuter[2u][jss::quality] == "100000000");
BEAST_EXPECT(jroOuter[2u][jss::taker_gets][jss::currency] == "USD");
BEAST_EXPECT(
jroOuter[2u][jss::taker_gets][jss::issuer] == gw.human());
BEAST_EXPECT(jroOuter[2u][jss::taker_gets][jss::value] == "6");
BEAST_EXPECT(jroOuter[2u][jss::taker_pays] == "30000000");
BEAST_EXPECT(jroOuter[2u][jss::taker_gets][jss::value] == "2");
BEAST_EXPECT(jroOuter[2u][jss::taker_pays] == "200000000");
}
{
@@ -327,7 +327,7 @@ public:
run() override
{
using namespace jtx;
FeatureBitset const all{supported_amendments() - featureXahauGenesis};
FeatureBitset const all{supported_amendments()};
testSequential(all, true);
testSequential(all, false);
testBadInput(all);

View File

@@ -191,80 +191,73 @@ class DeliveredAmount_test : public beast::unit_test::suite
auto const gw = Account("gateway");
auto const USD = gw["USD"];
for (bool const afterSwitchTime : {true, false})
Env env{*this, features};
env.fund(XRP(10000), alice, bob, carol, gw);
env.trust(USD(1000), alice, bob, carol);
env.close();
CheckDeliveredAmount checkDeliveredAmount{true};
{
Env env{*this, features};
env.fund(XRP(10000), alice, bob, carol, gw);
env.trust(USD(1000), alice, bob, carol);
if (afterSwitchTime)
env.close(NetClock::time_point{446000000s});
else
env.close();
// add payments, but do no close until subscribed
CheckDeliveredAmount checkDeliveredAmount{afterSwitchTime};
{
// add payments, but do no close until subscribed
// normal payments
env(pay(gw, alice, USD(50)));
checkDeliveredAmount.adjCountersSuccess();
env(pay(gw, alice, XRP(50)));
checkDeliveredAmount.adjCountersSuccess();
// normal payments
env(pay(gw, alice, USD(50)));
checkDeliveredAmount.adjCountersSuccess();
env(pay(gw, alice, XRP(50)));
checkDeliveredAmount.adjCountersSuccess();
// partial payment
env(pay(gw, bob, USD(9999999)), txflags(tfPartialPayment));
checkDeliveredAmount.adjCountersPartialPayment();
env.require(balance(bob, USD(1000)));
// partial payment
env(pay(gw, bob, USD(9999999)), txflags(tfPartialPayment));
checkDeliveredAmount.adjCountersPartialPayment();
env.require(balance(bob, USD(1000)));
// failed payment
env(pay(bob, carol, USD(9999999)), ter(tecPATH_PARTIAL));
checkDeliveredAmount.adjCountersFail();
env.require(balance(carol, USD(0)));
}
auto wsc = makeWSClient(env.app().config());
{
Json::Value stream;
// RPC subscribe to ledger stream
stream[jss::streams] = Json::arrayValue;
stream[jss::streams].append("ledger");
stream[jss::accounts] = Json::arrayValue;
stream[jss::accounts].append(toBase58(alice.id()));
stream[jss::accounts].append(toBase58(bob.id()));
stream[jss::accounts].append(toBase58(carol.id()));
auto jv = wsc->invoke("subscribe", stream);
if (wsc->version() == 2)
{
BEAST_EXPECT(
jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(
jv.isMember(jss::ripplerpc) &&
jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::result][jss::ledger_index] == 3);
}
{
env.close();
// Check stream update
while (true)
{
auto const r = wsc->findMsg(1s, [&](auto const& jv) {
return jv[jss::ledger_index] == 4;
});
if (!r)
break;
if (!r->isMember(jss::transaction))
continue;
BEAST_EXPECT(checkDeliveredAmount.checkTxn(
(*r)[jss::transaction], (*r)[jss::meta]));
}
}
BEAST_EXPECT(checkDeliveredAmount.checkExpectedCounters());
// failed payment
env(pay(bob, carol, USD(9999999)), ter(tecPATH_PARTIAL));
checkDeliveredAmount.adjCountersFail();
env.require(balance(carol, USD(0)));
}
auto wsc = makeWSClient(env.app().config());
{
Json::Value stream;
// RPC subscribe to ledger stream
stream[jss::streams] = Json::arrayValue;
stream[jss::streams].append("ledger");
stream[jss::accounts] = Json::arrayValue;
stream[jss::accounts].append(toBase58(alice.id()));
stream[jss::accounts].append(toBase58(bob.id()));
stream[jss::accounts].append(toBase58(carol.id()));
auto jv = wsc->invoke("subscribe", stream);
if (wsc->version() == 2)
{
BEAST_EXPECT(
jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
BEAST_EXPECT(
jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] == "2.0");
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
}
BEAST_EXPECT(jv[jss::result][jss::ledger_index] == 3);
}
{
env.close();
// Check stream update
while (true)
{
auto const r = wsc->findMsg(1s, [&](auto const& jv) {
return jv[jss::ledger_index] == 4;
});
if (!r)
break;
if (!r->isMember(jss::transaction))
continue;
BEAST_EXPECT(checkDeliveredAmount.checkTxn(
(*r)[jss::transaction], (*r)[jss::meta]));
}
}
BEAST_EXPECT(checkDeliveredAmount.checkExpectedCounters());
}
void
testTxDeliveredAmountRPC(FeatureBitset features)
@@ -280,49 +273,41 @@ class DeliveredAmount_test : public beast::unit_test::suite
auto const gw = Account("gateway");
auto const USD = gw["USD"];
for (bool const afterSwitchTime : {true, false})
{
Env env{*this, features};
env.fund(XRP(10000), alice, bob, carol, gw);
env.trust(USD(1000), alice, bob, carol);
if (afterSwitchTime)
env.close(NetClock::time_point{446000000s});
else
env.close();
Env env{*this, features};
env.fund(XRP(10000), alice, bob, carol, gw);
env.trust(USD(1000), alice, bob, carol);
env.close();
CheckDeliveredAmount checkDeliveredAmount{afterSwitchTime};
// normal payments
env(pay(gw, alice, USD(50)));
checkDeliveredAmount.adjCountersSuccess();
env(pay(gw, alice, XRP(50)));
checkDeliveredAmount.adjCountersSuccess();
CheckDeliveredAmount checkDeliveredAmount{true};
// normal payments
env(pay(gw, alice, USD(50)));
checkDeliveredAmount.adjCountersSuccess();
env(pay(gw, alice, XRP(50)));
checkDeliveredAmount.adjCountersSuccess();
// partial payment
env(pay(gw, bob, USD(9999999)), txflags(tfPartialPayment));
checkDeliveredAmount.adjCountersPartialPayment();
env.require(balance(bob, USD(1000)));
// partial payment
env(pay(gw, bob, USD(9999999)), txflags(tfPartialPayment));
checkDeliveredAmount.adjCountersPartialPayment();
env.require(balance(bob, USD(1000)));
// failed payment
env(pay(gw, carol, USD(9999999)), ter(tecPATH_PARTIAL));
checkDeliveredAmount.adjCountersFail();
env.require(balance(carol, USD(0)));
// failed payment
env(pay(gw, carol, USD(9999999)), ter(tecPATH_PARTIAL));
checkDeliveredAmount.adjCountersFail();
env.require(balance(carol, USD(0)));
env.close();
std::string index;
Json::Value jvParams;
jvParams[jss::ledger_index] = 4u;
jvParams[jss::transactions] = true;
jvParams[jss::expand] = true;
auto const jtxn = env.rpc(
"json",
"ledger",
to_string(
jvParams))[jss::result][jss::ledger][jss::transactions];
for (auto const& t : jtxn)
BEAST_EXPECT(
checkDeliveredAmount.checkTxn(t, t[jss::metaData]));
BEAST_EXPECT(checkDeliveredAmount.checkExpectedCounters());
}
env.close();
std::string index;
Json::Value jvParams;
jvParams[jss::ledger_index] = 4u;
jvParams[jss::transactions] = true;
jvParams[jss::expand] = true;
auto const jtxn = env.rpc(
"json",
"ledger",
to_string(jvParams))[jss::result][jss::ledger][jss::transactions];
for (auto const& t : jtxn)
BEAST_EXPECT(checkDeliveredAmount.checkTxn(t, t[jss::metaData]));
BEAST_EXPECT(checkDeliveredAmount.checkExpectedCounters());
}
public:

View File

@@ -17,8 +17,11 @@
*/
//==============================================================================
#include <ripple/app/misc/HashRouter.h>
#include <ripple/basics/StringUtilities.h>
#include <ripple/protocol/digest.h>
#include <ripple/protocol/jss.h>
#include <test/app/Import_json.h>
#include <test/jtx.h>
namespace ripple {
@@ -308,11 +311,16 @@ public:
// Put a bunch of different LedgerEntryTypes into a ledger
using namespace test::jtx;
using namespace std::chrono;
Env env{*this, envconfig(validator, "")};
std::vector<std::string> const keys = {
"ED74D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1CDC"
"1"};
Env env{*this, network::makeNetworkVLConfig(21337, keys)};
Account const alice{"alice"};
Account const gw{"gateway"};
auto const USD = gw["USD"];
env.fund(XRP(100000), gw);
env.fund(XRP(100000), gw, alice);
auto makeRequest = [&env](Json::StaticString const& type) {
Json::Value jvParams;
@@ -335,6 +343,10 @@ public:
jss::ticket,
jss::escrow,
jss::payment_channel,
jss::hook,
jss::hook_definition,
jss::hook_state,
jss::uri_token,
jss::deposit_preauth})
{
auto const jrr = makeRequest(type);
@@ -399,6 +411,12 @@ public:
env.close();
}
{
std::string const uri(maxTokenURILength, '?');
env(uritoken::mint(Account{"bob2"}, uri));
env.close();
}
{
Json::Value jv;
jv[jss::TransactionType] = jss::PaymentChannelCreate;
@@ -414,6 +432,63 @@ public:
env(jv);
}
{
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
env(import::import(
alice, import::loadXpop(test::ImportTCAccountSet::w_seed)),
fee(10 * 10),
ter(tesSUCCESS));
env.close();
}
{
// ADD UNL REPORT
std::vector<std::string> const _ivlKeys = {
"ED74D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1"
"CDC1",
"ED74D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1"
"CDC2",
};
std::vector<PublicKey> ivlKeys;
for (auto const& strPk : _ivlKeys)
{
auto pkHex = strUnHex(strPk);
ivlKeys.emplace_back(makeSlice(*pkHex));
}
std::vector<std::string> const _vlKeys = {
"ED8E43A943A174190BA2FAE91F44AC6E2D1D8202EFDCC2EA3DBB39814576D6"
"90F7",
"ED45D1840EE724BE327ABE9146503D5848EFD5F38B6D5FEDE71E80ACCE5E6E"
"738B"};
std::vector<PublicKey> vlKeys;
for (auto const& strPk : _vlKeys)
{
auto pkHex = strUnHex(strPk);
vlKeys.emplace_back(makeSlice(*pkHex));
}
// insert a ttUNL_REPORT pseudo into the open ledger
env.app().openLedger().modify(
[&](OpenView& view, beast::Journal j) -> bool {
STTx tx = test::unl::createUNLReportTx(
env.current()->seq() + 1, ivlKeys[0], vlKeys[0]);
uint256 txID = tx.getTransactionID();
auto s = std::make_shared<ripple::Serializer>();
tx.add(*s);
env.app().getHashRouter().setFlags(txID, SF_PRIVATE2);
view.rawTxInsert(txID, std::move(s), nullptr);
return true;
});
// close the ledger
env.close();
}
env(check::create("bob6", "bob7", XRP(100)));
// bob9 DepositPreauths bob4 and bob8.
@@ -425,18 +500,26 @@ public:
{ // jvParams[jss::type] = "account";
auto const jrr = makeRequest(jss::account);
BEAST_EXPECT(checkArraySize(jrr[jss::state], 12));
BEAST_EXPECT(checkArraySize(jrr[jss::state], 13));
for (auto const& j : jrr[jss::state])
BEAST_EXPECT(j["LedgerEntryType"] == jss::AccountRoot);
}
{ // jvParams[jss::type] = "amendments";
auto const jrr = makeRequest(jss::amendments);
BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
BEAST_EXPECT(checkArraySize(jrr[jss::state], 0));
for (auto const& j : jrr[jss::state])
BEAST_EXPECT(j["LedgerEntryType"] == jss::Amendments);
}
{ // jvParams[jss::type] = "unl_report";
auto const jrr = makeRequest(jss::unl_report);
BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
for (auto const& j : jrr[jss::state])
BEAST_EXPECT(j["LedgerEntryType"] == jss::UNLReport);
}
{ // jvParams[jss::type] = "check";
auto const jrr = makeRequest(jss::check);
BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
@@ -465,6 +548,13 @@ public:
BEAST_EXPECT(j["LedgerEntryType"] == jss::LedgerHashes);
}
{ // jvParams[jss::type] = "import_vlseq";
auto const jrr = makeRequest(jss::import_vlseq);
BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
for (auto const& j : jrr[jss::state])
BEAST_EXPECT(j["LedgerEntryType"] == jss::ImportVLSequence);
}
{ // jvParams[jss::type] = "offer";
auto const jrr = makeRequest(jss::offer);
BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
@@ -521,6 +611,13 @@ public:
BEAST_EXPECT(j["LedgerEntryType"] == jss::HookState);
}
{ // jvParams[jss::type] = "uri_token";
auto const jrr = makeRequest(jss::uri_token);
BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));
for (auto const& j : jrr[jss::state])
BEAST_EXPECT(j["LedgerEntryType"] == jss::URIToken);
}
{ // jvParams[jss::type] = "payment_channel";
auto const jrr = makeRequest(jss::payment_channel);
BEAST_EXPECT(checkArraySize(jrr[jss::state], 1));

View File

@@ -1707,6 +1707,39 @@ public:
BEAST_EXPECT(jrr[jss::node][sfURI.jsonName] == strHex(uri));
BEAST_EXPECT(jrr[jss::node][sfFlags.jsonName] == lsfBurnable);
}
{
// Request the uritoken using its account and uri.
Json::Value jvParams;
jvParams[jss::uri_token] = Json::objectValue;
jvParams[jss::uri_token][jss::account] = alice.human();
jvParams[jss::uri_token][jss::uri] = uri;
jvParams[jss::ledger_hash] = ledgerHash;
Json::Value const jrr = env.rpc(
"json", "ledger_entry", to_string(jvParams))[jss::result];
BEAST_EXPECT(jrr[jss::node][sfOwner.jsonName] == alice.human());
BEAST_EXPECT(jrr[jss::node][sfURI.jsonName] == strHex(uri));
BEAST_EXPECT(jrr[jss::node][sfFlags.jsonName] == lsfBurnable);
}
{
// Malformed uritoken object. Missing account member.
Json::Value jvParams;
jvParams[jss::uri_token] = Json::objectValue;
jvParams[jss::uri_token][jss::uri] = uri;
jvParams[jss::ledger_hash] = ledgerHash;
Json::Value const jrr = env.rpc(
"json", "ledger_entry", to_string(jvParams))[jss::result];
checkErrorValue(jrr, "malformedRequest", "");
}
{
// Malformed uritoken object. Missing seq member.
Json::Value jvParams;
jvParams[jss::uri_token] = Json::objectValue;
jvParams[jss::uri_token][jss::account] = env.master.human();
jvParams[jss::ledger_hash] = ledgerHash;
Json::Value const jrr = env.rpc(
"json", "ledger_entry", to_string(jvParams))[jss::result];
checkErrorValue(jrr, "malformedRequest", "");
}
{
// Request an index that is not a uritoken.
Json::Value jvParams;
@@ -1718,6 +1751,93 @@ public:
}
}
void
testLedgerEntryImportVLSeq()
{
testcase("ledger_entry Request ImportVLSeq");
using namespace test::jtx;
std::vector<std::string> const keys = {
"ED74D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1CDC"
"1"};
Env env{*this, network::makeNetworkVLConfig(21337, keys)};
Account const alice{"alice"};
env.fund(XRP(10000), alice);
env.close();
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
env(import::import(
alice, import::loadXpop(test::ImportTCAccountSet::w_seed)),
fee(10 * 10),
ter(tesSUCCESS));
env.close();
std::string const ledgerHash{to_string(env.closed()->info().hash)};
auto const pk = PublicKey(makeSlice(*strUnHex(keys[0u])));
uint256 const importvlIndex{keylet::import_vlseq(pk).key};
{
// Request the import vl using its index.
Json::Value jvParams;
jvParams[jss::import_vlseq] = to_string(importvlIndex);
jvParams[jss::ledger_hash] = ledgerHash;
Json::Value const jrr = env.rpc(
"json", "ledger_entry", to_string(jvParams))[jss::result];
BEAST_EXPECT(jrr[jss::node][sfPublicKey.jsonName] == keys[0u]);
}
{
// Request the import vl using its public key.
Json::Value jvParams;
jvParams[jss::import_vlseq] = Json::objectValue;
jvParams[jss::import_vlseq][jss::public_key] = keys[0u];
jvParams[jss::ledger_hash] = ledgerHash;
Json::Value const jrr = env.rpc(
"json", "ledger_entry", to_string(jvParams))[jss::result];
BEAST_EXPECT(jrr[jss::node][sfPublicKey.jsonName] == keys[0u]);
}
{
// Malformed import vl object. Missing public key.
Json::Value jvParams;
jvParams[jss::import_vlseq] = Json::objectValue;
jvParams[jss::ledger_hash] = ledgerHash;
Json::Value const jrr = env.rpc(
"json", "ledger_entry", to_string(jvParams))[jss::result];
checkErrorValue(jrr, "malformedRequest", "");
}
{
// Malformed import vl object. Bad public key.
Json::Value jvParams;
jvParams[jss::import_vlseq] = Json::objectValue;
jvParams[jss::import_vlseq][jss::public_key] = 1;
jvParams[jss::ledger_hash] = ledgerHash;
Json::Value const jrr = env.rpc(
"json", "ledger_entry", to_string(jvParams))[jss::result];
checkErrorValue(jrr, "malformedRequest", "");
}
{
// Malformed import vl object. Bad public key.
Json::Value jvParams;
jvParams[jss::import_vlseq] = Json::objectValue;
jvParams[jss::import_vlseq][jss::public_key] = "DEADBEEF";
jvParams[jss::ledger_hash] = ledgerHash;
Json::Value const jrr = env.rpc(
"json", "ledger_entry", to_string(jvParams))[jss::result];
checkErrorValue(jrr, "malformedRequest", "");
}
{
// Request an index that is not a uritoken.
Json::Value jvParams;
jvParams[jss::import_vlseq] = ledgerHash;
jvParams[jss::ledger_hash] = ledgerHash;
Json::Value const jrr = env.rpc(
"json", "ledger_entry", to_string(jvParams))[jss::result];
checkErrorValue(jrr, "entryNotFound", "");
}
}
void
testLedgerEntryUnknownOption()
{
@@ -2242,6 +2362,7 @@ public:
testLedgerEntryRippleState();
testLedgerEntryTicket();
testLedgerEntryURIToken();
testLedgerEntryImportVLSeq();
testLedgerEntryUnknownOption();
testLookupLedger();
testNoQueue();

View File

@@ -63,6 +63,9 @@ public:
BEAST_EXPECT(
result[jss::result].isMember(jss::TRANSACTION_RESULTS));
BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_TYPES));
BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_FLAGS));
BEAST_EXPECT(
result[jss::result].isMember(jss::TRANSACTION_FLAGS_INDICES));
BEAST_EXPECT(result[jss::result].isMember(jss::TYPES));
BEAST_EXPECT(result[jss::result].isMember(jss::hash));
BEAST_EXPECT(result[jss::result][jss::status] == "success");
@@ -70,7 +73,7 @@ public:
}
void
testDefitionsHash(FeatureBitset features)
testDefinitionsHash(FeatureBitset features)
{
testcase("Definitions Hash");
@@ -92,6 +95,9 @@ public:
BEAST_EXPECT(
!result[jss::result].isMember(jss::TRANSACTION_RESULTS));
BEAST_EXPECT(!result[jss::result].isMember(jss::TRANSACTION_TYPES));
BEAST_EXPECT(!result[jss::result].isMember(jss::TRANSACTION_FLAGS));
BEAST_EXPECT(
!result[jss::result].isMember(jss::TRANSACTION_FLAGS_INDICES));
BEAST_EXPECT(!result[jss::result].isMember(jss::TYPES));
BEAST_EXPECT(result[jss::result].isMember(jss::hash));
}
@@ -113,6 +119,9 @@ public:
BEAST_EXPECT(
result[jss::result].isMember(jss::TRANSACTION_RESULTS));
BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_TYPES));
BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_FLAGS));
BEAST_EXPECT(
result[jss::result].isMember(jss::TRANSACTION_FLAGS_INDICES));
BEAST_EXPECT(result[jss::result].isMember(jss::TYPES));
BEAST_EXPECT(result[jss::result].isMember(jss::hash));
}
@@ -330,7 +339,7 @@ public:
testServerDefinitions(FeatureBitset features)
{
testDefinitions(features);
testDefitionsHash(features);
testDefinitionsHash(features);
}
void