mirror of
https://github.com/XRPLF/rippled.git
synced 2026-03-06 04:42:30 +00:00
Compare commits
37 Commits
legleux/us
...
ximinez/nu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2191ef8d75 | ||
|
|
ea4f922492 | ||
|
|
9250ba9e27 | ||
|
|
61f38ba068 | ||
|
|
3d5ff2c8a2 | ||
|
|
e27249134a | ||
|
|
d79fdec886 | ||
|
|
024d05b70c | ||
|
|
ffb3e1da53 | ||
|
|
aef7e5b335 | ||
|
|
e2c09e79d0 | ||
|
|
c6f854bbd8 | ||
|
|
6a1e0b0f5a | ||
|
|
01f5ae0927 | ||
|
|
9b4587f9af | ||
|
|
fbc6f87983 | ||
|
|
0871eb0cb6 | ||
|
|
2ccf132f79 | ||
|
|
6600153958 | ||
|
|
fff73dac51 | ||
|
|
06ff77458a | ||
|
|
f19ecb3b80 | ||
|
|
cc2406bf3f | ||
|
|
30c65320e4 | ||
|
|
569d9ea94e | ||
|
|
02b7bcfa2b | ||
|
|
07c0c320a7 | ||
|
|
d57e37c34b | ||
|
|
154bb65c35 | ||
|
|
111eda22e9 | ||
|
|
f7b6834d2a | ||
|
|
e464adaee6 | ||
|
|
cca92dedca | ||
|
|
3d6f57a4df | ||
|
|
fc29fbe946 | ||
|
|
5e0a8d5c8a | ||
|
|
d27788f12a |
26
.github/workflows/publish-docs.yml
vendored
26
.github/workflows/publish-docs.yml
vendored
@@ -40,9 +40,11 @@ env:
|
||||
NPROC_SUBTRACT: ${{ github.event.repository.private && '1' || '2' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/xrplf/ci/tools-rippled-documentation:sha-a8c7be1
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
@@ -81,23 +83,9 @@ jobs:
|
||||
cmake -Donly_docs=ON ..
|
||||
cmake --build . --target docs --parallel ${BUILD_NPROC}
|
||||
|
||||
- name: Create documentation artifact
|
||||
- name: Publish documentation
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0
|
||||
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0
|
||||
with:
|
||||
path: ${{ env.BUILD_DIR }}/docs/html
|
||||
|
||||
deploy:
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pages: write
|
||||
id-token: write
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deploy.outputs.page_url }}
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deploy
|
||||
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ${{ env.BUILD_DIR }}/docs/html
|
||||
|
||||
@@ -22,19 +22,6 @@ API version 2 is available in `rippled` version 2.0.0 and later. See [API-VERSIO
|
||||
|
||||
This version is supported by all `rippled` versions. For WebSocket and HTTP JSON-RPC requests, it is currently the default API version used when no `api_version` is specified.
|
||||
|
||||
## Unreleased
|
||||
|
||||
This section contains changes targeting a future version.
|
||||
|
||||
### Additions
|
||||
|
||||
- `server_definitions`: Added the following new sections to the response ([#6321](https://github.com/XRPLF/rippled/pull/6321)):
|
||||
- `TRANSACTION_FORMATS`: Describes the fields and their optionality for each transaction type, including common fields shared across all transactions.
|
||||
- `LEDGER_ENTRY_FORMATS`: Describes the fields and their optionality for each ledger entry type, including common fields shared across all ledger entries.
|
||||
- `TRANSACTION_FLAGS`: Maps transaction type names to their supported flags and flag values.
|
||||
- `LEDGER_ENTRY_FLAGS`: Maps ledger entry type names to their flags and flag values.
|
||||
- `ACCOUNT_SET_FLAGS`: Maps AccountSet flag names (asf flags) to their numeric values.
|
||||
|
||||
## XRP Ledger server version 3.1.0
|
||||
|
||||
[Version 3.1.0](https://github.com/XRPLF/rippled/releases/tag/3.1.0) was released on Jan 27, 2026.
|
||||
|
||||
52
cmake/XrplConfig.cmake
Normal file
52
cmake/XrplConfig.cmake
Normal file
@@ -0,0 +1,52 @@
|
||||
include(CMakeFindDependencyMacro)
|
||||
# need to represent system dependencies of the lib here
|
||||
#[=========================================================[
|
||||
Boost
|
||||
#]=========================================================]
|
||||
if (static OR APPLE OR MSVC)
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
endif ()
|
||||
set(Boost_USE_MULTITHREADED ON)
|
||||
if (static OR MSVC)
|
||||
set(Boost_USE_STATIC_RUNTIME ON)
|
||||
else ()
|
||||
set(Boost_USE_STATIC_RUNTIME OFF)
|
||||
endif ()
|
||||
find_dependency(Boost
|
||||
COMPONENTS
|
||||
chrono
|
||||
container
|
||||
context
|
||||
coroutine
|
||||
date_time
|
||||
filesystem
|
||||
program_options
|
||||
regex
|
||||
system
|
||||
thread)
|
||||
#[=========================================================[
|
||||
OpenSSL
|
||||
#]=========================================================]
|
||||
if (NOT DEFINED OPENSSL_ROOT_DIR)
|
||||
if (DEFINED ENV{OPENSSL_ROOT})
|
||||
set(OPENSSL_ROOT_DIR $ENV{OPENSSL_ROOT})
|
||||
elseif (APPLE)
|
||||
find_program(homebrew brew)
|
||||
if (homebrew)
|
||||
execute_process(COMMAND ${homebrew} --prefix openssl OUTPUT_VARIABLE OPENSSL_ROOT_DIR
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif ()
|
||||
endif ()
|
||||
file(TO_CMAKE_PATH "${OPENSSL_ROOT_DIR}" OPENSSL_ROOT_DIR)
|
||||
endif ()
|
||||
|
||||
if (static OR APPLE OR MSVC)
|
||||
set(OPENSSL_USE_STATIC_LIBS ON)
|
||||
endif ()
|
||||
set(OPENSSL_MSVC_STATIC_RT ON)
|
||||
find_dependency(OpenSSL REQUIRED)
|
||||
find_dependency(ZLIB)
|
||||
find_dependency(date)
|
||||
if (TARGET ZLIB::ZLIB)
|
||||
set_target_properties(OpenSSL::Crypto PROPERTIES INTERFACE_LINK_LIBRARIES ZLIB::ZLIB)
|
||||
endif ()
|
||||
@@ -2,28 +2,13 @@
|
||||
install stuff
|
||||
#]===================================================================]
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
include(GNUInstallDirs)
|
||||
include(create_symbolic_link)
|
||||
|
||||
set(xrpl_cmakedir "${CMAKE_INSTALL_LIBDIR}/cmake/xrpl")
|
||||
|
||||
if(is_root_project AND TARGET xrpld)
|
||||
install(TARGETS xrpld
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
COMPONENT runtime
|
||||
)
|
||||
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/cfg/xrpld-example.cfg"
|
||||
DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}/xrpld"
|
||||
RENAME xrpld.cfg
|
||||
COMPONENT runtime
|
||||
)
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/cfg/validators-example.txt"
|
||||
DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}/xrpld"
|
||||
RENAME validators.txt
|
||||
COMPONENT runtime
|
||||
)
|
||||
endif()
|
||||
# If no suffix is defined for executables (e.g. Windows uses .exe but Linux
|
||||
# and macOS use none), then explicitly set it to the empty string.
|
||||
if (NOT DEFINED suffix)
|
||||
set(suffix "")
|
||||
endif ()
|
||||
|
||||
install(TARGETS common
|
||||
opts
|
||||
@@ -49,14 +34,46 @@ install(TARGETS common
|
||||
xrpl.libxrpl.server
|
||||
xrpl.libxrpl.shamap
|
||||
xrpl.libxrpl.tx
|
||||
EXPORT xrpl_targets
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT development
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT development
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT development
|
||||
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||
)
|
||||
antithesis-sdk-cpp
|
||||
EXPORT XrplExports
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
RUNTIME DESTINATION bin
|
||||
INCLUDES
|
||||
DESTINATION include)
|
||||
|
||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/xrpl"
|
||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||
COMPONENT development
|
||||
)
|
||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
|
||||
install(EXPORT XrplExports FILE XrplTargets.cmake NAMESPACE Xrpl:: DESTINATION lib/cmake/xrpl)
|
||||
include(CMakePackageConfigHelpers)
|
||||
write_basic_package_version_file(XrplConfigVersion.cmake VERSION ${xrpld_version}
|
||||
COMPATIBILITY SameMajorVersion)
|
||||
|
||||
if (is_root_project AND TARGET xrpld)
|
||||
install(TARGETS xrpld RUNTIME DESTINATION bin)
|
||||
set_target_properties(xrpld PROPERTIES INSTALL_RPATH_USE_LINK_PATH ON)
|
||||
# sample configs should not overwrite existing files
|
||||
# install if-not-exists workaround as suggested by
|
||||
# https://cmake.org/Bug/view.php?id=12646
|
||||
install(CODE "
|
||||
macro (copy_if_not_exists SRC DEST NEWNAME)
|
||||
if (NOT EXISTS \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\${DEST}/\${NEWNAME}\")
|
||||
file (INSTALL FILE_PERMISSIONS OWNER_READ OWNER_WRITE DESTINATION \"\${CMAKE_INSTALL_PREFIX}/\${DEST}\" FILES \"\${SRC}\" RENAME \"\${NEWNAME}\")
|
||||
else ()
|
||||
message (\"-- Skipping : \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\${DEST}/\${NEWNAME}\")
|
||||
endif ()
|
||||
endmacro()
|
||||
copy_if_not_exists(\"${CMAKE_CURRENT_SOURCE_DIR}/cfg/xrpld-example.cfg\" etc xrpld.cfg)
|
||||
copy_if_not_exists(\"${CMAKE_CURRENT_SOURCE_DIR}/cfg/validators-example.txt\" etc validators.txt)
|
||||
")
|
||||
install(CODE "
|
||||
set(CMAKE_MODULE_PATH \"${CMAKE_MODULE_PATH}\")
|
||||
include(create_symbolic_link)
|
||||
create_symbolic_link(xrpld${suffix} \
|
||||
\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/rippled${suffix})
|
||||
")
|
||||
endif ()
|
||||
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/XrplConfig.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/XrplConfigVersion.cmake DESTINATION lib/cmake/xrpl)
|
||||
|
||||
@@ -15,6 +15,6 @@ if (validator_keys)
|
||||
GIT_TAG "${current_branch}")
|
||||
FetchContent_MakeAvailable(validator_keys)
|
||||
set_target_properties(validator-keys PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
|
||||
install(TARGETS validator-keys RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime)
|
||||
install(TARGETS validator-keys RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
endif ()
|
||||
|
||||
@@ -50,6 +50,5 @@ function (target_protobuf_sources target prefix)
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
# Allows #include "path/to/file.proto" used by generated files.
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${prefix}>)
|
||||
install(DIRECTORY ${dir}/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
COMPONENT development FILES_MATCHING PATTERN "*.h")
|
||||
install(DIRECTORY ${dir}/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.h")
|
||||
endfunction ()
|
||||
|
||||
@@ -2,13 +2,19 @@
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
#endif // !defined(_MSC_VER)
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class Number;
|
||||
@@ -16,18 +22,39 @@ class Number;
|
||||
std::string
|
||||
to_string(Number const& amount);
|
||||
|
||||
/** Returns a rough estimate of log10(value).
|
||||
*
|
||||
* The return value is a pair (log, rem), where log is the estimated
|
||||
* base-10 logarithm (roughly floor(log10(value))), and rem is value with
|
||||
* all trailing 0s removed (i.e., divided by the largest power of 10 that
|
||||
* evenly divides value). If rem is 1, then value is an exact power of ten, and
|
||||
* log is the exact log10(value).
|
||||
*
|
||||
* This function only works for positive values.
|
||||
*/
|
||||
template <std::unsigned_integral T>
|
||||
constexpr std::pair<int, T>
|
||||
logTenEstimate(T value)
|
||||
{
|
||||
int log = 0;
|
||||
T remainder = value;
|
||||
while (value >= 10)
|
||||
{
|
||||
if (value % 10 == 0)
|
||||
remainder = remainder / 10;
|
||||
value /= 10;
|
||||
++log;
|
||||
}
|
||||
return {log, remainder};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr std::optional<int>
|
||||
logTen(T value)
|
||||
{
|
||||
int log = 0;
|
||||
while (value >= 10 && value % 10 == 0)
|
||||
{
|
||||
value /= 10;
|
||||
++log;
|
||||
}
|
||||
if (value == 1)
|
||||
return log;
|
||||
auto const est = logTenEstimate(value);
|
||||
if (est.second == 1)
|
||||
return est.first;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -41,12 +68,10 @@ isPowerOfTen(T value)
|
||||
/** MantissaRange defines a range for the mantissa of a normalized Number.
|
||||
*
|
||||
* The mantissa is in the range [min, max], where
|
||||
* * min is a power of 10, and
|
||||
* * max = min * 10 - 1.
|
||||
*
|
||||
* The mantissa_scale enum indicates whether the range is "small" or "large".
|
||||
* This intentionally restricts the number of MantissaRanges that can be
|
||||
* instantiated to two: one for each scale.
|
||||
* The mantissa_scale enum indicates whether the range is "small" or
|
||||
* "large". This intentionally prevents the creation of any
|
||||
* MantissaRanges representing other values.
|
||||
*
|
||||
* The "small" scale is based on the behavior of STAmount for IOUs. It has a min
|
||||
* value of 10^15, and a max value of 10^16-1. This was sufficient for
|
||||
@@ -60,8 +85,8 @@ isPowerOfTen(T value)
|
||||
* "large" scale.
|
||||
*
|
||||
* The "large" scale is intended to represent all values that can be represented
|
||||
* by an STAmount - IOUs, XRP, and MPTs. It has a min value of 10^18, and a max
|
||||
* value of 10^19-1.
|
||||
* by an STAmount - IOUs, XRP, and MPTs. It has a min value of 2^63/10+1
|
||||
* (truncated), and a max value of 2^63-1.
|
||||
*
|
||||
* Note that if the mentioned amendments are eventually retired, this class
|
||||
* should be left in place, but the "small" scale option should be removed. This
|
||||
@@ -73,25 +98,56 @@ struct MantissaRange
|
||||
enum mantissa_scale { small, large };
|
||||
|
||||
explicit constexpr MantissaRange(mantissa_scale scale_)
|
||||
: min(getMin(scale_)), max(min * 10 - 1), log(logTen(min).value_or(-1)), scale(scale_)
|
||||
: max(getMax(scale_))
|
||||
, min(computeMin(max))
|
||||
, internalMin(getInternalMin(scale_, min))
|
||||
, log(computeLog(min))
|
||||
, scale(scale_)
|
||||
{
|
||||
// Keep the error messages terse. Since this is constexpr, if any of these throw, it won't
|
||||
// compile, so there's no real need to worry about runtime exceptions here.
|
||||
if (min * 10 <= max)
|
||||
throw std::out_of_range("Invalid mantissa range: min * 10 <= max");
|
||||
if (max / 10 >= min)
|
||||
throw std::out_of_range("Invalid mantissa range: max / 10 >= min");
|
||||
if ((min - 1) * 10 > max)
|
||||
throw std::out_of_range("Invalid mantissa range: (min - 1) * 10 > max");
|
||||
// This is a little hacky
|
||||
if ((max + 10) / 10 < min)
|
||||
throw std::out_of_range("Invalid mantissa range: (max + 10) / 10 < min");
|
||||
if (computeLog(internalMin) != log)
|
||||
throw std::out_of_range("Invalid mantissa range: computeLog(internalMin) != log");
|
||||
}
|
||||
|
||||
rep min;
|
||||
// Explicitly delete copy and move operations
|
||||
MantissaRange(MantissaRange const&) = delete;
|
||||
MantissaRange(MantissaRange&&) = delete;
|
||||
MantissaRange&
|
||||
operator=(MantissaRange const&) = delete;
|
||||
MantissaRange&
|
||||
operator=(MantissaRange&&) = delete;
|
||||
|
||||
rep max;
|
||||
rep min;
|
||||
/* Used to determine if mantissas are in range, but have fewer digits than max.
|
||||
*
|
||||
* Unlike min, internalMin is always an exact power of 10, so a mantissa in the internal
|
||||
* representation will always have a consistent number of digits.
|
||||
*/
|
||||
rep internalMin;
|
||||
int log;
|
||||
mantissa_scale scale;
|
||||
|
||||
private:
|
||||
static constexpr rep
|
||||
getMin(mantissa_scale scale_)
|
||||
getMax(mantissa_scale scale)
|
||||
{
|
||||
switch (scale_)
|
||||
switch (scale)
|
||||
{
|
||||
case small:
|
||||
return 1'000'000'000'000'000ULL;
|
||||
return 9'999'999'999'999'999ULL;
|
||||
case large:
|
||||
return 1'000'000'000'000'000'000ULL;
|
||||
return std::numeric_limits<std::int64_t>::max();
|
||||
default:
|
||||
// Since this can never be called outside a non-constexpr
|
||||
// context, this throw assures that the build fails if an
|
||||
@@ -99,19 +155,59 @@ private:
|
||||
throw std::runtime_error("Unknown mantissa scale");
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr rep
|
||||
computeMin(rep max)
|
||||
{
|
||||
return max / 10 + 1;
|
||||
}
|
||||
|
||||
static constexpr rep
|
||||
getInternalMin(mantissa_scale scale, rep min)
|
||||
{
|
||||
switch (scale)
|
||||
{
|
||||
case large:
|
||||
return 1'000'000'000'000'000'000ULL;
|
||||
default:
|
||||
if (isPowerOfTen(min))
|
||||
return min;
|
||||
throw std::runtime_error("Unknown/bad mantissa scale");
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr rep
|
||||
computeLog(rep min)
|
||||
{
|
||||
auto const estimate = logTenEstimate(min);
|
||||
return estimate.first + (estimate.second == 1 ? 0 : 1);
|
||||
}
|
||||
};
|
||||
|
||||
// Like std::integral, but only 64-bit integral types.
|
||||
template <class T>
|
||||
concept Integral64 = std::is_same_v<T, std::int64_t> || std::is_same_v<T, std::uint64_t>;
|
||||
|
||||
namespace detail {
|
||||
#ifdef _MSC_VER
|
||||
using uint128_t = boost::multiprecision::uint128_t;
|
||||
using int128_t = boost::multiprecision::int128_t;
|
||||
#else // !defined(_MSC_VER)
|
||||
using uint128_t = __uint128_t;
|
||||
using int128_t = __int128_t;
|
||||
#endif // !defined(_MSC_VER)
|
||||
|
||||
template <class T>
|
||||
concept UnsignedMantissa = std::is_unsigned_v<T> || std::is_same_v<T, uint128_t>;
|
||||
} // namespace detail
|
||||
|
||||
/** Number is a floating point type that can represent a wide range of values.
|
||||
*
|
||||
* It can represent all values that can be represented by an STAmount -
|
||||
* regardless of asset type - XRPAmount, MPTAmount, and IOUAmount, with at least
|
||||
* as much precision as those types require.
|
||||
*
|
||||
* ---- Internal Representation ----
|
||||
* ---- Internal Operational Representation ----
|
||||
*
|
||||
* Internally, Number is represented with three values:
|
||||
* 1. a bool sign flag,
|
||||
@@ -126,15 +222,21 @@ concept Integral64 = std::is_same_v<T, std::int64_t> || std::is_same_v<T, std::u
|
||||
*
|
||||
* A non-zero mantissa is (almost) always normalized, meaning it and the
|
||||
* exponent are grown or shrunk until the mantissa is in the range
|
||||
* [MantissaRange.min, MantissaRange.max].
|
||||
* [MantissaRange.internalMin, MantissaRange.internalMin * 10 - 1].
|
||||
*
|
||||
* This internal representation is only used during some operations to ensure
|
||||
* that the mantissa is a known, predictable size. The class itself stores the
|
||||
* values using the external representation described below.
|
||||
*
|
||||
* Note:
|
||||
* 1. Normalization can be disabled by using the "unchecked" ctor tag. This
|
||||
* should only be used at specific conversion points, some constexpr
|
||||
* values, and in unit tests.
|
||||
* 2. The max of the "large" range, 10^19-1, is the largest 10^X-1 value that
|
||||
* fits in an unsigned 64-bit number. (10^19-1 < 2^64-1 and
|
||||
* 10^20-1 > 2^64-1). This avoids under- and overflows.
|
||||
* 2. Unlike MantissaRange.min, internalMin is always an exact power of 10,
|
||||
* so a mantissa in the internal representation will always have a
|
||||
* consistent number of digits.
|
||||
* 3. The functions toInternal() and fromInternal() are used to convert
|
||||
* between the two representations.
|
||||
*
|
||||
* ---- External Interface ----
|
||||
*
|
||||
@@ -147,13 +249,12 @@ concept Integral64 = std::is_same_v<T, std::int64_t> || std::is_same_v<T, std::u
|
||||
* represent the full range of valid XRP and MPT integer values accurately.
|
||||
*
|
||||
* Note:
|
||||
* 1. 2^63-1 is between 10^18 and 10^19-1, which are the limits of the "large"
|
||||
* mantissa range.
|
||||
* 1. The "large" mantissa range is (2^63/10+1) to 2^63-1. 2^63-1 is between
|
||||
* 10^18 and 10^19-1, and (2^63/10+1) is between 10^17 and 10^18-1. Thus,
|
||||
* the mantissa may have 18 or 19 digits. This value will be modified to
|
||||
* always have 19 digits before some operations to ensure consistency.
|
||||
* 2. The functions mantissa() and exponent() return the external view of the
|
||||
* Number value, specifically using a signed 63-bit mantissa. This may
|
||||
* require altering the internal representation to fit into that range
|
||||
* before the value is returned. The interface guarantees consistency of
|
||||
* the two values.
|
||||
* Number value, specifically using a signed 63-bit mantissa.
|
||||
* 3. Number cannot represent -2^63 (std::numeric_limits<std::int64_t>::min())
|
||||
* as an exact integer, but it doesn't need to, because all asset values
|
||||
* on-ledger are non-negative. This is due to implementation details of
|
||||
@@ -208,8 +309,7 @@ class Number
|
||||
using rep = std::int64_t;
|
||||
using internalrep = MantissaRange::rep;
|
||||
|
||||
bool negative_{false};
|
||||
internalrep mantissa_{0};
|
||||
rep mantissa_{0};
|
||||
int exponent_{std::numeric_limits<int>::lowest()};
|
||||
|
||||
public:
|
||||
@@ -217,10 +317,6 @@ public:
|
||||
constexpr static int minExponent = -32768;
|
||||
constexpr static int maxExponent = 32768;
|
||||
|
||||
constexpr static internalrep maxRep = std::numeric_limits<rep>::max();
|
||||
static_assert(maxRep == 9'223'372'036'854'775'807);
|
||||
static_assert(-maxRep == std::numeric_limits<rep>::min() + 1);
|
||||
|
||||
// May need to make unchecked private
|
||||
struct unchecked
|
||||
{
|
||||
@@ -298,8 +394,7 @@ public:
|
||||
friend constexpr bool
|
||||
operator==(Number const& x, Number const& y) noexcept
|
||||
{
|
||||
return x.negative_ == y.negative_ && x.mantissa_ == y.mantissa_ &&
|
||||
x.exponent_ == y.exponent_;
|
||||
return x.mantissa_ == y.mantissa_ && x.exponent_ == y.exponent_;
|
||||
}
|
||||
|
||||
friend constexpr bool
|
||||
@@ -313,8 +408,8 @@ public:
|
||||
{
|
||||
// If the two amounts have different signs (zero is treated as positive)
|
||||
// then the comparison is true iff the left is negative.
|
||||
bool const lneg = x.negative_;
|
||||
bool const rneg = y.negative_;
|
||||
bool const lneg = x.mantissa_ < 0;
|
||||
bool const rneg = y.mantissa_ < 0;
|
||||
|
||||
if (lneg != rneg)
|
||||
return lneg;
|
||||
@@ -342,7 +437,7 @@ public:
|
||||
constexpr int
|
||||
signum() const noexcept
|
||||
{
|
||||
return negative_ ? -1 : (mantissa_ ? 1 : 0);
|
||||
return mantissa_ < 0 ? -1 : (mantissa_ ? 1 : 0);
|
||||
}
|
||||
|
||||
Number
|
||||
@@ -381,6 +476,9 @@ public:
|
||||
friend Number
|
||||
root2(Number f);
|
||||
|
||||
friend Number
|
||||
power(Number const& f, unsigned n, unsigned d);
|
||||
|
||||
// Thread local rounding control. Default is to_nearest
|
||||
enum rounding_mode { to_nearest, towards_zero, downward, upward };
|
||||
static rounding_mode
|
||||
@@ -445,22 +543,39 @@ private:
|
||||
static_assert(isPowerOfTen(smallRange.min));
|
||||
static_assert(smallRange.min == 1'000'000'000'000'000LL);
|
||||
static_assert(smallRange.max == 9'999'999'999'999'999LL);
|
||||
static_assert(smallRange.internalMin == smallRange.min);
|
||||
static_assert(smallRange.log == 15);
|
||||
static_assert(smallRange.min < maxRep);
|
||||
static_assert(smallRange.max < maxRep);
|
||||
constexpr static MantissaRange largeRange{MantissaRange::large};
|
||||
static_assert(isPowerOfTen(largeRange.min));
|
||||
static_assert(largeRange.min == 1'000'000'000'000'000'000ULL);
|
||||
static_assert(largeRange.max == internalrep(9'999'999'999'999'999'999ULL));
|
||||
static_assert(!isPowerOfTen(largeRange.min));
|
||||
static_assert(largeRange.min == 922'337'203'685'477'581ULL);
|
||||
static_assert(largeRange.max == internalrep(9'223'372'036'854'775'807ULL));
|
||||
static_assert(largeRange.max == std::numeric_limits<rep>::max());
|
||||
static_assert(largeRange.internalMin == 1'000'000'000'000'000'000ULL);
|
||||
static_assert(largeRange.log == 18);
|
||||
static_assert(largeRange.min < maxRep);
|
||||
static_assert(largeRange.max > maxRep);
|
||||
// There are 2 values that will not fit in largeRange without some extra
|
||||
// work
|
||||
// * 9223372036854775808
|
||||
// * 9223372036854775809
|
||||
// They both end up < min, but with a leftover. If they round up, everything
|
||||
// will be fine. If they don't, we'll need to bring them up into range.
|
||||
// Guard::bringIntoRange handles this situation.
|
||||
|
||||
// The range for the mantissa when normalized.
|
||||
// Use reference_wrapper to avoid making copies, and prevent accidentally
|
||||
// changing the values inside the range.
|
||||
static thread_local std::reference_wrapper<MantissaRange const> range_;
|
||||
|
||||
// And one is needed because it needs to choose between oneSmall and
|
||||
// oneLarge based on the current range
|
||||
static Number
|
||||
one(MantissaRange const& range);
|
||||
|
||||
static Number
|
||||
root(MantissaRange const& range, Number f, unsigned d);
|
||||
|
||||
void
|
||||
normalize(MantissaRange const& range);
|
||||
|
||||
void
|
||||
normalize();
|
||||
|
||||
@@ -483,11 +598,14 @@ private:
|
||||
friend void
|
||||
doNormalize(
|
||||
bool& negative,
|
||||
T& mantissa_,
|
||||
int& exponent_,
|
||||
T& mantissa,
|
||||
int& exponent,
|
||||
MantissaRange::rep const& minMantissa,
|
||||
MantissaRange::rep const& maxMantissa);
|
||||
|
||||
bool
|
||||
isnormal(MantissaRange const& range) const noexcept;
|
||||
|
||||
bool
|
||||
isnormal() const noexcept;
|
||||
|
||||
@@ -497,14 +615,60 @@ private:
|
||||
Number
|
||||
shiftExponent(int exponentDelta) const;
|
||||
|
||||
// Safely convert rep (int64) mantissa to internalrep (uint64). If the rep
|
||||
// is negative, returns the positive value. This takes a little extra work
|
||||
// because converting std::numeric_limits<std::int64_t>::min() flirts with
|
||||
// UB, and can vary across compilers.
|
||||
// Safely return the absolute value of a rep (int64) mantissa as an internalrep (uint64).
|
||||
static internalrep
|
||||
externalToInternal(rep mantissa);
|
||||
|
||||
/** Breaks down the number into components, potentially de-normalizing it.
|
||||
*
|
||||
* Ensures that the mantissa always has range_.log + 1 digits.
|
||||
*
|
||||
*/
|
||||
template <detail::UnsignedMantissa Rep = internalrep>
|
||||
std::tuple<bool, Rep, int>
|
||||
toInternal(MantissaRange const& range) const;
|
||||
|
||||
/** Breaks down the number into components, potentially de-normalizing it.
|
||||
*
|
||||
* Ensures that the mantissa always has range_.log + 1 digits.
|
||||
*
|
||||
*/
|
||||
template <detail::UnsignedMantissa Rep = internalrep>
|
||||
std::tuple<bool, Rep, int>
|
||||
toInternal() const;
|
||||
|
||||
/** Rebuilds the number from components.
|
||||
*
|
||||
* If "expectNormal" is true, the values are expected to be normalized - all
|
||||
* in their valid ranges.
|
||||
*
|
||||
* If "expectNormal" is false, the values are expected to be "near
|
||||
* normalized", meaning that the mantissa has to be modified at most once to
|
||||
* bring it back into range.
|
||||
*
|
||||
*/
|
||||
template <bool expectNormal = true, detail::UnsignedMantissa Rep = internalrep>
|
||||
void
|
||||
fromInternal(bool negative, Rep mantissa, int exponent, MantissaRange const* pRange);
|
||||
|
||||
/** Rebuilds the number from components.
|
||||
*
|
||||
* If "expectNormal" is true, the values are expected to be normalized - all
|
||||
* in their valid ranges.
|
||||
*
|
||||
* If "expectNormal" is false, the values are expected to be "near
|
||||
* normalized", meaning that the mantissa has to be modified at most once to
|
||||
* bring it back into range.
|
||||
*
|
||||
*/
|
||||
template <bool expectNormal = true, detail::UnsignedMantissa Rep = internalrep>
|
||||
void
|
||||
fromInternal(bool negative, Rep mantissa, int exponent);
|
||||
|
||||
class Guard;
|
||||
|
||||
public:
|
||||
constexpr static internalrep largestMantissa = largeRange.max;
|
||||
};
|
||||
|
||||
inline constexpr Number::Number(
|
||||
@@ -512,7 +676,8 @@ inline constexpr Number::Number(
|
||||
internalrep mantissa,
|
||||
int exponent,
|
||||
unchecked) noexcept
|
||||
: negative_(negative), mantissa_{mantissa}, exponent_{exponent}
|
||||
: mantissa_{negative ? -static_cast<rep>(mantissa) : static_cast<rep>(mantissa)}
|
||||
, exponent_{exponent}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -523,12 +688,6 @@ inline constexpr Number::Number(internalrep mantissa, int exponent, unchecked) n
|
||||
|
||||
constexpr static Number numZero{};
|
||||
|
||||
inline Number::Number(bool negative, internalrep mantissa, int exponent, normalized)
|
||||
: Number(negative, mantissa, exponent, unchecked{})
|
||||
{
|
||||
normalize();
|
||||
}
|
||||
|
||||
inline Number::Number(internalrep mantissa, int exponent, normalized)
|
||||
: Number(false, mantissa, exponent, normalized{})
|
||||
{
|
||||
@@ -551,17 +710,7 @@ inline Number::Number(rep mantissa) : Number{mantissa, 0}
|
||||
inline constexpr Number::rep
|
||||
Number::mantissa() const noexcept
|
||||
{
|
||||
auto m = mantissa_;
|
||||
if (m > maxRep)
|
||||
{
|
||||
XRPL_ASSERT_PARTS(
|
||||
!isnormal() || (m % 10 == 0 && m / 10 <= maxRep),
|
||||
"xrpl::Number::mantissa",
|
||||
"large normalized mantissa has no remainder");
|
||||
m /= 10;
|
||||
}
|
||||
auto const sign = negative_ ? -1 : 1;
|
||||
return sign * static_cast<Number::rep>(m);
|
||||
return mantissa_;
|
||||
}
|
||||
|
||||
/** Returns the exponent of the external view of the Number.
|
||||
@@ -572,16 +721,7 @@ Number::mantissa() const noexcept
|
||||
inline constexpr int
|
||||
Number::exponent() const noexcept
|
||||
{
|
||||
auto e = exponent_;
|
||||
if (mantissa_ > maxRep)
|
||||
{
|
||||
XRPL_ASSERT_PARTS(
|
||||
!isnormal() || (mantissa_ % 10 == 0 && mantissa_ / 10 <= maxRep),
|
||||
"xrpl::Number::exponent",
|
||||
"large normalized mantissa has no remainder");
|
||||
++e;
|
||||
}
|
||||
return e;
|
||||
return exponent_;
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
@@ -596,7 +736,7 @@ Number::operator-() const noexcept
|
||||
if (mantissa_ == 0)
|
||||
return Number{};
|
||||
auto x = *this;
|
||||
x.negative_ = !x.negative_;
|
||||
x.mantissa_ = -x.mantissa_;
|
||||
return x;
|
||||
}
|
||||
|
||||
@@ -677,42 +817,58 @@ Number::min() noexcept
|
||||
inline Number
|
||||
Number::max() noexcept
|
||||
{
|
||||
return Number{false, std::min(range_.get().max, maxRep), maxExponent, unchecked{}};
|
||||
return Number{false, range_.get().max, maxExponent, unchecked{}};
|
||||
}
|
||||
|
||||
inline Number
|
||||
Number::lowest() noexcept
|
||||
{
|
||||
return Number{true, std::min(range_.get().max, maxRep), maxExponent, unchecked{}};
|
||||
return Number{true, range_.get().max, maxExponent, unchecked{}};
|
||||
}
|
||||
|
||||
inline bool
|
||||
Number::isnormal(MantissaRange const& range) const noexcept
|
||||
{
|
||||
auto const abs_m = externalToInternal(mantissa_);
|
||||
|
||||
return *this == Number{} ||
|
||||
(range.min <= abs_m && abs_m <= range.max && //
|
||||
minExponent <= exponent_ && exponent_ <= maxExponent);
|
||||
}
|
||||
|
||||
inline bool
|
||||
Number::isnormal() const noexcept
|
||||
{
|
||||
MantissaRange const& range = range_;
|
||||
auto const abs_m = mantissa_;
|
||||
return *this == Number{} ||
|
||||
(range.min <= abs_m && abs_m <= range.max && (abs_m <= maxRep || abs_m % 10 == 0) &&
|
||||
minExponent <= exponent_ && exponent_ <= maxExponent);
|
||||
return isnormal(range_);
|
||||
}
|
||||
|
||||
template <Integral64 T>
|
||||
std::pair<T, int>
|
||||
Number::normalizeToRange(T minMantissa, T maxMantissa) const
|
||||
{
|
||||
bool negative = negative_;
|
||||
internalrep mantissa = mantissa_;
|
||||
bool negative = mantissa_ < 0;
|
||||
internalrep mantissa = externalToInternal(mantissa_);
|
||||
int exponent = exponent_;
|
||||
|
||||
if constexpr (std::is_unsigned_v<T>)
|
||||
{
|
||||
XRPL_ASSERT_PARTS(
|
||||
!negative,
|
||||
"xrpl::Number::normalizeToRange",
|
||||
"Number is non-negative for unsigned range.");
|
||||
// To avoid logical errors in release builds, throw if the Number is
|
||||
// negative for an unsigned range.
|
||||
if (negative)
|
||||
throw std::runtime_error(
|
||||
"Number::normalizeToRange: Number is negative for "
|
||||
"unsigned range.");
|
||||
}
|
||||
Number::normalize(negative, mantissa, exponent, minMantissa, maxMantissa);
|
||||
|
||||
auto const sign = negative ? -1 : 1;
|
||||
return std::make_pair(static_cast<T>(sign * mantissa), exponent);
|
||||
// Cast mantissa to signed type first (if T is a signed type) to avoid
|
||||
// unsigned integer overflow when multiplying by negative sign
|
||||
T signedMantissa = negative ? -static_cast<T>(mantissa) : static_cast<T>(mantissa);
|
||||
return std::make_pair(signedMantissa, exponent);
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
|
||||
@@ -30,11 +30,9 @@ public:
|
||||
Item(
|
||||
char const* name,
|
||||
KeyType type,
|
||||
std::vector<SOElement> uniqueFields,
|
||||
std::vector<SOElement> commonFields)
|
||||
: soTemplate_(std::move(uniqueFields), std::move(commonFields))
|
||||
, name_(name)
|
||||
, type_(type)
|
||||
std::initializer_list<SOElement> uniqueFields,
|
||||
std::initializer_list<SOElement> commonFields)
|
||||
: soTemplate_(uniqueFields, commonFields), name_(name), type_(type)
|
||||
{
|
||||
// Verify that KeyType is appropriate.
|
||||
static_assert(
|
||||
@@ -144,16 +142,16 @@ protected:
|
||||
|
||||
@param name The name of this format.
|
||||
@param type The type of this format.
|
||||
@param uniqueFields A std::vector of unique fields
|
||||
@param commonFields A std::vector of common fields
|
||||
@param uniqueFields An std::initializer_list of unique fields
|
||||
@param commonFields An std::initializer_list of common fields
|
||||
|
||||
@return The created format.
|
||||
*/
|
||||
Item const&
|
||||
add(char const* name,
|
||||
KeyType type,
|
||||
std::vector<SOElement> uniqueFields,
|
||||
std::vector<SOElement> commonFields = {})
|
||||
std::initializer_list<SOElement> uniqueFields,
|
||||
std::initializer_list<SOElement> commonFields = {})
|
||||
{
|
||||
if (auto const item = findByType(type))
|
||||
{
|
||||
@@ -162,7 +160,7 @@ protected:
|
||||
item->getName());
|
||||
}
|
||||
|
||||
formats_.emplace_front(name, type, std::move(uniqueFields), std::move(commonFields));
|
||||
formats_.emplace_front(name, type, uniqueFields, commonFields);
|
||||
Item const& item{formats_.front()};
|
||||
|
||||
names_[name] = &item;
|
||||
|
||||
@@ -2,34 +2,36 @@
|
||||
|
||||
#include <xrpl/protocol/KnownFormats.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** Identifiers for on-ledger objects.
|
||||
|
||||
Each ledger object requires a unique type identifier, which is stored within the object itself;
|
||||
this makes it possible to iterate the entire ledger and determine each object's type and verify
|
||||
that the object you retrieved from a given hash matches the expected type.
|
||||
Each ledger object requires a unique type identifier, which is stored
|
||||
within the object itself; this makes it possible to iterate the entire
|
||||
ledger and determine each object's type and verify that the object you
|
||||
retrieved from a given hash matches the expected type.
|
||||
|
||||
@warning Since these values are stored inside objects stored on the ledger they are part of the
|
||||
protocol.
|
||||
**Changing them should be avoided because without special handling, this will result in a hard
|
||||
@warning Since these values are stored inside objects stored on the ledger
|
||||
they are part of the protocol. **Changing them should be avoided
|
||||
because without special handling, this will result in a hard
|
||||
fork.**
|
||||
|
||||
@note Values outside this range may be used internally by the code for various purposes, but
|
||||
attempting to use such values to identify on-ledger objects will result in an invariant failure.
|
||||
@note Values outside this range may be used internally by the code for
|
||||
various purposes, but attempting to use such values to identify
|
||||
on-ledger objects will results in an invariant failure.
|
||||
|
||||
@note When retiring types, the specific values should not be removed but should be marked as
|
||||
[[deprecated]]. This is to avoid accidental reuse of identifiers.
|
||||
@note When retiring types, the specific values should not be removed but
|
||||
should be marked as [[deprecated]]. This is to avoid accidental
|
||||
reuse of identifiers.
|
||||
|
||||
@todo The C++ language does not enable checking for duplicate values here.
|
||||
If it becomes possible then we should do this.
|
||||
@todo The C++ language does not enable checking for duplicate values
|
||||
here. If it becomes possible then we should do this.
|
||||
|
||||
@ingroup protocol
|
||||
*/
|
||||
enum LedgerEntryType : std::uint16_t {
|
||||
// clang-format off
|
||||
enum LedgerEntryType : std::uint16_t
|
||||
{
|
||||
|
||||
#pragma push_macro("LEDGER_ENTRY")
|
||||
#undef LEDGER_ENTRY
|
||||
@@ -44,10 +46,12 @@ enum LedgerEntryType : std::uint16_t {
|
||||
//---------------------------------------------------------------------------
|
||||
/** A special type, matching any ledger entry type.
|
||||
|
||||
The value does not represent a concrete type, but rather is used in contexts where the
|
||||
specific type of a ledger object is unimportant, unknown or unavailable.
|
||||
The value does not represent a concrete type, but rather is used in
|
||||
contexts where the specific type of a ledger object is unimportant,
|
||||
unknown or unavailable.
|
||||
|
||||
Objects with this special type cannot be created or stored on the ledger.
|
||||
Objects with this special type cannot be created or stored on the
|
||||
ledger.
|
||||
|
||||
\sa keylet::unchecked
|
||||
*/
|
||||
@@ -55,11 +59,12 @@ enum LedgerEntryType : std::uint16_t {
|
||||
|
||||
/** A special type, matching any ledger type except directory nodes.
|
||||
|
||||
The value does not represent a concrete type, but rather is used in contexts where the
|
||||
ledger object must not be a directory node but its specific type is otherwise unimportant,
|
||||
unknown or unavailable.
|
||||
The value does not represent a concrete type, but rather is used in
|
||||
contexts where the ledger object must not be a directory node but
|
||||
its specific type is otherwise unimportant, unknown or unavailable.
|
||||
|
||||
Objects with this special type cannot be created or stored on the ledger.
|
||||
Objects with this special type cannot be created or stored on the
|
||||
ledger.
|
||||
|
||||
\sa keylet::child
|
||||
*/
|
||||
@@ -88,188 +93,104 @@ enum LedgerEntryType : std::uint16_t {
|
||||
Support for this type of object was never implemented.
|
||||
No objects of this type were ever created.
|
||||
*/
|
||||
ltGENERATOR_MAP [[deprecated("This object type is not supported and should not be used.")]] =
|
||||
0x0067,
|
||||
ltGENERATOR_MAP [[deprecated("This object type is not supported and should not be used.")]] = 0x0067,
|
||||
};
|
||||
|
||||
/** Ledger object flags.
|
||||
|
||||
These flags are specified in ledger objects and modify their behavior.
|
||||
|
||||
@warning Ledger object flags form part of the protocol.
|
||||
**Changing them should be avoided because without special handling, this will result in a hard
|
||||
fork.**
|
||||
|
||||
@ingroup protocol
|
||||
*/
|
||||
#pragma push_macro("XMACRO")
|
||||
#pragma push_macro("TO_VALUE")
|
||||
#pragma push_macro("VALUE_TO_MAP")
|
||||
#pragma push_macro("NULL_NAME")
|
||||
#pragma push_macro("TO_MAP")
|
||||
#pragma push_macro("ALL_LEDGER_FLAGS")
|
||||
|
||||
#undef XMACRO
|
||||
#undef TO_VALUE
|
||||
#undef VALUE_TO_MAP
|
||||
#undef NULL_NAME
|
||||
#undef TO_MAP
|
||||
|
||||
#undef ALL_LEDGER_FLAGS
|
||||
|
||||
// clang-format off
|
||||
|
||||
#define XMACRO(LEDGER_OBJECT, LSF_FLAG, LSF_FLAG2) \
|
||||
LEDGER_OBJECT(AccountRoot, \
|
||||
LSF_FLAG(lsfPasswordSpent, 0x00010000) /* True, if password set fee is spent. */ \
|
||||
LSF_FLAG(lsfRequireDestTag, 0x00020000) /* True, to require a DestinationTag for payments. */ \
|
||||
LSF_FLAG(lsfRequireAuth, 0x00040000) /* True, to require a authorization to hold IOUs. */ \
|
||||
LSF_FLAG(lsfDisallowXRP, 0x00080000) /* True, to disallow sending XRP. */ \
|
||||
LSF_FLAG(lsfDisableMaster, 0x00100000) /* True, force regular key */ \
|
||||
LSF_FLAG(lsfNoFreeze, 0x00200000) /* True, cannot freeze ripple states */ \
|
||||
LSF_FLAG(lsfGlobalFreeze, 0x00400000) /* True, all assets frozen */ \
|
||||
LSF_FLAG(lsfDefaultRipple, 0x00800000) /* True, incoming trust lines allow rippling by default */ \
|
||||
LSF_FLAG(lsfDepositAuth, 0x01000000) /* True, all deposits require authorization */ \
|
||||
LSF_FLAG(lsfDisallowIncomingNFTokenOffer, 0x04000000) /* True, reject new incoming NFT offers */ \
|
||||
LSF_FLAG(lsfDisallowIncomingCheck, 0x08000000) /* True, reject new checks */ \
|
||||
LSF_FLAG(lsfDisallowIncomingPayChan, 0x10000000) /* True, reject new paychans */ \
|
||||
LSF_FLAG(lsfDisallowIncomingTrustline, 0x20000000) /* True, reject new trustlines (only if no issued assets) */ \
|
||||
LSF_FLAG(lsfAllowTrustLineLocking, 0x40000000) /* True, enable trustline locking */ \
|
||||
LSF_FLAG(lsfAllowTrustLineClawback, 0x80000000)) /* True, enable clawback */ \
|
||||
\
|
||||
LEDGER_OBJECT(Offer, \
|
||||
LSF_FLAG(lsfPassive, 0x00010000) \
|
||||
LSF_FLAG(lsfSell, 0x00020000) /* True, offer was placed as a sell. */ \
|
||||
LSF_FLAG(lsfHybrid, 0x00040000)) /* True, offer is hybrid. */ \
|
||||
\
|
||||
LEDGER_OBJECT(RippleState, \
|
||||
LSF_FLAG(lsfLowReserve, 0x00010000) /* True, if entry counts toward reserve. */ \
|
||||
LSF_FLAG(lsfHighReserve, 0x00020000) \
|
||||
LSF_FLAG(lsfLowAuth, 0x00040000) \
|
||||
LSF_FLAG(lsfHighAuth, 0x00080000) \
|
||||
LSF_FLAG(lsfLowNoRipple, 0x00100000) \
|
||||
LSF_FLAG(lsfHighNoRipple, 0x00200000) \
|
||||
LSF_FLAG(lsfLowFreeze, 0x00400000) /* True, low side has set freeze flag */ \
|
||||
LSF_FLAG(lsfHighFreeze, 0x00800000) /* True, high side has set freeze flag */ \
|
||||
LSF_FLAG(lsfAMMNode, 0x01000000) /* True, trust line to AMM. */ \
|
||||
/* Used by client apps to identify payments via AMM. */ \
|
||||
LSF_FLAG(lsfLowDeepFreeze, 0x02000000) /* True, low side has set deep freeze flag */ \
|
||||
LSF_FLAG(lsfHighDeepFreeze, 0x04000000)) /* True, high side has set deep freeze flag */ \
|
||||
\
|
||||
LEDGER_OBJECT(SignerList, \
|
||||
LSF_FLAG(lsfOneOwnerCount, 0x00010000)) /* True, uses only one OwnerCount */ \
|
||||
\
|
||||
LEDGER_OBJECT(DirNode, \
|
||||
LSF_FLAG(lsfNFTokenBuyOffers, 0x00000001) \
|
||||
LSF_FLAG(lsfNFTokenSellOffers, 0x00000002)) \
|
||||
\
|
||||
LEDGER_OBJECT(NFTokenOffer, \
|
||||
LSF_FLAG(lsfSellNFToken, 0x00000001)) \
|
||||
\
|
||||
LEDGER_OBJECT(MPTokenIssuance, \
|
||||
LSF_FLAG(lsfMPTLocked, 0x00000001) /* Also used in ltMPTOKEN */ \
|
||||
LSF_FLAG(lsfMPTCanLock, 0x00000002) \
|
||||
LSF_FLAG(lsfMPTRequireAuth, 0x00000004) \
|
||||
LSF_FLAG(lsfMPTCanEscrow, 0x00000008) \
|
||||
LSF_FLAG(lsfMPTCanTrade, 0x00000010) \
|
||||
LSF_FLAG(lsfMPTCanTransfer, 0x00000020) \
|
||||
LSF_FLAG(lsfMPTCanClawback, 0x00000040)) \
|
||||
\
|
||||
LEDGER_OBJECT(MPTokenIssuanceMutable, \
|
||||
LSF_FLAG(lsmfMPTCanMutateCanLock, 0x00000002) \
|
||||
LSF_FLAG(lsmfMPTCanMutateRequireAuth, 0x00000004) \
|
||||
LSF_FLAG(lsmfMPTCanMutateCanEscrow, 0x00000008) \
|
||||
LSF_FLAG(lsmfMPTCanMutateCanTrade, 0x00000010) \
|
||||
LSF_FLAG(lsmfMPTCanMutateCanTransfer, 0x00000020) \
|
||||
LSF_FLAG(lsmfMPTCanMutateCanClawback, 0x00000040) \
|
||||
LSF_FLAG(lsmfMPTCanMutateMetadata, 0x00010000) \
|
||||
LSF_FLAG(lsmfMPTCanMutateTransferFee, 0x00020000)) \
|
||||
\
|
||||
LEDGER_OBJECT(MPToken, \
|
||||
LSF_FLAG2(lsfMPTLocked, 0x00000001) \
|
||||
LSF_FLAG(lsfMPTAuthorized, 0x00000002)) \
|
||||
\
|
||||
LEDGER_OBJECT(Credential, \
|
||||
LSF_FLAG(lsfAccepted, 0x00010000)) \
|
||||
\
|
||||
LEDGER_OBJECT(Vault, \
|
||||
LSF_FLAG(lsfVaultPrivate, 0x00010000)) \
|
||||
\
|
||||
LEDGER_OBJECT(Loan, \
|
||||
LSF_FLAG(lsfLoanDefault, 0x00010000) \
|
||||
LSF_FLAG(lsfLoanImpaired, 0x00020000) \
|
||||
LSF_FLAG(lsfLoanOverpayment, 0x00040000)) /* True, loan allows overpayments */
|
||||
/**
|
||||
@ingroup protocol
|
||||
*/
|
||||
enum LedgerSpecificFlags {
|
||||
// ltACCOUNT_ROOT
|
||||
lsfPasswordSpent = 0x00010000, // True, if password set fee is spent.
|
||||
lsfRequireDestTag =
|
||||
0x00020000, // True, to require a DestinationTag for payments.
|
||||
lsfRequireAuth =
|
||||
0x00040000, // True, to require a authorization to hold IOUs.
|
||||
lsfDisallowXRP = 0x00080000, // True, to disallow sending XRP.
|
||||
lsfDisableMaster = 0x00100000, // True, force regular key
|
||||
lsfNoFreeze = 0x00200000, // True, cannot freeze ripple states
|
||||
lsfGlobalFreeze = 0x00400000, // True, all assets frozen
|
||||
lsfDefaultRipple =
|
||||
0x00800000, // True, incoming trust lines allow rippling by default
|
||||
lsfDepositAuth = 0x01000000, // True, all deposits require authorization
|
||||
/* // reserved for Hooks amendment
|
||||
lsfTshCollect = 0x02000000, // True, allow TSH collect-calls to acc hooks
|
||||
*/
|
||||
lsfDisallowIncomingNFTokenOffer =
|
||||
0x04000000, // True, reject new incoming NFT offers
|
||||
lsfDisallowIncomingCheck =
|
||||
0x08000000, // True, reject new checks
|
||||
lsfDisallowIncomingPayChan =
|
||||
0x10000000, // True, reject new paychans
|
||||
lsfDisallowIncomingTrustline =
|
||||
0x20000000, // True, reject new trustlines (only if no issued assets)
|
||||
lsfAllowTrustLineLocking =
|
||||
0x40000000, // True, enable trustline locking
|
||||
lsfAllowTrustLineClawback =
|
||||
0x80000000, // True, enable clawback
|
||||
|
||||
// clang-format on
|
||||
// ltOFFER
|
||||
lsfPassive = 0x00010000,
|
||||
lsfSell = 0x00020000, // True, offer was placed as a sell.
|
||||
lsfHybrid = 0x00040000, // True, offer is hybrid.
|
||||
|
||||
// Create all the flag values as an enum.
|
||||
//
|
||||
// example:
|
||||
// enum LedgerSpecificFlags {
|
||||
// lsfPasswordSpent = 0x00010000,
|
||||
// lsfRequireDestTag = 0x00020000,
|
||||
// ...
|
||||
// };
|
||||
#define TO_VALUE(name, value) name = value,
|
||||
#define NULL_NAME(name, values) values
|
||||
#define NULL_OUTPUT(name, value)
|
||||
enum LedgerSpecificFlags : std::uint32_t { XMACRO(NULL_NAME, TO_VALUE, NULL_OUTPUT) };
|
||||
// ltRIPPLE_STATE
|
||||
lsfLowReserve = 0x00010000, // True, if entry counts toward reserve.
|
||||
lsfHighReserve = 0x00020000,
|
||||
lsfLowAuth = 0x00040000,
|
||||
lsfHighAuth = 0x00080000,
|
||||
lsfLowNoRipple = 0x00100000,
|
||||
lsfHighNoRipple = 0x00200000,
|
||||
lsfLowFreeze = 0x00400000, // True, low side has set freeze flag
|
||||
lsfHighFreeze = 0x00800000, // True, high side has set freeze flag
|
||||
lsfLowDeepFreeze = 0x02000000, // True, low side has set deep freeze flag
|
||||
lsfHighDeepFreeze = 0x04000000, // True, high side has set deep freeze flag
|
||||
lsfAMMNode = 0x01000000, // True, trust line to AMM. Used by client
|
||||
// apps to identify payments via AMM.
|
||||
|
||||
// Create getter functions for each set of flags using Meyer's singleton pattern.
|
||||
// This avoids static initialization order fiasco while still providing efficient access.
|
||||
// This is used below in `getAllLedgerFlags()` to generate the server_definitions RPC output.
|
||||
//
|
||||
// example:
|
||||
// inline LedgerFlagMap const& getAccountRootFlags() {
|
||||
// static LedgerFlagMap const flags = {
|
||||
// {"lsfPasswordSpent", 0x00010000},
|
||||
// {"lsfRequireDestTag", 0x00020000},
|
||||
// ...};
|
||||
// return flags;
|
||||
// }
|
||||
using LedgerFlagMap = std::map<std::string, std::uint32_t>;
|
||||
#define VALUE_TO_MAP(name, value) {#name, value},
|
||||
#define TO_MAP(name, values) \
|
||||
inline LedgerFlagMap const& get##name##Flags() \
|
||||
{ \
|
||||
static LedgerFlagMap const flags = {values}; \
|
||||
return flags; \
|
||||
}
|
||||
XMACRO(TO_MAP, VALUE_TO_MAP, VALUE_TO_MAP)
|
||||
// ltSIGNER_LIST
|
||||
lsfOneOwnerCount = 0x00010000, // True, uses only one OwnerCount
|
||||
|
||||
// Create a getter function for all ledger flag maps using Meyer's singleton pattern.
|
||||
// This is used to generate the server_definitions RPC output.
|
||||
//
|
||||
// example:
|
||||
// inline std::vector<std::pair<std::string, LedgerFlagMap>> const& getAllLedgerFlags() {
|
||||
// static std::vector<std::pair<std::string, LedgerFlagMap>> const flags = {
|
||||
// {"AccountRoot", getAccountRootFlags()},
|
||||
// ...};
|
||||
// return flags;
|
||||
// }
|
||||
#define ALL_LEDGER_FLAGS(name, values) {#name, get##name##Flags()},
|
||||
inline std::vector<std::pair<std::string, LedgerFlagMap>> const&
|
||||
getAllLedgerFlags()
|
||||
{
|
||||
static std::vector<std::pair<std::string, LedgerFlagMap>> const flags = {
|
||||
XMACRO(ALL_LEDGER_FLAGS, NULL_OUTPUT, NULL_OUTPUT)};
|
||||
return flags;
|
||||
}
|
||||
// ltDIR_NODE
|
||||
lsfNFTokenBuyOffers = 0x00000001,
|
||||
lsfNFTokenSellOffers = 0x00000002,
|
||||
|
||||
#undef XMACRO
|
||||
#undef TO_VALUE
|
||||
#undef VALUE_TO_MAP
|
||||
#undef NULL_NAME
|
||||
#undef NULL_OUTPUT
|
||||
#undef TO_MAP
|
||||
#undef ALL_LEDGER_FLAGS
|
||||
// ltNFTOKEN_OFFER
|
||||
lsfSellNFToken = 0x00000001,
|
||||
|
||||
#pragma pop_macro("XMACRO")
|
||||
#pragma pop_macro("TO_VALUE")
|
||||
#pragma pop_macro("VALUE_TO_MAP")
|
||||
#pragma pop_macro("NULL_NAME")
|
||||
#pragma pop_macro("TO_MAP")
|
||||
#pragma pop_macro("ALL_LEDGER_FLAGS")
|
||||
// ltMPTOKEN_ISSUANCE
|
||||
lsfMPTLocked = 0x00000001, // Also used in ltMPTOKEN
|
||||
lsfMPTCanLock = 0x00000002,
|
||||
lsfMPTRequireAuth = 0x00000004,
|
||||
lsfMPTCanEscrow = 0x00000008,
|
||||
lsfMPTCanTrade = 0x00000010,
|
||||
lsfMPTCanTransfer = 0x00000020,
|
||||
lsfMPTCanClawback = 0x00000040,
|
||||
|
||||
lsmfMPTCanMutateCanLock = 0x00000002,
|
||||
lsmfMPTCanMutateRequireAuth = 0x00000004,
|
||||
lsmfMPTCanMutateCanEscrow = 0x00000008,
|
||||
lsmfMPTCanMutateCanTrade = 0x00000010,
|
||||
lsmfMPTCanMutateCanTransfer = 0x00000020,
|
||||
lsmfMPTCanMutateCanClawback = 0x00000040,
|
||||
lsmfMPTCanMutateMetadata = 0x00010000,
|
||||
lsmfMPTCanMutateTransferFee = 0x00020000,
|
||||
|
||||
// ltMPTOKEN
|
||||
lsfMPTAuthorized = 0x00000002,
|
||||
|
||||
// ltCREDENTIAL
|
||||
lsfAccepted = 0x00010000,
|
||||
|
||||
// ltVAULT
|
||||
lsfVaultPrivate = 0x00010000,
|
||||
|
||||
// ltLOAN
|
||||
lsfLoanDefault = 0x00010000,
|
||||
lsfLoanImpaired = 0x00020000,
|
||||
lsfLoanOverpayment = 0x00040000, // True, loan allows overpayments
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -286,10 +207,6 @@ private:
|
||||
public:
|
||||
static LedgerFormats const&
|
||||
getInstance();
|
||||
|
||||
// Fields shared by all ledger entry formats:
|
||||
static std::vector<SOElement> const&
|
||||
getCommonFields();
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -232,7 +232,7 @@ std::size_t constexpr maxMPTokenMetadataLength = 1024;
|
||||
|
||||
/** The maximum amount of MPTokenIssuance */
|
||||
std::uint64_t constexpr maxMPTokenAmount = 0x7FFF'FFFF'FFFF'FFFFull;
|
||||
static_assert(Number::maxRep >= maxMPTokenAmount);
|
||||
static_assert(Number::largestMantissa >= maxMPTokenAmount);
|
||||
|
||||
/** The maximum length of Data payload */
|
||||
std::size_t constexpr maxDataPayloadLength = 256;
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -98,12 +97,8 @@ public:
|
||||
operator=(SOTemplate&& other) = default;
|
||||
|
||||
/** Create a template populated with all fields.
|
||||
After creating the template fields cannot be added, modified, or removed.
|
||||
*/
|
||||
SOTemplate(std::vector<SOElement> uniqueFields, std::vector<SOElement> commonFields = {});
|
||||
|
||||
/** Create a template populated with all fields.
|
||||
Note: Defers to the vector constructor above.
|
||||
After creating the template fields cannot be
|
||||
added, modified, or removed.
|
||||
*/
|
||||
SOTemplate(
|
||||
std::initializer_list<SOElement> uniqueFields,
|
||||
|
||||
@@ -539,6 +539,8 @@ STAmount::fromNumber(A const& a, Number const& number)
|
||||
return STAmount{asset, intValue, 0, negative};
|
||||
}
|
||||
|
||||
XRPL_ASSERT_PARTS(
|
||||
working.signum() >= 0, "xrpl::STAmount::fromNumber", "non-negative Number to normalize");
|
||||
auto const [mantissa, exponent] = working.normalizeToRange(cMinValue, cMaxValue);
|
||||
|
||||
return STAmount{asset, mantissa, exponent, negative};
|
||||
|
||||
@@ -23,7 +23,7 @@ systemName()
|
||||
/** Number of drops in the genesis account. */
|
||||
constexpr XRPAmount INITIAL_XRP{100'000'000'000 * DROPS_PER_XRP};
|
||||
static_assert(INITIAL_XRP.drops() == 100'000'000'000'000'000);
|
||||
static_assert(Number::maxRep >= INITIAL_XRP.drops());
|
||||
static_assert(Number::largestMantissa >= INITIAL_XRP.drops());
|
||||
|
||||
/** Returns true if the amount does not exceed the initial XRP in existence. */
|
||||
inline bool
|
||||
|
||||
@@ -3,444 +3,294 @@
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** Transaction flags.
|
||||
|
||||
These flags are specified in a transaction's 'Flags' field and modify
|
||||
the behavior of that transaction.
|
||||
These flags are specified in a transaction's 'Flags' field and modify the
|
||||
behavior of that transaction.
|
||||
|
||||
There are two types of flags:
|
||||
|
||||
(1) Universal flags: these are flags which apply to, and are interpreted the same way by,
|
||||
all transactions, except, perhaps, to special pseudo-transactions.
|
||||
(1) Universal flags: these are flags which apply to, and are interpreted
|
||||
the same way by, all transactions, except, perhaps,
|
||||
to special pseudo-transactions.
|
||||
|
||||
(2) Tx-Specific flags: these are flags which are interpreted according to the type of the
|
||||
transaction being executed. That is, the same numerical flag value may have different
|
||||
effects, depending on the transaction being executed.
|
||||
(2) Tx-Specific flags: these are flags which are interpreted according
|
||||
to the type of the transaction being executed.
|
||||
That is, the same numerical flag value may have
|
||||
different effects, depending on the transaction
|
||||
being executed.
|
||||
|
||||
@note The universal transaction flags occupy the high-order 8 bits.
|
||||
The tx-specific flags occupy the remaining 24 bits.
|
||||
@note The universal transaction flags occupy the high-order 8 bits. The
|
||||
tx-specific flags occupy the remaining 24 bits.
|
||||
|
||||
@warning Transaction flags form part of the protocol.
|
||||
**Changing them should be avoided because without special handling, this will result in
|
||||
a hard fork.**
|
||||
@warning Transaction flags form part of the protocol. **Changing them
|
||||
should be avoided because without special handling, this will
|
||||
result in a hard fork.**
|
||||
|
||||
@ingroup protocol
|
||||
*/
|
||||
|
||||
using FlagValue = std::uint32_t;
|
||||
|
||||
// Universal Transaction flags:
|
||||
inline constexpr FlagValue tfFullyCanonicalSig = 0x80000000;
|
||||
inline constexpr FlagValue tfInnerBatchTxn = 0x40000000;
|
||||
inline constexpr FlagValue tfUniversal = tfFullyCanonicalSig | tfInnerBatchTxn;
|
||||
inline constexpr FlagValue tfUniversalMask = ~tfUniversal;
|
||||
|
||||
#pragma push_macro("XMACRO")
|
||||
#pragma push_macro("TO_VALUE")
|
||||
#pragma push_macro("VALUE_TO_MAP")
|
||||
#pragma push_macro("NULL_NAME")
|
||||
#pragma push_macro("NULL_OUTPUT")
|
||||
#pragma push_macro("TO_MAP")
|
||||
#pragma push_macro("TO_MASK")
|
||||
#pragma push_macro("VALUE_TO_MASK")
|
||||
#pragma push_macro("ALL_TX_FLAGS")
|
||||
#pragma push_macro("NULL_MASK_ADJ")
|
||||
#pragma push_macro("MASK_ADJ_TO_MASK")
|
||||
|
||||
#undef XMACRO
|
||||
#undef TO_VALUE
|
||||
#undef VALUE_TO_MAP
|
||||
#undef NULL_NAME
|
||||
#undef NULL_OUTPUT
|
||||
#undef TO_MAP
|
||||
#undef TO_MASK
|
||||
#undef VALUE_TO_MASK
|
||||
#undef NULL_MASK_ADJ
|
||||
#undef MASK_ADJ_TO_MASK
|
||||
|
||||
// Formatting equals sign aligned 4 spaces after longest prefix, except for
|
||||
// wrapped lines
|
||||
// clang-format off
|
||||
#undef ALL_TX_FLAGS
|
||||
// Universal Transaction flags:
|
||||
constexpr std::uint32_t tfFullyCanonicalSig = 0x80000000;
|
||||
constexpr std::uint32_t tfInnerBatchTxn = 0x40000000;
|
||||
constexpr std::uint32_t tfUniversal = tfFullyCanonicalSig | tfInnerBatchTxn;
|
||||
constexpr std::uint32_t tfUniversalMask = ~tfUniversal;
|
||||
|
||||
// XMACRO parameters:
|
||||
// - TRANSACTION: handles the transaction name, its flags, and mask adjustment
|
||||
// - TF_FLAG: defines a new flag constant
|
||||
// - TF_FLAG2: references an existing flag constant (no new definition)
|
||||
// - MASK_ADJ: specifies flags to add back to the mask (making them invalid for this tx type)
|
||||
//
|
||||
// Note: MASK_ADJ is used when a universal flag should be invalid for a specific transaction.
|
||||
// For example, Batch uses MASK_ADJ(tfInnerBatchTxn) because the outer Batch transaction
|
||||
// must not have tfInnerBatchTxn set (only inner transactions should have it).
|
||||
//
|
||||
// TODO: Consider rewriting this using reflection in C++26 or later. Alternatively this could be a DSL processed by a script at build time.
|
||||
#define XMACRO(TRANSACTION, TF_FLAG, TF_FLAG2, MASK_ADJ) \
|
||||
TRANSACTION(AccountSet, \
|
||||
TF_FLAG(tfRequireDestTag, 0x00010000) \
|
||||
TF_FLAG(tfOptionalDestTag, 0x00020000) \
|
||||
TF_FLAG(tfRequireAuth, 0x00040000) \
|
||||
TF_FLAG(tfOptionalAuth, 0x00080000) \
|
||||
TF_FLAG(tfDisallowXRP, 0x00100000) \
|
||||
TF_FLAG(tfAllowXRP, 0x00200000), \
|
||||
MASK_ADJ(0)) \
|
||||
\
|
||||
TRANSACTION(OfferCreate, \
|
||||
TF_FLAG(tfPassive, 0x00010000) \
|
||||
TF_FLAG(tfImmediateOrCancel, 0x00020000) \
|
||||
TF_FLAG(tfFillOrKill, 0x00040000) \
|
||||
TF_FLAG(tfSell, 0x00080000) \
|
||||
TF_FLAG(tfHybrid, 0x00100000), \
|
||||
MASK_ADJ(0)) \
|
||||
\
|
||||
TRANSACTION(Payment, \
|
||||
TF_FLAG(tfNoRippleDirect, 0x00010000) \
|
||||
TF_FLAG(tfPartialPayment, 0x00020000) \
|
||||
TF_FLAG(tfLimitQuality, 0x00040000), \
|
||||
MASK_ADJ(0)) \
|
||||
\
|
||||
TRANSACTION(TrustSet, \
|
||||
TF_FLAG(tfSetfAuth, 0x00010000) \
|
||||
TF_FLAG(tfSetNoRipple, 0x00020000) \
|
||||
TF_FLAG(tfClearNoRipple, 0x00040000) \
|
||||
TF_FLAG(tfSetFreeze, 0x00100000) \
|
||||
TF_FLAG(tfClearFreeze, 0x00200000) \
|
||||
TF_FLAG(tfSetDeepFreeze, 0x00400000) \
|
||||
TF_FLAG(tfClearDeepFreeze, 0x00800000), \
|
||||
MASK_ADJ(0)) \
|
||||
\
|
||||
TRANSACTION(EnableAmendment, \
|
||||
TF_FLAG(tfGotMajority, 0x00010000) \
|
||||
TF_FLAG(tfLostMajority, 0x00020000), \
|
||||
MASK_ADJ(0)) \
|
||||
\
|
||||
TRANSACTION(PaymentChannelClaim, \
|
||||
TF_FLAG(tfRenew, 0x00010000) \
|
||||
TF_FLAG(tfClose, 0x00020000), \
|
||||
MASK_ADJ(0)) \
|
||||
\
|
||||
TRANSACTION(NFTokenMint, \
|
||||
TF_FLAG(tfBurnable, 0x00000001) \
|
||||
TF_FLAG(tfOnlyXRP, 0x00000002) \
|
||||
/* deprecated TF_FLAG(tfTrustLine, 0x00000004) */ \
|
||||
TF_FLAG(tfTransferable, 0x00000008) \
|
||||
TF_FLAG(tfMutable, 0x00000010), \
|
||||
MASK_ADJ(0)) \
|
||||
\
|
||||
TRANSACTION(MPTokenIssuanceCreate, \
|
||||
/* Note: tf/lsfMPTLocked is intentionally omitted since this transaction is not allowed to modify it. */ \
|
||||
TF_FLAG(tfMPTCanLock, lsfMPTCanLock) \
|
||||
TF_FLAG(tfMPTRequireAuth, lsfMPTRequireAuth) \
|
||||
TF_FLAG(tfMPTCanEscrow, lsfMPTCanEscrow) \
|
||||
TF_FLAG(tfMPTCanTrade, lsfMPTCanTrade) \
|
||||
TF_FLAG(tfMPTCanTransfer, lsfMPTCanTransfer) \
|
||||
TF_FLAG(tfMPTCanClawback, lsfMPTCanClawback), \
|
||||
MASK_ADJ(0)) \
|
||||
\
|
||||
TRANSACTION(MPTokenAuthorize, \
|
||||
TF_FLAG(tfMPTUnauthorize, 0x00000001), \
|
||||
MASK_ADJ(0)) \
|
||||
\
|
||||
TRANSACTION(MPTokenIssuanceSet, \
|
||||
TF_FLAG(tfMPTLock, 0x00000001) \
|
||||
TF_FLAG(tfMPTUnlock, 0x00000002), \
|
||||
MASK_ADJ(0)) \
|
||||
\
|
||||
TRANSACTION(NFTokenCreateOffer, \
|
||||
TF_FLAG(tfSellNFToken, 0x00000001), \
|
||||
MASK_ADJ(0)) \
|
||||
\
|
||||
TRANSACTION(AMMDeposit, \
|
||||
TF_FLAG(tfLPToken, 0x00010000) \
|
||||
TF_FLAG(tfSingleAsset, 0x00080000) \
|
||||
TF_FLAG(tfTwoAsset, 0x00100000) \
|
||||
TF_FLAG(tfOneAssetLPToken, 0x00200000) \
|
||||
TF_FLAG(tfLimitLPToken, 0x00400000) \
|
||||
TF_FLAG(tfTwoAssetIfEmpty, 0x00800000), \
|
||||
MASK_ADJ(0)) \
|
||||
\
|
||||
TRANSACTION(AMMWithdraw, \
|
||||
TF_FLAG2(tfLPToken, 0x00010000) \
|
||||
TF_FLAG(tfWithdrawAll, 0x00020000) \
|
||||
TF_FLAG(tfOneAssetWithdrawAll, 0x00040000) \
|
||||
TF_FLAG2(tfSingleAsset, 0x00080000) \
|
||||
TF_FLAG2(tfTwoAsset, 0x00100000) \
|
||||
TF_FLAG2(tfOneAssetLPToken, 0x00200000) \
|
||||
TF_FLAG2(tfLimitLPToken, 0x00400000), \
|
||||
MASK_ADJ(0)) \
|
||||
\
|
||||
TRANSACTION(AMMClawback, \
|
||||
TF_FLAG(tfClawTwoAssets, 0x00000001), \
|
||||
MASK_ADJ(0)) \
|
||||
\
|
||||
TRANSACTION(XChainModifyBridge, \
|
||||
TF_FLAG(tfClearAccountCreateAmount, 0x00010000), \
|
||||
MASK_ADJ(0)) \
|
||||
\
|
||||
TRANSACTION(VaultCreate, \
|
||||
TF_FLAG(tfVaultPrivate, lsfVaultPrivate) \
|
||||
TF_FLAG(tfVaultShareNonTransferable, 0x00020000), \
|
||||
MASK_ADJ(0)) \
|
||||
\
|
||||
TRANSACTION(Batch, \
|
||||
TF_FLAG(tfAllOrNothing, 0x00010000) \
|
||||
TF_FLAG(tfOnlyOne, 0x00020000) \
|
||||
TF_FLAG(tfUntilFailure, 0x00040000) \
|
||||
TF_FLAG(tfIndependent, 0x00080000), \
|
||||
MASK_ADJ(tfInnerBatchTxn)) /* Batch must reject tfInnerBatchTxn - only inner transactions should have this flag */ \
|
||||
\
|
||||
TRANSACTION(LoanSet, /* True indicates the loan supports overpayments */ \
|
||||
TF_FLAG(tfLoanOverpayment, 0x00010000), \
|
||||
MASK_ADJ(0)) \
|
||||
\
|
||||
TRANSACTION(LoanPay, /* True indicates any excess in this payment can be used as an overpayment. */ \
|
||||
/* False: no overpayments will be taken. */ \
|
||||
TF_FLAG2(tfLoanOverpayment, 0x00010000) \
|
||||
TF_FLAG(tfLoanFullPayment, 0x00020000) /* True indicates that the payment is an early full payment. */ \
|
||||
/* It must pay the entire loan including close interest and fees, or it will fail. */ \
|
||||
/* False: Not a full payment. */ \
|
||||
TF_FLAG(tfLoanLatePayment, 0x00040000), /* True indicates that the payment is late, and includes late interest and fees. */ \
|
||||
/* If the loan is not late, it will fail. */ \
|
||||
/* False: not a late payment. If the current payment is overdue, the transaction will fail.*/ \
|
||||
MASK_ADJ(0)) \
|
||||
\
|
||||
TRANSACTION(LoanManage, \
|
||||
TF_FLAG(tfLoanDefault, 0x00010000) \
|
||||
TF_FLAG(tfLoanImpair, 0x00020000) \
|
||||
TF_FLAG(tfLoanUnimpair, 0x00040000), \
|
||||
MASK_ADJ(0))
|
||||
// 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;
|
||||
constexpr std::uint32_t tfAccountSetMask =
|
||||
~(tfUniversal | tfRequireDestTag | tfOptionalDestTag | tfRequireAuth |
|
||||
tfOptionalAuth | tfDisallowXRP | tfAllowXRP);
|
||||
|
||||
// clang-format on
|
||||
// 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;
|
||||
/* // reserved for Hooks amendment
|
||||
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 asfAllowTrustLineClawback = 16;
|
||||
constexpr std::uint32_t asfAllowTrustLineLocking = 17;
|
||||
|
||||
// Create all the flag values.
|
||||
//
|
||||
// example:
|
||||
// inline constexpr FlagValue tfAccountSetRequireDestTag = 0x00010000;
|
||||
#define TO_VALUE(name, value) inline constexpr FlagValue name = value;
|
||||
#define NULL_NAME(name, values, maskAdj) values
|
||||
#define NULL_OUTPUT(name, value)
|
||||
#define NULL_MASK_ADJ(value)
|
||||
XMACRO(NULL_NAME, TO_VALUE, NULL_OUTPUT, NULL_MASK_ADJ)
|
||||
// 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;
|
||||
constexpr std::uint32_t tfHybrid = 0x00100000;
|
||||
constexpr std::uint32_t tfOfferCreateMask =
|
||||
~(tfUniversal | tfPassive | tfImmediateOrCancel | tfFillOrKill | tfSell | tfHybrid);
|
||||
|
||||
// Create masks for each transaction type that has flags.
|
||||
//
|
||||
// example:
|
||||
// inline constexpr FlagValue tfAccountSetMask = ~(tfUniversal | tfRequireDestTag |
|
||||
// tfOptionalDestTag | tfRequireAuth | tfOptionalAuth | tfDisallowXRP | tfAllowXRP);
|
||||
//
|
||||
// The mask adjustment (maskAdj) allows adding flags back to the mask, making them invalid.
|
||||
// For example, Batch uses MASK_ADJ(tfInnerBatchTxn) to reject tfInnerBatchTxn on outer Batch.
|
||||
#define TO_MASK(name, values, maskAdj) \
|
||||
inline constexpr FlagValue tf##name##Mask = ~(tfUniversal values) | maskAdj;
|
||||
#define VALUE_TO_MASK(name, value) | name
|
||||
#define MASK_ADJ_TO_MASK(value) value
|
||||
XMACRO(TO_MASK, VALUE_TO_MASK, VALUE_TO_MASK, MASK_ADJ_TO_MASK)
|
||||
// Payment flags:
|
||||
constexpr std::uint32_t tfNoRippleDirect = 0x00010000;
|
||||
constexpr std::uint32_t tfPartialPayment = 0x00020000;
|
||||
constexpr std::uint32_t tfLimitQuality = 0x00040000;
|
||||
constexpr std::uint32_t tfPaymentMask =
|
||||
~(tfUniversal | tfPartialPayment | tfLimitQuality | tfNoRippleDirect);
|
||||
constexpr std::uint32_t tfMPTPaymentMask = ~(tfUniversal | tfPartialPayment);
|
||||
|
||||
// Verify that tfBatchMask correctly rejects tfInnerBatchTxn.
|
||||
// The outer Batch transaction must NOT have tfInnerBatchTxn set; only inner transactions should
|
||||
// have it.
|
||||
static_assert(
|
||||
(tfBatchMask & tfInnerBatchTxn) == tfInnerBatchTxn,
|
||||
"tfBatchMask must include tfInnerBatchTxn to reject it on outer Batch");
|
||||
// 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;
|
||||
constexpr std::uint32_t tfSetDeepFreeze = 0x00400000;
|
||||
constexpr std::uint32_t tfClearDeepFreeze = 0x00800000;
|
||||
constexpr std::uint32_t tfTrustSetMask =
|
||||
~(tfUniversal | tfSetfAuth | tfSetNoRipple | tfClearNoRipple | tfSetFreeze |
|
||||
tfClearFreeze | tfSetDeepFreeze | tfClearDeepFreeze);
|
||||
constexpr std::uint32_t tfTrustSetPermissionMask = ~(tfUniversal | tfSetfAuth | tfSetFreeze | tfClearFreeze);
|
||||
|
||||
// Verify that other transaction masks correctly allow tfInnerBatchTxn.
|
||||
// Inner transactions need tfInnerBatchTxn to be valid, so these masks must not reject it.
|
||||
static_assert(
|
||||
(tfPaymentMask & tfInnerBatchTxn) == 0,
|
||||
"tfPaymentMask must not reject tfInnerBatchTxn");
|
||||
static_assert(
|
||||
(tfAccountSetMask & tfInnerBatchTxn) == 0,
|
||||
"tfAccountSetMask must not reject tfInnerBatchTxn");
|
||||
// EnableAmendment flags:
|
||||
constexpr std::uint32_t tfGotMajority = 0x00010000;
|
||||
constexpr std::uint32_t tfLostMajority = 0x00020000;
|
||||
constexpr std::uint32_t tfChangeMask =
|
||||
~( tfUniversal | tfGotMajority | tfLostMajority);
|
||||
|
||||
// Create getter functions for each set of flags using Meyer's singleton pattern.
|
||||
// This avoids static initialization order fiasco while still providing efficient access.
|
||||
// This is used below in `getAllTxFlags()` to generate the server_definitions RPC
|
||||
// output.
|
||||
//
|
||||
// example:
|
||||
// inline FlagMap const& getAccountSetFlags() {
|
||||
// static FlagMap const flags = {
|
||||
// {"tfRequireDestTag", 0x00010000},
|
||||
// {"tfOptionalDestTag", 0x00020000},
|
||||
// ...};
|
||||
// return flags;
|
||||
// }
|
||||
using FlagMap = std::map<std::string, FlagValue>;
|
||||
#define VALUE_TO_MAP(name, value) {#name, value},
|
||||
#define TO_MAP(name, values, maskAdj) \
|
||||
inline FlagMap const& get##name##Flags() \
|
||||
{ \
|
||||
static FlagMap const flags = {values}; \
|
||||
return flags; \
|
||||
}
|
||||
XMACRO(TO_MAP, VALUE_TO_MAP, VALUE_TO_MAP, NULL_MASK_ADJ)
|
||||
// PaymentChannelClaim flags:
|
||||
constexpr std::uint32_t tfRenew = 0x00010000;
|
||||
constexpr std::uint32_t tfClose = 0x00020000;
|
||||
constexpr std::uint32_t tfPayChanClaimMask = ~(tfUniversal | tfRenew | tfClose);
|
||||
|
||||
inline FlagMap const&
|
||||
getUniversalFlags()
|
||||
{
|
||||
static FlagMap const flags = {
|
||||
{"tfFullyCanonicalSig", tfFullyCanonicalSig}, {"tfInnerBatchTxn", tfInnerBatchTxn}};
|
||||
return flags;
|
||||
}
|
||||
// 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 tfMutable = 0x00000010;
|
||||
|
||||
// Create a getter function for all transaction flag maps using Meyer's singleton pattern.
|
||||
// This is used to generate the server_definitions RPC output.
|
||||
//
|
||||
// example:
|
||||
// inline FlagMapPairList const& getAllTxFlags() {
|
||||
// static FlagMapPairList const flags = {
|
||||
// {"AccountSet", getAccountSetFlags()},
|
||||
// ...};
|
||||
// return flags;
|
||||
// }
|
||||
using FlagMapPairList = std::vector<std::pair<std::string, FlagMap>>;
|
||||
#define ALL_TX_FLAGS(name, values, maskAdj) {#name, get##name##Flags()},
|
||||
inline FlagMapPairList const&
|
||||
getAllTxFlags()
|
||||
{
|
||||
static FlagMapPairList const flags = {
|
||||
{"universal", getUniversalFlags()},
|
||||
XMACRO(ALL_TX_FLAGS, NULL_OUTPUT, NULL_OUTPUT, NULL_MASK_ADJ)};
|
||||
return flags;
|
||||
}
|
||||
|
||||
#undef XMACRO
|
||||
#undef TO_VALUE
|
||||
#undef VALUE_TO_MAP
|
||||
#undef NULL_NAME
|
||||
#undef NULL_OUTPUT
|
||||
#undef TO_MAP
|
||||
#undef TO_MASK
|
||||
#undef VALUE_TO_MASK
|
||||
#undef ALL_TX_FLAGS
|
||||
#undef NULL_MASK_ADJ
|
||||
#undef MASK_ADJ_TO_MASK
|
||||
|
||||
#pragma pop_macro("XMACRO")
|
||||
#pragma pop_macro("TO_VALUE")
|
||||
#pragma pop_macro("VALUE_TO_MAP")
|
||||
#pragma pop_macro("NULL_NAME")
|
||||
#pragma pop_macro("NULL_OUTPUT")
|
||||
#pragma pop_macro("TO_MAP")
|
||||
#pragma pop_macro("TO_MASK")
|
||||
#pragma pop_macro("VALUE_TO_MASK")
|
||||
#pragma pop_macro("ALL_TX_FLAGS")
|
||||
#pragma pop_macro("NULL_MASK_ADJ")
|
||||
#pragma pop_macro("MASK_ADJ_TO_MASK")
|
||||
|
||||
// Additional transaction masks and combos
|
||||
inline constexpr FlagValue tfMPTPaymentMask = ~(tfUniversal | tfPartialPayment);
|
||||
inline constexpr FlagValue tfTrustSetPermissionMask =
|
||||
~(tfUniversal | tfSetfAuth | tfSetFreeze | tfClearFreeze);
|
||||
// MPTokenIssuanceCreate flags:
|
||||
// Note: tf/lsfMPTLocked is intentionally omitted, since this transaction
|
||||
// is not allowed to modify it.
|
||||
constexpr std::uint32_t const tfMPTCanLock = lsfMPTCanLock;
|
||||
constexpr std::uint32_t const tfMPTRequireAuth = lsfMPTRequireAuth;
|
||||
constexpr std::uint32_t const tfMPTCanEscrow = lsfMPTCanEscrow;
|
||||
constexpr std::uint32_t const tfMPTCanTrade = lsfMPTCanTrade;
|
||||
constexpr std::uint32_t const tfMPTCanTransfer = lsfMPTCanTransfer;
|
||||
constexpr std::uint32_t const tfMPTCanClawback = lsfMPTCanClawback;
|
||||
constexpr std::uint32_t const tfMPTokenIssuanceCreateMask =
|
||||
~(tfUniversal | tfMPTCanLock | tfMPTRequireAuth | tfMPTCanEscrow | tfMPTCanTrade | tfMPTCanTransfer | tfMPTCanClawback);
|
||||
|
||||
// MPTokenIssuanceCreate MutableFlags:
|
||||
// Indicating specific fields or flags may be changed after issuance.
|
||||
inline constexpr FlagValue tmfMPTCanMutateCanLock = lsmfMPTCanMutateCanLock;
|
||||
inline constexpr FlagValue tmfMPTCanMutateRequireAuth = lsmfMPTCanMutateRequireAuth;
|
||||
inline constexpr FlagValue tmfMPTCanMutateCanEscrow = lsmfMPTCanMutateCanEscrow;
|
||||
inline constexpr FlagValue tmfMPTCanMutateCanTrade = lsmfMPTCanMutateCanTrade;
|
||||
inline constexpr FlagValue tmfMPTCanMutateCanTransfer = lsmfMPTCanMutateCanTransfer;
|
||||
inline constexpr FlagValue tmfMPTCanMutateCanClawback = lsmfMPTCanMutateCanClawback;
|
||||
inline constexpr FlagValue tmfMPTCanMutateMetadata = lsmfMPTCanMutateMetadata;
|
||||
inline constexpr FlagValue tmfMPTCanMutateTransferFee = lsmfMPTCanMutateTransferFee;
|
||||
inline constexpr FlagValue tmfMPTokenIssuanceCreateMutableMask =
|
||||
~(tmfMPTCanMutateCanLock | tmfMPTCanMutateRequireAuth | tmfMPTCanMutateCanEscrow |
|
||||
tmfMPTCanMutateCanTrade | tmfMPTCanMutateCanTransfer | tmfMPTCanMutateCanClawback |
|
||||
tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee);
|
||||
constexpr std::uint32_t const tmfMPTCanMutateCanLock = lsmfMPTCanMutateCanLock;
|
||||
constexpr std::uint32_t const tmfMPTCanMutateRequireAuth = lsmfMPTCanMutateRequireAuth;
|
||||
constexpr std::uint32_t const tmfMPTCanMutateCanEscrow = lsmfMPTCanMutateCanEscrow;
|
||||
constexpr std::uint32_t const tmfMPTCanMutateCanTrade = lsmfMPTCanMutateCanTrade;
|
||||
constexpr std::uint32_t const tmfMPTCanMutateCanTransfer = lsmfMPTCanMutateCanTransfer;
|
||||
constexpr std::uint32_t const tmfMPTCanMutateCanClawback = lsmfMPTCanMutateCanClawback;
|
||||
constexpr std::uint32_t const tmfMPTCanMutateMetadata = lsmfMPTCanMutateMetadata;
|
||||
constexpr std::uint32_t const tmfMPTCanMutateTransferFee = lsmfMPTCanMutateTransferFee;
|
||||
constexpr std::uint32_t const tmfMPTokenIssuanceCreateMutableMask =
|
||||
~(tmfMPTCanMutateCanLock | tmfMPTCanMutateRequireAuth | tmfMPTCanMutateCanEscrow | tmfMPTCanMutateCanTrade
|
||||
| tmfMPTCanMutateCanTransfer | tmfMPTCanMutateCanClawback | tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee);
|
||||
|
||||
// MPTokenAuthorize flags:
|
||||
constexpr std::uint32_t const tfMPTUnauthorize = 0x00000001;
|
||||
constexpr std::uint32_t const tfMPTokenAuthorizeMask = ~(tfUniversal | tfMPTUnauthorize);
|
||||
|
||||
// MPTokenIssuanceSet flags:
|
||||
constexpr std::uint32_t const tfMPTLock = 0x00000001;
|
||||
constexpr std::uint32_t const tfMPTUnlock = 0x00000002;
|
||||
constexpr std::uint32_t const tfMPTokenIssuanceSetMask = ~(tfUniversal | tfMPTLock | tfMPTUnlock);
|
||||
constexpr std::uint32_t const tfMPTokenIssuanceSetPermissionMask = ~(tfUniversal | tfMPTLock | tfMPTUnlock);
|
||||
|
||||
// MPTokenIssuanceSet MutableFlags:
|
||||
// Set or Clear flags.
|
||||
constexpr std::uint32_t const tmfMPTSetCanLock = 0x00000001;
|
||||
constexpr std::uint32_t const tmfMPTClearCanLock = 0x00000002;
|
||||
constexpr std::uint32_t const tmfMPTSetRequireAuth = 0x00000004;
|
||||
constexpr std::uint32_t const tmfMPTClearRequireAuth = 0x00000008;
|
||||
constexpr std::uint32_t const tmfMPTSetCanEscrow = 0x00000010;
|
||||
constexpr std::uint32_t const tmfMPTClearCanEscrow = 0x00000020;
|
||||
constexpr std::uint32_t const tmfMPTSetCanTrade = 0x00000040;
|
||||
constexpr std::uint32_t const tmfMPTClearCanTrade = 0x00000080;
|
||||
constexpr std::uint32_t const tmfMPTSetCanTransfer = 0x00000100;
|
||||
constexpr std::uint32_t const tmfMPTClearCanTransfer = 0x00000200;
|
||||
constexpr std::uint32_t const tmfMPTSetCanClawback = 0x00000400;
|
||||
constexpr std::uint32_t const tmfMPTClearCanClawback = 0x00000800;
|
||||
constexpr std::uint32_t const tmfMPTokenIssuanceSetMutableMask = ~(tmfMPTSetCanLock | tmfMPTClearCanLock |
|
||||
tmfMPTSetRequireAuth | tmfMPTClearRequireAuth | tmfMPTSetCanEscrow | tmfMPTClearCanEscrow |
|
||||
tmfMPTSetCanTrade | tmfMPTClearCanTrade | tmfMPTSetCanTransfer | tmfMPTClearCanTransfer |
|
||||
tmfMPTSetCanClawback | tmfMPTClearCanClawback);
|
||||
|
||||
inline constexpr FlagValue tmfMPTSetCanLock = 0x00000001;
|
||||
inline constexpr FlagValue tmfMPTClearCanLock = 0x00000002;
|
||||
inline constexpr FlagValue tmfMPTSetRequireAuth = 0x00000004;
|
||||
inline constexpr FlagValue tmfMPTClearRequireAuth = 0x00000008;
|
||||
inline constexpr FlagValue tmfMPTSetCanEscrow = 0x00000010;
|
||||
inline constexpr FlagValue tmfMPTClearCanEscrow = 0x00000020;
|
||||
inline constexpr FlagValue tmfMPTSetCanTrade = 0x00000040;
|
||||
inline constexpr FlagValue tmfMPTClearCanTrade = 0x00000080;
|
||||
inline constexpr FlagValue tmfMPTSetCanTransfer = 0x00000100;
|
||||
inline constexpr FlagValue tmfMPTClearCanTransfer = 0x00000200;
|
||||
inline constexpr FlagValue tmfMPTSetCanClawback = 0x00000400;
|
||||
inline constexpr FlagValue tmfMPTClearCanClawback = 0x00000800;
|
||||
inline constexpr FlagValue tmfMPTokenIssuanceSetMutableMask = ~(
|
||||
tmfMPTSetCanLock | tmfMPTClearCanLock | tmfMPTSetRequireAuth | tmfMPTClearRequireAuth |
|
||||
tmfMPTSetCanEscrow | tmfMPTClearCanEscrow | tmfMPTSetCanTrade | tmfMPTClearCanTrade |
|
||||
tmfMPTSetCanTransfer | tmfMPTClearCanTransfer | tmfMPTSetCanClawback | tmfMPTClearCanClawback);
|
||||
// MPTokenIssuanceDestroy flags:
|
||||
constexpr std::uint32_t const tfMPTokenIssuanceDestroyMask = ~tfUniversal;
|
||||
|
||||
// Prior to fixRemoveNFTokenAutoTrustLine, transfer of an NFToken between accounts allowed a
|
||||
// TrustLine to be added to the issuer of that token without explicit permission from that issuer.
|
||||
// This was enabled by minting the NFToken with the tfTrustLine flag set.
|
||||
// Prior to fixRemoveNFTokenAutoTrustLine, transfer of an NFToken between
|
||||
// accounts allowed a TrustLine to be added to the issuer of that token
|
||||
// without explicit permission from that issuer. This was enabled by
|
||||
// minting the NFToken with the tfTrustLine flag set.
|
||||
//
|
||||
// That capability could be used to attack the NFToken issuer.
|
||||
// It would be possible for two accounts to trade the NFToken back and forth building up any number
|
||||
// of TrustLines on the issuer, increasing the issuer's reserve without bound.
|
||||
// That capability could be used to attack the NFToken issuer. It
|
||||
// would be possible for two accounts to trade the NFToken back and forth
|
||||
// building up any number of TrustLines on the issuer, increasing the
|
||||
// issuer's reserve without bound.
|
||||
//
|
||||
// The fixRemoveNFTokenAutoTrustLine amendment disables minting with the tfTrustLine flag as a way
|
||||
// to prevent the attack. But until the amendment passes we still need to keep the old behavior
|
||||
// available.
|
||||
inline constexpr FlagValue tfTrustLine = 0x00000004; // needed for backwards compatibility
|
||||
inline constexpr FlagValue tfNFTokenMintMaskWithoutMutable =
|
||||
// The fixRemoveNFTokenAutoTrustLine amendment disables minting with the
|
||||
// tfTrustLine flag as a way to prevent the attack. But until the
|
||||
// amendment passes we still need to keep the old behavior available.
|
||||
constexpr std::uint32_t const tfNFTokenMintMask =
|
||||
~(tfUniversal | tfBurnable | tfOnlyXRP | tfTransferable);
|
||||
|
||||
inline constexpr FlagValue tfNFTokenMintOldMask = ~(~tfNFTokenMintMaskWithoutMutable | tfTrustLine);
|
||||
constexpr std::uint32_t const tfNFTokenMintOldMask =
|
||||
~( ~tfNFTokenMintMask | tfTrustLine);
|
||||
|
||||
// if featureDynamicNFT enabled then new flag allowing mutable URI available.
|
||||
inline constexpr FlagValue tfNFTokenMintOldMaskWithMutable = ~(~tfNFTokenMintOldMask | tfMutable);
|
||||
constexpr std::uint32_t const tfNFTokenMintOldMaskWithMutable =
|
||||
~( ~tfNFTokenMintOldMask | tfMutable);
|
||||
|
||||
inline constexpr FlagValue tfWithdrawSubTx = tfLPToken | tfSingleAsset | tfTwoAsset |
|
||||
tfOneAssetLPToken | tfLimitLPToken | tfWithdrawAll | tfOneAssetWithdrawAll;
|
||||
inline constexpr FlagValue tfDepositSubTx =
|
||||
tfLPToken | tfSingleAsset | tfTwoAsset | tfOneAssetLPToken | tfLimitLPToken | tfTwoAssetIfEmpty;
|
||||
constexpr std::uint32_t const tfNFTokenMintMaskWithMutable =
|
||||
~( ~tfNFTokenMintMask | tfMutable);
|
||||
|
||||
#pragma push_macro("ACCOUNTSET_FLAGS")
|
||||
#pragma push_macro("ACCOUNTSET_FLAG_TO_VALUE")
|
||||
#pragma push_macro("ACCOUNTSET_FLAG_TO_MAP")
|
||||
// NFTokenCreateOffer flags:
|
||||
constexpr std::uint32_t const tfSellNFToken = 0x00000001;
|
||||
constexpr std::uint32_t const tfNFTokenCreateOfferMask =
|
||||
~(tfUniversal | tfSellNFToken);
|
||||
|
||||
// AccountSet SetFlag/ClearFlag values
|
||||
#define ACCOUNTSET_FLAGS(ASF_FLAG) \
|
||||
ASF_FLAG(asfRequireDest, 1) \
|
||||
ASF_FLAG(asfRequireAuth, 2) \
|
||||
ASF_FLAG(asfDisallowXRP, 3) \
|
||||
ASF_FLAG(asfDisableMaster, 4) \
|
||||
ASF_FLAG(asfAccountTxnID, 5) \
|
||||
ASF_FLAG(asfNoFreeze, 6) \
|
||||
ASF_FLAG(asfGlobalFreeze, 7) \
|
||||
ASF_FLAG(asfDefaultRipple, 8) \
|
||||
ASF_FLAG(asfDepositAuth, 9) \
|
||||
ASF_FLAG(asfAuthorizedNFTokenMinter, 10) \
|
||||
/* 11 is reserved for Hooks amendment */ \
|
||||
/* ASF_FLAG(asfTshCollect, 11) */ \
|
||||
ASF_FLAG(asfDisallowIncomingNFTokenOffer, 12) \
|
||||
ASF_FLAG(asfDisallowIncomingCheck, 13) \
|
||||
ASF_FLAG(asfDisallowIncomingPayChan, 14) \
|
||||
ASF_FLAG(asfDisallowIncomingTrustline, 15) \
|
||||
ASF_FLAG(asfAllowTrustLineClawback, 16) \
|
||||
ASF_FLAG(asfAllowTrustLineLocking, 17)
|
||||
// NFTokenCancelOffer flags:
|
||||
constexpr std::uint32_t const tfNFTokenCancelOfferMask = ~tfUniversal;
|
||||
|
||||
#define ACCOUNTSET_FLAG_TO_VALUE(name, value) inline constexpr FlagValue name = value;
|
||||
#define ACCOUNTSET_FLAG_TO_MAP(name, value) {#name, value},
|
||||
// NFTokenAcceptOffer flags:
|
||||
constexpr std::uint32_t const tfNFTokenAcceptOfferMask = ~tfUniversal;
|
||||
|
||||
ACCOUNTSET_FLAGS(ACCOUNTSET_FLAG_TO_VALUE)
|
||||
// Clawback flags:
|
||||
constexpr std::uint32_t const tfClawbackMask = ~tfUniversal;
|
||||
|
||||
inline std::map<std::string, FlagValue> const&
|
||||
getAsfFlagMap()
|
||||
{
|
||||
static std::map<std::string, FlagValue> const flags = {
|
||||
ACCOUNTSET_FLAGS(ACCOUNTSET_FLAG_TO_MAP)};
|
||||
return flags;
|
||||
}
|
||||
// AMM Flags:
|
||||
constexpr std::uint32_t tfLPToken = 0x00010000;
|
||||
constexpr std::uint32_t tfWithdrawAll = 0x00020000;
|
||||
constexpr std::uint32_t tfOneAssetWithdrawAll = 0x00040000;
|
||||
constexpr std::uint32_t tfSingleAsset = 0x00080000;
|
||||
constexpr std::uint32_t tfTwoAsset = 0x00100000;
|
||||
constexpr std::uint32_t tfOneAssetLPToken = 0x00200000;
|
||||
constexpr std::uint32_t tfLimitLPToken = 0x00400000;
|
||||
constexpr std::uint32_t tfTwoAssetIfEmpty = 0x00800000;
|
||||
constexpr std::uint32_t tfWithdrawSubTx =
|
||||
tfLPToken | tfSingleAsset | tfTwoAsset | tfOneAssetLPToken |
|
||||
tfLimitLPToken | tfWithdrawAll | tfOneAssetWithdrawAll;
|
||||
constexpr std::uint32_t tfDepositSubTx =
|
||||
tfLPToken | tfSingleAsset | tfTwoAsset | tfOneAssetLPToken |
|
||||
tfLimitLPToken | tfTwoAssetIfEmpty;
|
||||
constexpr std::uint32_t tfWithdrawMask = ~(tfUniversal | tfWithdrawSubTx);
|
||||
constexpr std::uint32_t tfDepositMask = ~(tfUniversal | tfDepositSubTx);
|
||||
|
||||
#undef ACCOUNTSET_FLAG_TO_VALUE
|
||||
#undef ACCOUNTSET_FLAG_TO_MAP
|
||||
#undef ACCOUNTSET_FLAGS
|
||||
// AMMClawback flags:
|
||||
constexpr std::uint32_t tfClawTwoAssets = 0x00000001;
|
||||
constexpr std::uint32_t tfAMMClawbackMask = ~(tfUniversal | tfClawTwoAssets);
|
||||
|
||||
#pragma pop_macro("ACCOUNTSET_FLAG_TO_VALUE")
|
||||
#pragma pop_macro("ACCOUNTSET_FLAG_TO_MAP")
|
||||
#pragma pop_macro("ACCOUNTSET_FLAGS")
|
||||
// BridgeModify flags:
|
||||
constexpr std::uint32_t tfClearAccountCreateAmount = 0x00010000;
|
||||
constexpr std::uint32_t tfBridgeModifyMask = ~(tfUniversal | tfClearAccountCreateAmount);
|
||||
|
||||
// VaultCreate flags:
|
||||
constexpr std::uint32_t const tfVaultPrivate = 0x00010000;
|
||||
static_assert(tfVaultPrivate == lsfVaultPrivate);
|
||||
constexpr std::uint32_t const tfVaultShareNonTransferable = 0x00020000;
|
||||
constexpr std::uint32_t const tfVaultCreateMask = ~(tfUniversal | tfVaultPrivate | tfVaultShareNonTransferable);
|
||||
|
||||
// Batch Flags:
|
||||
constexpr std::uint32_t tfAllOrNothing = 0x00010000;
|
||||
constexpr std::uint32_t tfOnlyOne = 0x00020000;
|
||||
constexpr std::uint32_t tfUntilFailure = 0x00040000;
|
||||
constexpr std::uint32_t tfIndependent = 0x00080000;
|
||||
/**
|
||||
* @note If nested Batch transactions are supported in the future, the tfInnerBatchTxn flag
|
||||
* will need to be removed from this mask to allow Batch transaction to be inside
|
||||
* the sfRawTransactions array.
|
||||
*/
|
||||
constexpr std::uint32_t const tfBatchMask =
|
||||
~(tfUniversal | tfAllOrNothing | tfOnlyOne | tfUntilFailure | tfIndependent) | tfInnerBatchTxn;
|
||||
|
||||
// LoanSet and LoanPay flags:
|
||||
// LoanSet: True, indicates the loan supports overpayments
|
||||
// LoanPay: True, indicates any excess in this payment can be used
|
||||
// as an overpayment. False, no overpayments will be taken.
|
||||
constexpr std::uint32_t const tfLoanOverpayment = 0x00010000;
|
||||
// LoanPay exclusive flags:
|
||||
// tfLoanFullPayment: True, indicates that the payment is an early
|
||||
// full payment. It must pay the entire loan including close
|
||||
// interest and fees, or it will fail. False: Not a full payment.
|
||||
constexpr std::uint32_t const tfLoanFullPayment = 0x00020000;
|
||||
// tfLoanLatePayment: True, indicates that the payment is late,
|
||||
// and includes late interest and fees. If the loan is not late,
|
||||
// it will fail. False: not a late payment. If the current payment
|
||||
// is overdue, the transaction will fail.
|
||||
constexpr std::uint32_t const tfLoanLatePayment = 0x00040000;
|
||||
constexpr std::uint32_t const tfLoanSetMask = ~(tfUniversal |
|
||||
tfLoanOverpayment);
|
||||
constexpr std::uint32_t const tfLoanPayMask = ~(tfUniversal |
|
||||
tfLoanOverpayment | tfLoanFullPayment | tfLoanLatePayment);
|
||||
|
||||
// LoanManage flags:
|
||||
constexpr std::uint32_t const tfLoanDefault = 0x00010000;
|
||||
constexpr std::uint32_t const tfLoanImpair = 0x00020000;
|
||||
constexpr std::uint32_t const tfLoanUnimpair = 0x00040000;
|
||||
constexpr std::uint32_t const tfLoanManageMask = ~(tfUniversal | tfLoanDefault | tfLoanImpair | tfLoanUnimpair);
|
||||
|
||||
// clang-format on
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
#include <xrpl/protocol/KnownFormats.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** Transaction type identifiers.
|
||||
@@ -75,9 +73,6 @@ private:
|
||||
public:
|
||||
static TxFormats const&
|
||||
getInstance();
|
||||
|
||||
static std::vector<SOElement> const&
|
||||
getCommonFields();
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -25,7 +25,6 @@ namespace jss {
|
||||
JSS(AL_size); // out: GetCounts
|
||||
JSS(AL_hit_rate); // out: GetCounts
|
||||
JSS(AcceptedCredentials); // out: AccountObjects
|
||||
JSS(ACCOUNT_SET_FLAGS); // out: RPC server_definitions
|
||||
JSS(Account); // in: TransactionSign; field.
|
||||
JSS(AMMID); // field
|
||||
JSS(Amount); // in: TransactionSign; field.
|
||||
@@ -188,7 +187,6 @@ JSS(closed_ledger); // out: NetworkOPs
|
||||
JSS(cluster); // out: PeerImp
|
||||
JSS(code); // out: errors
|
||||
JSS(command); // in: RPCHandler
|
||||
JSS(common); // out: RPC server_definitions
|
||||
JSS(complete); // out: NetworkOPs, InboundLedger
|
||||
JSS(complete_ledgers); // out: NetworkOPs, PeerImp
|
||||
JSS(consensus); // out: NetworkOPs, LedgerConsensus
|
||||
@@ -358,8 +356,6 @@ JSS(ledger_min); // in, out: AccountTx*
|
||||
JSS(ledger_time); // out: NetworkOPs
|
||||
JSS(LEDGER_ENTRY_TYPES); // out: RPC server_definitions
|
||||
// matches definitions.json format
|
||||
JSS(LEDGER_ENTRY_FLAGS); // out: RPC server_definitions
|
||||
JSS(LEDGER_ENTRY_FORMATS); // out: RPC server_definitions
|
||||
JSS(levels); // LogLevels
|
||||
JSS(limit); // in/out: AccountTx*, AccountOffers,
|
||||
// AccountLines, AccountObjects
|
||||
@@ -461,7 +457,6 @@ JSS(open); // out: handlers/Ledger
|
||||
JSS(open_ledger_cost); // out: SubmitTransaction
|
||||
JSS(open_ledger_fee); // out: TxQ
|
||||
JSS(open_ledger_level); // out: TxQ
|
||||
JSS(optionality); // out: server_definitions
|
||||
JSS(oracles); // in: get_aggregate_price
|
||||
JSS(oracle_document_id); // in: get_aggregate_price
|
||||
JSS(owner); // in: LedgerEntry, out: NetworkOPs
|
||||
@@ -621,8 +616,6 @@ JSS(TRANSACTION_RESULTS); // out: RPC server_definitions
|
||||
// matches definitions.json format
|
||||
JSS(TRANSACTION_TYPES); // out: RPC server_definitions
|
||||
// matches definitions.json format
|
||||
JSS(TRANSACTION_FLAGS); // out: RPC server_definitions
|
||||
JSS(TRANSACTION_FORMATS); // out: RPC server_definitions
|
||||
JSS(TYPES); // out: RPC server_definitions
|
||||
// matches definitions.json format
|
||||
JSS(transfer_rate); // out: nft_info (clio)
|
||||
|
||||
@@ -13,6 +13,9 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
|
||||
@@ -26,6 +26,9 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
|
||||
@@ -9,20 +9,17 @@
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma message("Using boost::multiprecision::uint128_t and int128_t")
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
using uint128_t = boost::multiprecision::uint128_t;
|
||||
using int128_t = boost::multiprecision::int128_t;
|
||||
#else // !defined(_MSC_VER)
|
||||
using uint128_t = __uint128_t;
|
||||
using int128_t = __int128_t;
|
||||
#endif // !defined(_MSC_VER)
|
||||
#endif
|
||||
|
||||
using uint128_t = xrpl::detail::uint128_t;
|
||||
using int128_t = xrpl::detail::int128_t;
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -61,9 +58,6 @@ Number::setMantissaScale(MantissaRange::mantissa_scale scale)
|
||||
// precision to an operation. This enables the final result
|
||||
// to be correctly rounded to the internal precision of Number.
|
||||
|
||||
template <class T>
|
||||
concept UnsignedMantissa = std::is_unsigned_v<T> || std::is_same_v<T, uint128_t>;
|
||||
|
||||
class Number::Guard
|
||||
{
|
||||
std::uint64_t digits_; // 16 decimal guard digits
|
||||
@@ -99,7 +93,7 @@ public:
|
||||
round() noexcept;
|
||||
|
||||
// Modify the result to the correctly rounded value
|
||||
template <UnsignedMantissa T>
|
||||
template <detail::UnsignedMantissa T>
|
||||
void
|
||||
doRoundUp(
|
||||
bool& negative,
|
||||
@@ -107,22 +101,22 @@ public:
|
||||
int& exponent,
|
||||
internalrep const& minMantissa,
|
||||
internalrep const& maxMantissa,
|
||||
std::string location);
|
||||
std::string_view location);
|
||||
|
||||
// Modify the result to the correctly rounded value
|
||||
template <UnsignedMantissa T>
|
||||
template <detail::UnsignedMantissa T>
|
||||
void
|
||||
doRoundDown(bool& negative, T& mantissa, int& exponent, internalrep const& minMantissa);
|
||||
|
||||
// Modify the result to the correctly rounded value
|
||||
void
|
||||
doRound(rep& drops, std::string location);
|
||||
doRound(internalrep& drops, std::string_view location);
|
||||
|
||||
private:
|
||||
void
|
||||
doPush(unsigned d) noexcept;
|
||||
|
||||
template <UnsignedMantissa T>
|
||||
template <detail::UnsignedMantissa T>
|
||||
void
|
||||
bringIntoRange(bool& negative, T& mantissa, int& exponent, internalrep const& minMantissa);
|
||||
};
|
||||
@@ -209,7 +203,7 @@ Number::Guard::round() noexcept
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <UnsignedMantissa T>
|
||||
template <detail::UnsignedMantissa T>
|
||||
void
|
||||
Number::Guard::bringIntoRange(
|
||||
bool& negative,
|
||||
@@ -228,13 +222,11 @@ Number::Guard::bringIntoRange(
|
||||
{
|
||||
constexpr Number zero = Number{};
|
||||
|
||||
negative = zero.negative_;
|
||||
mantissa = zero.mantissa_;
|
||||
exponent = zero.exponent_;
|
||||
std::tie(negative, mantissa, exponent) = zero.toInternal();
|
||||
}
|
||||
}
|
||||
|
||||
template <UnsignedMantissa T>
|
||||
template <detail::UnsignedMantissa T>
|
||||
void
|
||||
Number::Guard::doRoundUp(
|
||||
bool& negative,
|
||||
@@ -242,7 +234,7 @@ Number::Guard::doRoundUp(
|
||||
int& exponent,
|
||||
internalrep const& minMantissa,
|
||||
internalrep const& maxMantissa,
|
||||
std::string location)
|
||||
std::string_view location)
|
||||
{
|
||||
auto r = round();
|
||||
if (r == 1 || (r == 0 && (mantissa & 1) == 1))
|
||||
@@ -250,7 +242,7 @@ Number::Guard::doRoundUp(
|
||||
++mantissa;
|
||||
// Ensure mantissa after incrementing fits within both the
|
||||
// min/maxMantissa range and is a valid "rep".
|
||||
if (mantissa > maxMantissa || mantissa > maxRep)
|
||||
if (mantissa > maxMantissa)
|
||||
{
|
||||
mantissa /= 10;
|
||||
++exponent;
|
||||
@@ -258,10 +250,10 @@ Number::Guard::doRoundUp(
|
||||
}
|
||||
bringIntoRange(negative, mantissa, exponent, minMantissa);
|
||||
if (exponent > maxExponent)
|
||||
throw std::overflow_error(location);
|
||||
throw std::overflow_error(std::string{location});
|
||||
}
|
||||
|
||||
template <UnsignedMantissa T>
|
||||
template <detail::UnsignedMantissa T>
|
||||
void
|
||||
Number::Guard::doRoundDown(
|
||||
bool& negative,
|
||||
@@ -284,26 +276,25 @@ Number::Guard::doRoundDown(
|
||||
|
||||
// Modify the result to the correctly rounded value
|
||||
void
|
||||
Number::Guard::doRound(rep& drops, std::string location)
|
||||
Number::Guard::doRound(internalrep& drops, std::string_view location)
|
||||
{
|
||||
auto r = round();
|
||||
if (r == 1 || (r == 0 && (drops & 1) == 1))
|
||||
{
|
||||
if (drops >= maxRep)
|
||||
auto const& range = range_.get();
|
||||
if (drops >= range.max)
|
||||
{
|
||||
static_assert(sizeof(internalrep) == sizeof(rep));
|
||||
// This should be impossible, because it's impossible to represent
|
||||
// "maxRep + 0.6" in Number, regardless of the scale. There aren't
|
||||
// enough digits available. You'd either get a mantissa of "maxRep"
|
||||
// or "(maxRep + 1) / 10", neither of which will round up when
|
||||
// "largestMantissa + 0.6" in Number, regardless of the scale. There aren't
|
||||
// enough digits available. You'd either get a mantissa of "largestMantissa"
|
||||
// or "largestMantissa / 10 + 1", neither of which will round up when
|
||||
// converting to rep, though the latter might overflow _before_
|
||||
// rounding.
|
||||
throw std::overflow_error(location); // LCOV_EXCL_LINE
|
||||
throw std::overflow_error(std::string{location}); // LCOV_EXCL_LINE
|
||||
}
|
||||
++drops;
|
||||
}
|
||||
if (is_negative())
|
||||
drops = -drops;
|
||||
}
|
||||
|
||||
// Number
|
||||
@@ -318,23 +309,135 @@ Number::externalToInternal(rep mantissa)
|
||||
// If the mantissa is already positive, just return it
|
||||
if (mantissa >= 0)
|
||||
return mantissa;
|
||||
// If the mantissa is negative, but fits within the positive range of rep,
|
||||
// return it negated
|
||||
if (mantissa >= -std::numeric_limits<rep>::max())
|
||||
return -mantissa;
|
||||
|
||||
// If the mantissa doesn't fit within the positive range, convert to
|
||||
// int128_t, negate that, and cast it back down to the internalrep
|
||||
// In practice, this is only going to cover the case of
|
||||
// std::numeric_limits<rep>::min().
|
||||
int128_t temp = mantissa;
|
||||
return static_cast<internalrep>(-temp);
|
||||
// Cast to unsigned before negating to avoid undefined behavior when
|
||||
// mantissa == std::numeric_limits<rep>::min() (INT64_MIN). Negating
|
||||
// INT64_MIN in signed arithmetic is UB, but casting to the unsigned
|
||||
// internalrep first makes the operation well-defined.
|
||||
return -static_cast<internalrep>(mantissa);
|
||||
}
|
||||
|
||||
/** Breaks down the number into components, potentially de-normalizing it.
|
||||
*
|
||||
* Ensures that the mantissa always has range_.log + 1 digits.
|
||||
*
|
||||
*/
|
||||
template <detail::UnsignedMantissa Rep>
|
||||
std::tuple<bool, Rep, int>
|
||||
Number::toInternal(MantissaRange const& range) const
|
||||
{
|
||||
auto exponent = exponent_;
|
||||
bool const negative = mantissa_ < 0;
|
||||
// It should be impossible for mantissa_ to be INT64_MIN, but use externalToInternal just in
|
||||
// case.
|
||||
Rep mantissa = static_cast<Rep>(externalToInternal(mantissa_));
|
||||
|
||||
auto const internalMin = range.internalMin;
|
||||
auto const minMantissa = range.min;
|
||||
|
||||
if (mantissa != 0 && mantissa >= minMantissa && mantissa < internalMin)
|
||||
{
|
||||
// Ensure the mantissa has the correct number of digits
|
||||
mantissa *= 10;
|
||||
--exponent;
|
||||
XRPL_ASSERT_PARTS(
|
||||
mantissa >= internalMin && mantissa < internalMin * 10,
|
||||
"xrpl::Number::toInternal()",
|
||||
"Number is within reference range and has 'log' digits");
|
||||
}
|
||||
|
||||
return {negative, mantissa, exponent};
|
||||
}
|
||||
|
||||
/** Breaks down the number into components, potentially de-normalizing it.
|
||||
*
|
||||
* Ensures that the mantissa always has exactly range_.log + 1 digits.
|
||||
*
|
||||
*/
|
||||
template <detail::UnsignedMantissa Rep>
|
||||
std::tuple<bool, Rep, int>
|
||||
Number::toInternal() const
|
||||
{
|
||||
return toInternal(range_);
|
||||
}
|
||||
|
||||
/** Rebuilds the number from components.
|
||||
*
|
||||
* If "expectNormal" is true, the values are expected to be normalized - all
|
||||
* in their valid ranges.
|
||||
*
|
||||
* If "expectNormal" is false, the values are expected to be "near
|
||||
* normalized", meaning that the mantissa has to be modified at most once to
|
||||
* bring it back into range.
|
||||
*
|
||||
*/
|
||||
template <bool expectNormal, detail::UnsignedMantissa Rep>
|
||||
void
|
||||
Number::fromInternal(bool negative, Rep mantissa, int exponent, MantissaRange const* pRange)
|
||||
{
|
||||
if constexpr (std::is_same_v<std::bool_constant<expectNormal>, std::false_type>)
|
||||
{
|
||||
if (!pRange)
|
||||
throw std::runtime_error("Missing range to Number::fromInternal!");
|
||||
auto const& range = *pRange;
|
||||
|
||||
auto const maxMantissa = range.max;
|
||||
auto const minMantissa = range.min;
|
||||
|
||||
XRPL_ASSERT_PARTS(
|
||||
mantissa >= minMantissa, "xrpl::Number::fromInternal", "mantissa large enough");
|
||||
|
||||
if (mantissa > maxMantissa || mantissa < minMantissa)
|
||||
{
|
||||
normalize(negative, mantissa, exponent, range.min, maxMantissa);
|
||||
}
|
||||
|
||||
XRPL_ASSERT_PARTS(
|
||||
mantissa >= minMantissa && mantissa <= maxMantissa,
|
||||
"xrpl::Number::fromInternal",
|
||||
"mantissa in range");
|
||||
}
|
||||
|
||||
// mantissa is unsigned, but it might not be uint64
|
||||
mantissa_ = static_cast<rep>(static_cast<internalrep>(mantissa));
|
||||
if (negative)
|
||||
mantissa_ = -mantissa_;
|
||||
exponent_ = exponent;
|
||||
|
||||
XRPL_ASSERT_PARTS(
|
||||
(pRange && isnormal(*pRange)) || isnormal(),
|
||||
"xrpl::Number::fromInternal",
|
||||
"Number is normalized");
|
||||
}
|
||||
|
||||
/** Rebuilds the number from components.
|
||||
*
|
||||
* If "expectNormal" is true, the values are expected to be normalized - all in
|
||||
* their valid ranges.
|
||||
*
|
||||
* If "expectNormal" is false, the values are expected to be "near normalized",
|
||||
* meaning that the mantissa has to be modified at most once to bring it back
|
||||
* into range.
|
||||
*
|
||||
*/
|
||||
template <bool expectNormal, detail::UnsignedMantissa Rep>
|
||||
void
|
||||
Number::fromInternal(bool negative, Rep mantissa, int exponent)
|
||||
{
|
||||
MantissaRange const* pRange = nullptr;
|
||||
if constexpr (std::is_same_v<std::bool_constant<expectNormal>, std::false_type>)
|
||||
{
|
||||
pRange = &Number::range_.get();
|
||||
}
|
||||
|
||||
fromInternal(negative, mantissa, exponent, pRange);
|
||||
}
|
||||
|
||||
constexpr Number
|
||||
Number::oneSmall()
|
||||
{
|
||||
return Number{false, Number::smallRange.min, -Number::smallRange.log, Number::unchecked{}};
|
||||
return Number{
|
||||
false, Number::smallRange.internalMin, -Number::smallRange.log, Number::unchecked{}};
|
||||
};
|
||||
|
||||
constexpr Number oneSml = Number::oneSmall();
|
||||
@@ -342,103 +445,86 @@ constexpr Number oneSml = Number::oneSmall();
|
||||
constexpr Number
|
||||
Number::oneLarge()
|
||||
{
|
||||
return Number{false, Number::largeRange.min, -Number::largeRange.log, Number::unchecked{}};
|
||||
return Number{
|
||||
false, Number::largeRange.internalMin, -Number::largeRange.log, Number::unchecked{}};
|
||||
};
|
||||
|
||||
constexpr Number oneLrg = Number::oneLarge();
|
||||
|
||||
Number
|
||||
Number::one()
|
||||
Number::one(MantissaRange const& range)
|
||||
{
|
||||
if (&range_.get() == &smallRange)
|
||||
if (&range == &smallRange)
|
||||
return oneSml;
|
||||
XRPL_ASSERT(&range_.get() == &largeRange, "Number::one() : valid range_");
|
||||
XRPL_ASSERT(&range == &largeRange, "Number::one() : valid range");
|
||||
return oneLrg;
|
||||
}
|
||||
|
||||
Number
|
||||
Number::one()
|
||||
{
|
||||
return one(range_);
|
||||
}
|
||||
|
||||
// Use the member names in this static function for now so the diff is cleaner
|
||||
// TODO: Rename the function parameters to get rid of the "_" suffix
|
||||
template <class T>
|
||||
void
|
||||
doNormalize(
|
||||
bool& negative,
|
||||
T& mantissa_,
|
||||
int& exponent_,
|
||||
T& mantissa,
|
||||
int& exponent,
|
||||
MantissaRange::rep const& minMantissa,
|
||||
MantissaRange::rep const& maxMantissa)
|
||||
{
|
||||
auto constexpr minExponent = Number::minExponent;
|
||||
auto constexpr maxExponent = Number::maxExponent;
|
||||
auto constexpr maxRep = Number::maxRep;
|
||||
|
||||
using Guard = Number::Guard;
|
||||
|
||||
constexpr Number zero = Number{};
|
||||
if (mantissa_ == 0)
|
||||
auto const& range = Number::range_.get();
|
||||
if (mantissa == 0 || (mantissa < minMantissa && exponent <= minExponent))
|
||||
{
|
||||
mantissa_ = zero.mantissa_;
|
||||
exponent_ = zero.exponent_;
|
||||
negative = zero.negative_;
|
||||
std::tie(negative, mantissa, exponent) = zero.toInternal(range);
|
||||
return;
|
||||
}
|
||||
auto m = mantissa_;
|
||||
while ((m < minMantissa) && (exponent_ > minExponent))
|
||||
|
||||
auto m = mantissa;
|
||||
while ((m < minMantissa) && (exponent > minExponent))
|
||||
{
|
||||
m *= 10;
|
||||
--exponent_;
|
||||
--exponent;
|
||||
}
|
||||
Guard g;
|
||||
if (negative)
|
||||
g.set_negative();
|
||||
while (m > maxMantissa)
|
||||
{
|
||||
if (exponent_ >= maxExponent)
|
||||
if (exponent >= maxExponent)
|
||||
throw std::overflow_error("Number::normalize 1");
|
||||
g.push(m % 10);
|
||||
m /= 10;
|
||||
++exponent_;
|
||||
++exponent;
|
||||
}
|
||||
if ((exponent_ < minExponent) || (m < minMantissa))
|
||||
if ((exponent < minExponent) || (m == 0))
|
||||
{
|
||||
mantissa_ = zero.mantissa_;
|
||||
exponent_ = zero.exponent_;
|
||||
negative = zero.negative_;
|
||||
std::tie(negative, mantissa, exponent) = zero.toInternal(range);
|
||||
return;
|
||||
}
|
||||
|
||||
// When using the largeRange, "m" needs fit within an int64, even if
|
||||
// the final mantissa_ is going to end up larger to fit within the
|
||||
// MantissaRange. Cut it down here so that the rounding will be done while
|
||||
// it's smaller.
|
||||
//
|
||||
// Example: 9,900,000,000,000,123,456 > 9,223,372,036,854,775,807,
|
||||
// so "m" will be modified to 990,000,000,000,012,345. Then that value
|
||||
// will be rounded to 990,000,000,000,012,345 or
|
||||
// 990,000,000,000,012,346, depending on the rounding mode. Finally,
|
||||
// mantissa_ will be "m*10" so it fits within the range, and end up as
|
||||
// 9,900,000,000,000,123,450 or 9,900,000,000,000,123,460.
|
||||
// mantissa() will return mantissa_ / 10, and exponent() will return
|
||||
// exponent_ + 1.
|
||||
if (m > maxRep)
|
||||
{
|
||||
if (exponent_ >= maxExponent)
|
||||
throw std::overflow_error("Number::normalize 1.5");
|
||||
g.push(m % 10);
|
||||
m /= 10;
|
||||
++exponent_;
|
||||
}
|
||||
// Before modification, m should be within the min/max range. After
|
||||
// modification, it must be less than maxRep. In other words, the original
|
||||
// value should have been no more than maxRep * 10.
|
||||
// (maxRep * 10 > maxMantissa)
|
||||
XRPL_ASSERT_PARTS(m <= maxRep, "xrpl::doNormalize", "intermediate mantissa fits in int64");
|
||||
mantissa_ = m;
|
||||
XRPL_ASSERT_PARTS(m <= maxMantissa, "xrpl::doNormalize", "intermediate mantissa fits in int64");
|
||||
mantissa = m;
|
||||
|
||||
g.doRoundUp(negative, mantissa, exponent, minMantissa, maxMantissa, "Number::normalize 2");
|
||||
|
||||
g.doRoundUp(negative, mantissa_, exponent_, minMantissa, maxMantissa, "Number::normalize 2");
|
||||
XRPL_ASSERT_PARTS(
|
||||
mantissa_ >= minMantissa && mantissa_ <= maxMantissa,
|
||||
mantissa >= minMantissa && mantissa <= maxMantissa,
|
||||
"xrpl::doNormalize",
|
||||
"final mantissa fits in range");
|
||||
XRPL_ASSERT_PARTS(
|
||||
exponent >= minExponent && exponent <= maxExponent,
|
||||
"xrpl::doNormalize",
|
||||
"final exponent fits in range");
|
||||
}
|
||||
|
||||
template <>
|
||||
@@ -477,11 +563,20 @@ Number::normalize<unsigned long>(
|
||||
doNormalize(negative, mantissa, exponent, minMantissa, maxMantissa);
|
||||
}
|
||||
|
||||
void
|
||||
Number::normalize(MantissaRange const& range)
|
||||
{
|
||||
auto [negative, mantissa, exponent] = toInternal(range);
|
||||
|
||||
normalize(negative, mantissa, exponent, range.min, range.max);
|
||||
|
||||
fromInternal(negative, mantissa, exponent, &range);
|
||||
}
|
||||
|
||||
void
|
||||
Number::normalize()
|
||||
{
|
||||
auto const& range = range_.get();
|
||||
normalize(negative_, mantissa_, exponent_, range.min, range.max);
|
||||
normalize(range_);
|
||||
}
|
||||
|
||||
// Copy the number, but set a new exponent. Because the mantissa doesn't change,
|
||||
@@ -491,21 +586,33 @@ Number
|
||||
Number::shiftExponent(int exponentDelta) const
|
||||
{
|
||||
XRPL_ASSERT_PARTS(isnormal(), "xrpl::Number::shiftExponent", "normalized");
|
||||
auto const newExponent = exponent_ + exponentDelta;
|
||||
if (newExponent >= maxExponent)
|
||||
|
||||
Number result = *this;
|
||||
|
||||
result.exponent_ += exponentDelta;
|
||||
|
||||
if (result.exponent_ >= maxExponent)
|
||||
throw std::overflow_error("Number::shiftExponent");
|
||||
if (newExponent < minExponent)
|
||||
if (result.exponent_ < minExponent)
|
||||
{
|
||||
return Number{};
|
||||
}
|
||||
Number const result{negative_, mantissa_, newExponent, unchecked{}};
|
||||
XRPL_ASSERT_PARTS(result.isnormal(), "xrpl::Number::shiftExponent", "result is normalized");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Number::Number(bool negative, internalrep mantissa, int exponent, normalized)
|
||||
{
|
||||
auto const& range = range_.get();
|
||||
normalize(negative, mantissa, exponent, range.min, range.max);
|
||||
fromInternal(negative, mantissa, exponent, &range);
|
||||
}
|
||||
|
||||
Number&
|
||||
Number::operator+=(Number const& y)
|
||||
{
|
||||
auto const& range = range_.get();
|
||||
|
||||
constexpr Number zero = Number{};
|
||||
if (y == zero)
|
||||
return *this;
|
||||
@@ -520,7 +627,8 @@ Number::operator+=(Number const& y)
|
||||
return *this;
|
||||
}
|
||||
|
||||
XRPL_ASSERT(isnormal() && y.isnormal(), "xrpl::Number::operator+=(Number) : is normal");
|
||||
XRPL_ASSERT(
|
||||
isnormal(range) && y.isnormal(range), "xrpl::Number::operator+=(Number) : is normal");
|
||||
// *n = negative
|
||||
// *s = sign
|
||||
// *m = mantissa
|
||||
@@ -528,13 +636,10 @@ Number::operator+=(Number const& y)
|
||||
|
||||
// Need to use uint128_t, because large mantissas can overflow when added
|
||||
// together.
|
||||
bool xn = negative_;
|
||||
uint128_t xm = mantissa_;
|
||||
auto xe = exponent_;
|
||||
auto [xn, xm, xe] = toInternal<uint128_t>(range);
|
||||
|
||||
auto [yn, ym, ye] = y.toInternal<uint128_t>(range);
|
||||
|
||||
bool yn = y.negative_;
|
||||
uint128_t ym = y.mantissa_;
|
||||
auto ye = y.exponent_;
|
||||
Guard g;
|
||||
if (xe < ye)
|
||||
{
|
||||
@@ -559,14 +664,13 @@ Number::operator+=(Number const& y)
|
||||
} while (xe > ye);
|
||||
}
|
||||
|
||||
auto const& range = range_.get();
|
||||
auto const& minMantissa = range.min;
|
||||
auto const& maxMantissa = range.max;
|
||||
|
||||
if (xn == yn)
|
||||
{
|
||||
xm += ym;
|
||||
if (xm > maxMantissa || xm > maxRep)
|
||||
if (xm > maxMantissa)
|
||||
{
|
||||
g.push(xm % 10);
|
||||
xm /= 10;
|
||||
@@ -586,7 +690,7 @@ Number::operator+=(Number const& y)
|
||||
xe = ye;
|
||||
xn = yn;
|
||||
}
|
||||
while (xm < minMantissa && xm * 10 <= maxRep)
|
||||
while (xm < minMantissa)
|
||||
{
|
||||
xm *= 10;
|
||||
xm -= g.pop();
|
||||
@@ -595,10 +699,8 @@ Number::operator+=(Number const& y)
|
||||
g.doRoundDown(xn, xm, xe, minMantissa);
|
||||
}
|
||||
|
||||
negative_ = xn;
|
||||
mantissa_ = static_cast<internalrep>(xm);
|
||||
exponent_ = xe;
|
||||
normalize();
|
||||
normalize(xn, xm, xe, minMantissa, maxMantissa);
|
||||
fromInternal(xn, xm, xe, &range);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -633,6 +735,8 @@ divu10(uint128_t& u)
|
||||
Number&
|
||||
Number::operator*=(Number const& y)
|
||||
{
|
||||
auto const& range = range_.get();
|
||||
|
||||
constexpr Number zero = Number{};
|
||||
if (*this == zero)
|
||||
return *this;
|
||||
@@ -646,15 +750,11 @@ Number::operator*=(Number const& y)
|
||||
// *m = mantissa
|
||||
// *e = exponent
|
||||
|
||||
bool xn = negative_;
|
||||
auto [xn, xm, xe] = toInternal(range);
|
||||
int xs = xn ? -1 : 1;
|
||||
internalrep xm = mantissa_;
|
||||
auto xe = exponent_;
|
||||
|
||||
bool yn = y.negative_;
|
||||
auto [yn, ym, ye] = y.toInternal(range);
|
||||
int ys = yn ? -1 : 1;
|
||||
internalrep ym = y.mantissa_;
|
||||
auto ye = y.exponent_;
|
||||
|
||||
auto zm = uint128_t(xm) * uint128_t(ym);
|
||||
auto ze = xe + ye;
|
||||
@@ -664,11 +764,10 @@ Number::operator*=(Number const& y)
|
||||
if (zn)
|
||||
g.set_negative();
|
||||
|
||||
auto const& range = range_.get();
|
||||
auto const& minMantissa = range.min;
|
||||
auto const& maxMantissa = range.max;
|
||||
|
||||
while (zm > maxMantissa || zm > maxRep)
|
||||
while (zm > maxMantissa)
|
||||
{
|
||||
// The following is optimization for:
|
||||
// g.push(static_cast<unsigned>(zm % 10));
|
||||
@@ -685,17 +784,17 @@ Number::operator*=(Number const& y)
|
||||
minMantissa,
|
||||
maxMantissa,
|
||||
"Number::multiplication overflow : exponent is " + std::to_string(xe));
|
||||
negative_ = zn;
|
||||
mantissa_ = xm;
|
||||
exponent_ = xe;
|
||||
|
||||
normalize();
|
||||
normalize(zn, xm, xe, minMantissa, maxMantissa);
|
||||
fromInternal(zn, xm, xe, &range);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Number&
|
||||
Number::operator/=(Number const& y)
|
||||
{
|
||||
auto const& range = range_.get();
|
||||
|
||||
constexpr Number zero = Number{};
|
||||
if (y == zero)
|
||||
throw std::overflow_error("Number: divide by 0");
|
||||
@@ -708,17 +807,12 @@ Number::operator/=(Number const& y)
|
||||
// *m = mantissa
|
||||
// *e = exponent
|
||||
|
||||
bool np = negative_;
|
||||
auto [np, nm, ne] = toInternal(range);
|
||||
int ns = (np ? -1 : 1);
|
||||
auto nm = mantissa_;
|
||||
auto ne = exponent_;
|
||||
|
||||
bool dp = y.negative_;
|
||||
auto [dp, dm, de] = y.toInternal(range);
|
||||
int ds = (dp ? -1 : 1);
|
||||
auto dm = y.mantissa_;
|
||||
auto de = y.exponent_;
|
||||
|
||||
auto const& range = range_.get();
|
||||
auto const& minMantissa = range.min;
|
||||
auto const& maxMantissa = range.max;
|
||||
|
||||
@@ -730,7 +824,7 @@ Number::operator/=(Number const& y)
|
||||
// f can be up to 10^(38-19) = 10^19 safely
|
||||
static_assert(smallRange.log == 15);
|
||||
static_assert(largeRange.log == 18);
|
||||
bool small = Number::getMantissaScale() == MantissaRange::small;
|
||||
bool small = range.scale == MantissaRange::small;
|
||||
uint128_t const f = small ? 100'000'000'000'000'000 : 10'000'000'000'000'000'000ULL;
|
||||
XRPL_ASSERT_PARTS(f >= minMantissa * 10, "Number::operator/=", "factor expected size");
|
||||
|
||||
@@ -780,10 +874,8 @@ Number::operator/=(Number const& y)
|
||||
}
|
||||
}
|
||||
normalize(zn, zm, ze, minMantissa, maxMantissa);
|
||||
negative_ = zn;
|
||||
mantissa_ = static_cast<internalrep>(zm);
|
||||
exponent_ = ze;
|
||||
XRPL_ASSERT_PARTS(isnormal(), "xrpl::Number::operator/=", "result is normalized");
|
||||
fromInternal(zn, zm, ze, &range);
|
||||
XRPL_ASSERT_PARTS(isnormal(range), "xrpl::Number::operator/=", "result is normalized");
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -791,30 +883,36 @@ Number::operator/=(Number const& y)
|
||||
Number::
|
||||
operator rep() const
|
||||
{
|
||||
rep drops = mantissa();
|
||||
auto const m = mantissa();
|
||||
internalrep drops = externalToInternal(m);
|
||||
|
||||
if (drops == 0)
|
||||
return drops;
|
||||
|
||||
int offset = exponent();
|
||||
Guard g;
|
||||
if (drops != 0)
|
||||
|
||||
if (m < 0)
|
||||
{
|
||||
if (negative_)
|
||||
{
|
||||
g.set_negative();
|
||||
drops = -drops;
|
||||
}
|
||||
for (; offset < 0; ++offset)
|
||||
{
|
||||
g.push(drops % 10);
|
||||
drops /= 10;
|
||||
}
|
||||
for (; offset > 0; --offset)
|
||||
{
|
||||
if (drops > maxRep / 10)
|
||||
throw std::overflow_error("Number::operator rep() overflow");
|
||||
drops *= 10;
|
||||
}
|
||||
g.doRound(drops, "Number::operator rep() rounding overflow");
|
||||
g.set_negative();
|
||||
}
|
||||
return drops;
|
||||
for (; offset < 0; ++offset)
|
||||
{
|
||||
g.push(drops % 10);
|
||||
drops /= 10;
|
||||
}
|
||||
for (; offset > 0; --offset)
|
||||
{
|
||||
if (drops >= largeRange.min)
|
||||
throw std::overflow_error("Number::operator rep() overflow");
|
||||
drops *= 10;
|
||||
}
|
||||
g.doRound(drops, "Number::operator rep() rounding overflow");
|
||||
|
||||
if (g.is_negative())
|
||||
return -drops;
|
||||
else
|
||||
return drops;
|
||||
}
|
||||
|
||||
Number
|
||||
@@ -838,19 +936,22 @@ Number::truncate() const noexcept
|
||||
std::string
|
||||
to_string(Number const& amount)
|
||||
{
|
||||
auto const& range = Number::range_.get();
|
||||
|
||||
// keep full internal accuracy, but make more human friendly if possible
|
||||
constexpr Number zero = Number{};
|
||||
if (amount == zero)
|
||||
return "0";
|
||||
|
||||
auto exponent = amount.exponent_;
|
||||
auto mantissa = amount.mantissa_;
|
||||
bool const negative = amount.negative_;
|
||||
// The mantissa must have a set number of decimal places for this to work
|
||||
auto [negative, mantissa, exponent] = amount.toInternal(range);
|
||||
|
||||
// Use scientific notation for exponents that are too small or too large
|
||||
auto const rangeLog = Number::mantissaLog();
|
||||
if (((exponent != 0) && ((exponent < -(rangeLog + 10)) || (exponent > -(rangeLog - 10)))))
|
||||
auto const rangeLog = range.log;
|
||||
if (((exponent != 0 && amount.exponent() != 0) &&
|
||||
((exponent < -(rangeLog + 10)) || (exponent > -(rangeLog - 10)))))
|
||||
{
|
||||
// Remove trailing zeroes from the mantissa.
|
||||
while (mantissa != 0 && mantissa % 10 == 0 && exponent < Number::maxExponent)
|
||||
{
|
||||
mantissa /= 10;
|
||||
@@ -858,8 +959,11 @@ to_string(Number const& amount)
|
||||
}
|
||||
std::string ret = negative ? "-" : "";
|
||||
ret.append(std::to_string(mantissa));
|
||||
ret.append(1, 'e');
|
||||
ret.append(std::to_string(exponent));
|
||||
if (exponent != 0)
|
||||
{
|
||||
ret.append(1, 'e');
|
||||
ret.append(std::to_string(exponent));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -943,20 +1047,11 @@ power(Number const& f, unsigned n)
|
||||
return r;
|
||||
}
|
||||
|
||||
// Returns f^(1/d)
|
||||
// Uses Newton–Raphson iterations until the result stops changing
|
||||
// to find the non-negative root of the polynomial g(x) = x^d - f
|
||||
|
||||
// This function, and power(Number f, unsigned n, unsigned d)
|
||||
// treat corner cases such as 0 roots as advised by Annex F of
|
||||
// the C standard, which itself is consistent with the IEEE
|
||||
// floating point standards.
|
||||
|
||||
Number
|
||||
root(Number f, unsigned d)
|
||||
Number::root(MantissaRange const& range, Number f, unsigned d)
|
||||
{
|
||||
constexpr Number zero = Number{};
|
||||
auto const one = Number::one();
|
||||
auto const one = Number::one(range);
|
||||
|
||||
if (f == one || d == 1)
|
||||
return f;
|
||||
@@ -973,21 +1068,28 @@ root(Number f, unsigned d)
|
||||
if (f == zero)
|
||||
return f;
|
||||
|
||||
// Scale f into the range (0, 1) such that f's exponent is a multiple of d
|
||||
auto e = f.exponent_ + Number::mantissaLog() + 1;
|
||||
auto const di = static_cast<int>(d);
|
||||
auto ex = [e = e, di = di]() // Euclidean remainder of e/d
|
||||
{
|
||||
int k = (e >= 0 ? e : e - (di - 1)) / di;
|
||||
int k2 = e - k * di;
|
||||
if (k2 == 0)
|
||||
return 0;
|
||||
return di - k2;
|
||||
}();
|
||||
e += ex;
|
||||
f = f.shiftExponent(-e); // f /= 10^e;
|
||||
auto const [e, di] = [&]() {
|
||||
auto const exponent = std::get<2>(f.toInternal(range));
|
||||
|
||||
XRPL_ASSERT_PARTS(f.isnormal(), "xrpl::root(Number, unsigned)", "f is normalized");
|
||||
// Scale f into the range (0, 1) such that the scale change (e) is a
|
||||
// multiple of the root (d)
|
||||
auto e = exponent + range.log + 1;
|
||||
auto const di = static_cast<int>(d);
|
||||
auto ex = [e = e, di = di]() // Euclidean remainder of e/d
|
||||
{
|
||||
int k = (e >= 0 ? e : e - (di - 1)) / di;
|
||||
int k2 = e - k * di;
|
||||
if (k2 == 0)
|
||||
return 0;
|
||||
return di - k2;
|
||||
}();
|
||||
e += ex;
|
||||
f = f.shiftExponent(-e); // f /= 10^e;
|
||||
return std::make_tuple(e, di);
|
||||
}();
|
||||
|
||||
XRPL_ASSERT_PARTS(e % di == 0, "xrpl::root(Number, unsigned)", "e is divisible by d");
|
||||
XRPL_ASSERT_PARTS(f.isnormal(range), "xrpl::root(Number, unsigned)", "f is normalized");
|
||||
bool neg = false;
|
||||
if (f < zero)
|
||||
{
|
||||
@@ -1020,15 +1122,33 @@ root(Number f, unsigned d)
|
||||
|
||||
// return r * 10^(e/d) to reverse scaling
|
||||
auto const result = r.shiftExponent(e / di);
|
||||
XRPL_ASSERT_PARTS(result.isnormal(), "xrpl::root(Number, unsigned)", "result is normalized");
|
||||
XRPL_ASSERT_PARTS(
|
||||
result.isnormal(range), "xrpl::root(Number, unsigned)", "result is normalized");
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns f^(1/d)
|
||||
// Uses Newton–Raphson iterations until the result stops changing
|
||||
// to find the non-negative root of the polynomial g(x) = x^d - f
|
||||
|
||||
// This function, and power(Number f, unsigned n, unsigned d)
|
||||
// treat corner cases such as 0 roots as advised by Annex F of
|
||||
// the C standard, which itself is consistent with the IEEE
|
||||
// floating point standards.
|
||||
|
||||
Number
|
||||
root(Number f, unsigned d)
|
||||
{
|
||||
auto const& range = Number::range_.get();
|
||||
return Number::root(range, f, d);
|
||||
}
|
||||
|
||||
Number
|
||||
root2(Number f)
|
||||
{
|
||||
auto const& range = Number::range_.get();
|
||||
constexpr Number zero = Number{};
|
||||
auto const one = Number::one();
|
||||
auto const one = Number::one(range);
|
||||
|
||||
if (f == one)
|
||||
return f;
|
||||
@@ -1037,12 +1157,18 @@ root2(Number f)
|
||||
if (f == zero)
|
||||
return f;
|
||||
|
||||
// Scale f into the range (0, 1) such that f's exponent is a multiple of d
|
||||
auto e = f.exponent_ + Number::mantissaLog() + 1;
|
||||
if (e % 2 != 0)
|
||||
++e;
|
||||
f = f.shiftExponent(-e); // f /= 10^e;
|
||||
XRPL_ASSERT_PARTS(f.isnormal(), "xrpl::root2(Number)", "f is normalized");
|
||||
auto const e = [&]() {
|
||||
auto const exponent = std::get<2>(f.toInternal(range));
|
||||
|
||||
// Scale f into the range (0, 1) such that f's exponent is a
|
||||
// multiple of d
|
||||
auto e = exponent + range.log + 1;
|
||||
if (e % 2 != 0)
|
||||
++e;
|
||||
f = f.shiftExponent(-e); // f /= 10^e;
|
||||
return e;
|
||||
}();
|
||||
XRPL_ASSERT_PARTS(f.isnormal(range), "xrpl::root2(Number)", "f is normalized");
|
||||
|
||||
// Quadratic least squares curve fit of f^(1/d) in the range [0, 1]
|
||||
auto const D = 105;
|
||||
@@ -1064,7 +1190,7 @@ root2(Number f)
|
||||
|
||||
// return r * 10^(e/2) to reverse scaling
|
||||
auto const result = r.shiftExponent(e / 2);
|
||||
XRPL_ASSERT_PARTS(result.isnormal(), "xrpl::root2(Number)", "result is normalized");
|
||||
XRPL_ASSERT_PARTS(result.isnormal(range), "xrpl::root2(Number)", "result is normalized");
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1074,8 +1200,10 @@ root2(Number f)
|
||||
Number
|
||||
power(Number const& f, unsigned n, unsigned d)
|
||||
{
|
||||
auto const& range = Number::range_.get();
|
||||
|
||||
constexpr Number zero = Number{};
|
||||
auto const one = Number::one();
|
||||
auto const one = Number::one(range);
|
||||
|
||||
if (f == one)
|
||||
return f;
|
||||
@@ -1097,7 +1225,7 @@ power(Number const& f, unsigned n, unsigned d)
|
||||
d /= g;
|
||||
if ((n % 2) == 1 && (d % 2) == 0 && f < zero)
|
||||
throw std::overflow_error("Number::power nan");
|
||||
return root(power(f, n), d);
|
||||
return Number::root(range, power(f, n), d);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -3,23 +3,19 @@
|
||||
#include <xrpl/protocol/SOTemplate.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <vector>
|
||||
#include <initializer_list>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
std::vector<SOElement> const&
|
||||
LedgerFormats::getCommonFields()
|
||||
LedgerFormats::LedgerFormats()
|
||||
{
|
||||
static auto const commonFields = std::vector<SOElement>{
|
||||
// Fields shared by all ledger formats:
|
||||
static std::initializer_list<SOElement> const commonFields{
|
||||
{sfLedgerIndex, soeOPTIONAL},
|
||||
{sfLedgerEntryType, soeREQUIRED},
|
||||
{sfFlags, soeREQUIRED},
|
||||
};
|
||||
return commonFields;
|
||||
}
|
||||
|
||||
LedgerFormats::LedgerFormats()
|
||||
{
|
||||
#pragma push_macro("UNWRAP")
|
||||
#undef UNWRAP
|
||||
#pragma push_macro("LEDGER_ENTRY")
|
||||
@@ -27,7 +23,7 @@ LedgerFormats::LedgerFormats()
|
||||
|
||||
#define UNWRAP(...) __VA_ARGS__
|
||||
#define LEDGER_ENTRY(tag, value, name, rpcName, fields) \
|
||||
add(jss::name, tag, UNWRAP fields, getCommonFields());
|
||||
add(jss::name, tag, UNWRAP fields, commonFields);
|
||||
|
||||
#include <xrpl/protocol/detail/ledger_entries.macro>
|
||||
|
||||
|
||||
@@ -2,32 +2,23 @@
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/SOTemplate.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
SOTemplate::SOTemplate(
|
||||
std::initializer_list<SOElement> uniqueFields,
|
||||
std::initializer_list<SOElement> commonFields)
|
||||
: SOTemplate(std::vector(uniqueFields), std::vector(commonFields))
|
||||
{
|
||||
}
|
||||
|
||||
SOTemplate::SOTemplate(std::vector<SOElement> uniqueFields, std::vector<SOElement> commonFields)
|
||||
: indices_(SField::getNumFields() + 1, -1) // Unmapped indices == -1
|
||||
{
|
||||
// Add all SOElements.
|
||||
//
|
||||
elements_ = std::move(uniqueFields);
|
||||
std::ranges::move(commonFields, std::back_inserter(elements_));
|
||||
elements_.reserve(uniqueFields.size() + commonFields.size());
|
||||
elements_.assign(uniqueFields);
|
||||
elements_.insert(elements_.end(), commonFields);
|
||||
|
||||
// Validate and index elements_.
|
||||
//
|
||||
for (std::size_t i = 0; i < elements_.size(); ++i)
|
||||
{
|
||||
SField const& sField{elements_[i].sField()};
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
#include <xrpl/protocol/TxFormats.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <vector>
|
||||
#include <initializer_list>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
std::vector<SOElement> const&
|
||||
TxFormats::getCommonFields()
|
||||
TxFormats::TxFormats()
|
||||
{
|
||||
static auto const commonFields = std::vector<SOElement>{
|
||||
// Fields shared by all txFormats:
|
||||
static std::initializer_list<SOElement> const commonFields{
|
||||
{sfTransactionType, soeREQUIRED},
|
||||
{sfFlags, soeOPTIONAL},
|
||||
{sfSourceTag, soeOPTIONAL},
|
||||
@@ -29,11 +29,7 @@ TxFormats::getCommonFields()
|
||||
{sfNetworkID, soeOPTIONAL},
|
||||
{sfDelegate, soeOPTIONAL},
|
||||
};
|
||||
return commonFields;
|
||||
}
|
||||
|
||||
TxFormats::TxFormats()
|
||||
{
|
||||
#pragma push_macro("UNWRAP")
|
||||
#undef UNWRAP
|
||||
#pragma push_macro("TRANSACTION")
|
||||
@@ -41,7 +37,7 @@ TxFormats::TxFormats()
|
||||
|
||||
#define UNWRAP(...) __VA_ARGS__
|
||||
#define TRANSACTION(tag, value, name, delegable, amendment, privileges, fields) \
|
||||
add(jss::name, tag, UNWRAP fields, getCommonFields());
|
||||
add(jss::name, tag, UNWRAP fields, commonFields);
|
||||
|
||||
#include <xrpl/protocol/detail/transactions.macro>
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ std::uint32_t
|
||||
AMMDeposit::getFlagsMask(PreflightContext const& ctx)
|
||||
|
||||
{
|
||||
return tfAMMDepositMask;
|
||||
return tfDepositMask;
|
||||
}
|
||||
|
||||
NotTEC
|
||||
|
||||
@@ -17,7 +17,7 @@ AMMWithdraw::checkExtraFeatures(PreflightContext const& ctx)
|
||||
std::uint32_t
|
||||
AMMWithdraw::getFlagsMask(PreflightContext const& ctx)
|
||||
{
|
||||
return tfAMMWithdrawMask;
|
||||
return tfWithdrawMask;
|
||||
}
|
||||
|
||||
NotTEC
|
||||
|
||||
@@ -16,11 +16,11 @@ NotTEC
|
||||
Transactor::invokePreflight<Change>(PreflightContext const& ctx)
|
||||
{
|
||||
// 0 means "Allow any flags"
|
||||
// The check for tfEnableAmendmentMask is gated by LendingProtocol because
|
||||
// that feature introduced this parameter, and it's not worth adding another
|
||||
// The check for tfChangeMask is gated by LendingProtocol because that
|
||||
// feature introduced this parameter, and it's not worth adding another
|
||||
// amendment just for this.
|
||||
if (auto const ret =
|
||||
preflight0(ctx, ctx.rules.enabled(featureLendingProtocol) ? tfEnableAmendmentMask : 0))
|
||||
preflight0(ctx, ctx.rules.enabled(featureLendingProtocol) ? tfChangeMask : 0))
|
||||
return ret;
|
||||
|
||||
auto account = ctx.tx.getAccountID(sfAccount);
|
||||
|
||||
@@ -54,6 +54,12 @@ preflightHelper<MPTIssue>(PreflightContext const& ctx)
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
Clawback::getFlagsMask(PreflightContext const& ctx)
|
||||
{
|
||||
return tfClawbackMask;
|
||||
}
|
||||
|
||||
NotTEC
|
||||
Clawback::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
|
||||
@@ -5,6 +5,12 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
std::uint32_t
|
||||
MPTokenIssuanceDestroy::getFlagsMask(PreflightContext const& ctx)
|
||||
{
|
||||
return tfMPTokenIssuanceDestroyMask;
|
||||
}
|
||||
|
||||
NotTEC
|
||||
MPTokenIssuanceDestroy::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
|
||||
@@ -130,7 +130,7 @@ MPTokenIssuanceSet::checkPermission(ReadView const& view, STTx const& tx)
|
||||
|
||||
// this is added in case more flags will be added for MPTokenIssuanceSet
|
||||
// in the future. Currently unreachable.
|
||||
if (txFlags & tfMPTokenIssuanceSetMask)
|
||||
if (txFlags & tfMPTokenIssuanceSetPermissionMask)
|
||||
return terNO_DELEGATE_PERMISSION; // LCOV_EXCL_LINE
|
||||
|
||||
std::unordered_set<GranularPermissionType> granularPermissions;
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
std::uint32_t
|
||||
NFTokenAcceptOffer::getFlagsMask(PreflightContext const& ctx)
|
||||
{
|
||||
return tfNFTokenAcceptOfferMask;
|
||||
}
|
||||
|
||||
NotTEC
|
||||
NFTokenAcceptOffer::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
std::uint32_t
|
||||
NFTokenCancelOffer::getFlagsMask(PreflightContext const& ctx)
|
||||
{
|
||||
return tfNFTokenCancelOfferMask;
|
||||
}
|
||||
|
||||
NotTEC
|
||||
NFTokenCancelOffer::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
|
||||
@@ -48,8 +48,9 @@ NFTokenMint::getFlagsMask(PreflightContext const& ctx)
|
||||
// tfTrustLine flag as a way to prevent the attack. But until the
|
||||
// amendment passes we still need to keep the old behavior available.
|
||||
std::uint32_t const nfTokenMintMask = ctx.rules.enabled(fixRemoveNFTokenAutoTrustLine)
|
||||
// if featureDynamicNFT enabled then new flag allowing mutable URI available
|
||||
? ctx.rules.enabled(featureDynamicNFT) ? tfNFTokenMintMask : tfNFTokenMintMaskWithoutMutable
|
||||
// if featureDynamicNFT enabled then new flag allowing mutable URI
|
||||
// available
|
||||
? ctx.rules.enabled(featureDynamicNFT) ? tfNFTokenMintMaskWithMutable : tfNFTokenMintMask
|
||||
: ctx.rules.enabled(featureDynamicNFT) ? tfNFTokenMintOldMaskWithMutable
|
||||
: tfNFTokenMintOldMask;
|
||||
|
||||
|
||||
@@ -378,7 +378,7 @@ PayChanClaim::checkExtraFeatures(PreflightContext const& ctx)
|
||||
std::uint32_t
|
||||
PayChanClaim::getFlagsMask(PreflightContext const&)
|
||||
{
|
||||
return tfPaymentChannelClaimMask;
|
||||
return tfPayChanClaimMask;
|
||||
}
|
||||
|
||||
NotTEC
|
||||
|
||||
@@ -1448,7 +1448,7 @@ XChainCreateBridge::doApply()
|
||||
std::uint32_t
|
||||
BridgeModify::getFlagsMask(PreflightContext const& ctx)
|
||||
{
|
||||
return tfXChainModifyBridgeMask;
|
||||
return tfBridgeModifyMask;
|
||||
}
|
||||
|
||||
NotTEC
|
||||
|
||||
@@ -4355,7 +4355,6 @@ public:
|
||||
run() override
|
||||
{
|
||||
using namespace test::jtx;
|
||||
|
||||
auto const sa = testable_amendments();
|
||||
testWithFeats(sa - fixBatchInnerSigs);
|
||||
testWithFeats(sa);
|
||||
|
||||
@@ -32,9 +32,10 @@ public:
|
||||
test_limits()
|
||||
{
|
||||
auto const scale = Number::getMantissaScale();
|
||||
testcase << "test_limits " << to_string(scale);
|
||||
bool caught = false;
|
||||
auto const minMantissa = Number::minMantissa();
|
||||
|
||||
testcase << "test_limits " << to_string(scale) << ", " << minMantissa;
|
||||
bool caught = false;
|
||||
try
|
||||
{
|
||||
Number x = Number{false, minMantissa * 10, 32768, Number::normalized{}};
|
||||
@@ -58,8 +59,9 @@ public:
|
||||
__LINE__);
|
||||
test(Number{false, minMantissa, -32769, Number::normalized{}}, Number{}, __LINE__);
|
||||
test(
|
||||
// Use 1501 to force rounding up
|
||||
Number{false, minMantissa, 32000, Number::normalized{}} * 1'000 +
|
||||
Number{false, 1'500, 32000, Number::normalized{}},
|
||||
Number{false, 1'501, 32000, Number::normalized{}},
|
||||
Number{false, minMantissa + 2, 32003, Number::normalized{}},
|
||||
__LINE__);
|
||||
// 9,223,372,036,854,775,808
|
||||
@@ -168,8 +170,12 @@ public:
|
||||
{Number{true, 9'999'999'999'999'999'999ULL, -37, Number::normalized{}},
|
||||
Number{1'000'000'000'000'000'000, -18},
|
||||
Number{false, 9'999'999'999'999'999'990ULL, -19, Number::normalized{}}},
|
||||
{Number{Number::maxRep}, Number{6, -1}, Number{Number::maxRep / 10, 1}},
|
||||
{Number{Number::maxRep - 1}, Number{1, 0}, Number{Number::maxRep}},
|
||||
{Number{Number::largestMantissa},
|
||||
Number{6, -1},
|
||||
Number{Number::largestMantissa / 10, 1}},
|
||||
{Number{Number::largestMantissa - 1},
|
||||
Number{1, 0},
|
||||
Number{Number::largestMantissa}},
|
||||
// Test extremes
|
||||
{
|
||||
// Each Number operand rounds up, so the actual mantissa is
|
||||
@@ -179,11 +185,18 @@ public:
|
||||
Number{2, 19},
|
||||
},
|
||||
{
|
||||
// Does not round. Mantissas are going to be > maxRep, so if
|
||||
// added together as uint64_t's, the result will overflow.
|
||||
// With addition using uint128_t, there's no problem. After
|
||||
// normalizing, the resulting mantissa ends up less than
|
||||
// maxRep.
|
||||
// Does not round. Mantissas are going to be >
|
||||
// largestMantissa, so if added together as uint64_t's, the
|
||||
// result will overflow. With addition using uint128_t,
|
||||
// there's no problem. After normalizing, the resulting
|
||||
// mantissa ends up less than largestMantissa.
|
||||
Number{false, Number::largestMantissa, 0, Number::normalized{}},
|
||||
Number{false, Number::largestMantissa, 0, Number::normalized{}},
|
||||
Number{false, Number::largestMantissa * 2, 0, Number::normalized{}},
|
||||
},
|
||||
{
|
||||
// These mantissas round down, so adding them together won't
|
||||
// have any consequences.
|
||||
Number{false, 9'999'999'999'999'999'990ULL, 0, Number::normalized{}},
|
||||
Number{false, 9'999'999'999'999'999'990ULL, 0, Number::normalized{}},
|
||||
Number{false, 1'999'999'999'999'999'998ULL, 1, Number::normalized{}},
|
||||
@@ -272,14 +285,16 @@ public:
|
||||
{Number{1'000'000'000'000'000'001, -18},
|
||||
Number{1'000'000'000'000'000'000, -18},
|
||||
Number{1'000'000'000'000'000'000, -36}},
|
||||
{Number{Number::maxRep}, Number{6, -1}, Number{Number::maxRep - 1}},
|
||||
{Number{false, Number::maxRep + 1, 0, Number::normalized{}},
|
||||
{Number{Number::largestMantissa},
|
||||
Number{6, -1},
|
||||
Number{Number::largestMantissa - 1}},
|
||||
{Number{false, Number::largestMantissa + 1, 0, Number::normalized{}},
|
||||
Number{1, 0},
|
||||
Number{Number::maxRep / 10 + 1, 1}},
|
||||
{Number{false, Number::maxRep + 1, 0, Number::normalized{}},
|
||||
Number{Number::largestMantissa / 10 + 1, 1}},
|
||||
{Number{false, Number::largestMantissa + 1, 0, Number::normalized{}},
|
||||
Number{3, 0},
|
||||
Number{Number::maxRep}},
|
||||
{power(2, 63), Number{3, 0}, Number{Number::maxRep}},
|
||||
Number{Number::largestMantissa}},
|
||||
{power(2, 63), Number{3, 0}, Number{Number::largestMantissa}},
|
||||
});
|
||||
auto test = [this](auto const& c) {
|
||||
for (auto const& [x, y, z] : c)
|
||||
@@ -296,20 +311,30 @@ public:
|
||||
test(cLarge);
|
||||
}
|
||||
|
||||
static std::uint64_t
|
||||
getMaxInternalMantissa()
|
||||
{
|
||||
return static_cast<std::uint64_t>(
|
||||
static_cast<std::int64_t>(power(10, Number::mantissaLog()))) *
|
||||
10 -
|
||||
1;
|
||||
}
|
||||
|
||||
void
|
||||
test_mul()
|
||||
{
|
||||
auto const scale = Number::getMantissaScale();
|
||||
testcase << "test_mul " << to_string(scale);
|
||||
|
||||
using Case = std::tuple<Number, Number, Number>;
|
||||
// Case: Factor 1, Factor 2, Expected product, Line number
|
||||
using Case = std::tuple<Number, Number, Number, int>;
|
||||
auto test = [this](auto const& c) {
|
||||
for (auto const& [x, y, z] : c)
|
||||
for (auto const& [x, y, z, line] : c)
|
||||
{
|
||||
auto const result = x * y;
|
||||
std::stringstream ss;
|
||||
ss << x << " * " << y << " = " << result << ". Expected: " << z;
|
||||
BEAST_EXPECTS(result == z, ss.str());
|
||||
BEAST_EXPECTS(result == z, ss.str() + " line: " + std::to_string(line));
|
||||
}
|
||||
};
|
||||
auto tests = [&](auto const& cSmall, auto const& cLarge) {
|
||||
@@ -319,70 +344,97 @@ public:
|
||||
test(cLarge);
|
||||
};
|
||||
auto const maxMantissa = Number::maxMantissa();
|
||||
auto const maxInternalMantissa = getMaxInternalMantissa();
|
||||
|
||||
saveNumberRoundMode save{Number::setround(Number::to_nearest)};
|
||||
{
|
||||
auto const cSmall = std::to_array<Case>({
|
||||
{Number{7}, Number{8}, Number{56}},
|
||||
{Number{7}, Number{8}, Number{56}, __LINE__},
|
||||
{Number{1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{2000000000000000, -15}},
|
||||
Number{2000000000000000, -15},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{-2000000000000000, -15}},
|
||||
Number{-2000000000000000, -15},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{-1414213562373095, -15},
|
||||
Number{2000000000000000, -15}},
|
||||
Number{2000000000000000, -15},
|
||||
__LINE__},
|
||||
{Number{3214285714285706, -15},
|
||||
Number{3111111111111119, -15},
|
||||
Number{1000000000000000, -14}},
|
||||
{Number{1000000000000000, -32768}, Number{1000000000000000, -32768}, Number{0}},
|
||||
Number{1000000000000000, -14},
|
||||
__LINE__},
|
||||
{Number{1000000000000000, -32768},
|
||||
Number{1000000000000000, -32768},
|
||||
Number{0},
|
||||
__LINE__},
|
||||
// Maximum mantissa range
|
||||
{Number{9'999'999'999'999'999, 0},
|
||||
Number{9'999'999'999'999'999, 0},
|
||||
Number{9'999'999'999'999'998, 16}},
|
||||
Number{9'999'999'999'999'998, 16},
|
||||
__LINE__},
|
||||
});
|
||||
auto const cLarge = std::to_array<Case>({
|
||||
// Note that items with extremely large mantissas need to be
|
||||
// calculated, because otherwise they overflow uint64. Items
|
||||
// from C with larger mantissa
|
||||
{Number{7}, Number{8}, Number{56}},
|
||||
{Number{7}, Number{8}, Number{56}, __LINE__},
|
||||
{Number{1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{1999999999999999862, -18}},
|
||||
Number{1999999999999999862, -18},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{-1999999999999999862, -18}},
|
||||
Number{-1999999999999999862, -18},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{-1414213562373095, -15},
|
||||
Number{1999999999999999862, -18}},
|
||||
Number{1999999999999999862, -18},
|
||||
__LINE__},
|
||||
{Number{3214285714285706, -15},
|
||||
Number{3111111111111119, -15},
|
||||
Number{false, 9'999'999'999'999'999'579ULL, -18, Number::normalized{}}},
|
||||
Number{false, 9'999'999'999'999'999'579ULL, -18, Number::normalized{}},
|
||||
__LINE__},
|
||||
{Number{1000000000000000000, -32768},
|
||||
Number{1000000000000000000, -32768},
|
||||
Number{0}},
|
||||
Number{0},
|
||||
__LINE__},
|
||||
// Items from cSmall expanded for the larger mantissa,
|
||||
// except duplicates. Sadly, it looks like sqrt(2)^2 != 2
|
||||
// with higher precision
|
||||
{Number{1414213562373095049, -18},
|
||||
Number{1414213562373095049, -18},
|
||||
Number{2000000000000000001, -18}},
|
||||
Number{2000000000000000001, -18},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095048, -18},
|
||||
Number{1414213562373095048, -18},
|
||||
Number{-1999999999999999998, -18}},
|
||||
Number{-1999999999999999998, -18},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095048, -18},
|
||||
Number{-1414213562373095049, -18},
|
||||
Number{1999999999999999999, -18}},
|
||||
{Number{3214285714285714278, -18}, Number{3111111111111111119, -18}, Number{10, 0}},
|
||||
// Maximum mantissa range - rounds up to 1e19
|
||||
Number{1999999999999999999, -18},
|
||||
__LINE__},
|
||||
{Number{3214285714285714278, -18},
|
||||
Number{3111111111111111119, -18},
|
||||
Number{10, 0},
|
||||
__LINE__},
|
||||
// Maximum internal mantissa range - rounds up to 1e19
|
||||
{Number{false, maxInternalMantissa, 0, Number::normalized{}},
|
||||
Number{false, maxInternalMantissa, 0, Number::normalized{}},
|
||||
Number{1, 38},
|
||||
__LINE__},
|
||||
// Maximum actual mantissa range - same as int64 range
|
||||
{Number{false, maxMantissa, 0, Number::normalized{}},
|
||||
Number{false, maxMantissa, 0, Number::normalized{}},
|
||||
Number{1, 38}},
|
||||
Number{85'070'591'730'234'615'85, 19},
|
||||
__LINE__},
|
||||
// Maximum int64 range
|
||||
{Number{Number::maxRep, 0},
|
||||
Number{Number::maxRep, 0},
|
||||
Number{85'070'591'730'234'615'85, 19}},
|
||||
{Number{Number::largestMantissa, 0},
|
||||
Number{Number::largestMantissa, 0},
|
||||
Number{85'070'591'730'234'615'85, 19},
|
||||
__LINE__},
|
||||
});
|
||||
tests(cSmall, cLarge);
|
||||
}
|
||||
@@ -390,66 +442,90 @@ public:
|
||||
testcase << "test_mul " << to_string(Number::getMantissaScale()) << " towards_zero";
|
||||
{
|
||||
auto const cSmall = std::to_array<Case>(
|
||||
{{Number{7}, Number{8}, Number{56}},
|
||||
{{Number{7}, Number{8}, Number{56}, __LINE__},
|
||||
{Number{1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{1999999999999999, -15}},
|
||||
Number{1999999999999999, -15},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{-1999999999999999, -15}},
|
||||
Number{-1999999999999999, -15},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{-1414213562373095, -15},
|
||||
Number{1999999999999999, -15}},
|
||||
Number{1999999999999999, -15},
|
||||
__LINE__},
|
||||
{Number{3214285714285706, -15},
|
||||
Number{3111111111111119, -15},
|
||||
Number{9999999999999999, -15}},
|
||||
{Number{1000000000000000, -32768}, Number{1000000000000000, -32768}, Number{0}}});
|
||||
Number{9999999999999999, -15},
|
||||
__LINE__},
|
||||
{Number{1000000000000000, -32768},
|
||||
Number{1000000000000000, -32768},
|
||||
Number{0},
|
||||
__LINE__}});
|
||||
auto const cLarge = std::to_array<Case>(
|
||||
// Note that items with extremely large mantissas need to be
|
||||
// calculated, because otherwise they overflow uint64. Items
|
||||
// from C with larger mantissa
|
||||
{
|
||||
{Number{7}, Number{8}, Number{56}},
|
||||
{Number{7}, Number{8}, Number{56}, __LINE__},
|
||||
{Number{1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{1999999999999999861, -18}},
|
||||
Number{1999999999999999861, -18},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{-1999999999999999861, -18}},
|
||||
Number{-1999999999999999861, -18},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{-1414213562373095, -15},
|
||||
Number{1999999999999999861, -18}},
|
||||
Number{1999999999999999861, -18},
|
||||
__LINE__},
|
||||
{Number{3214285714285706, -15},
|
||||
Number{3111111111111119, -15},
|
||||
Number{false, 9999999999999999579ULL, -18, Number::normalized{}}},
|
||||
Number{false, 9999999999999999579ULL, -18, Number::normalized{}},
|
||||
__LINE__},
|
||||
{Number{1000000000000000000, -32768},
|
||||
Number{1000000000000000000, -32768},
|
||||
Number{0}},
|
||||
Number{0},
|
||||
__LINE__},
|
||||
// Items from cSmall expanded for the larger mantissa,
|
||||
// except duplicates. Sadly, it looks like sqrt(2)^2 != 2
|
||||
// with higher precision
|
||||
{Number{1414213562373095049, -18},
|
||||
Number{1414213562373095049, -18},
|
||||
Number{2, 0}},
|
||||
Number{2, 0},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095048, -18},
|
||||
Number{1414213562373095048, -18},
|
||||
Number{-1999999999999999997, -18}},
|
||||
Number{-1999999999999999997, -18},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095048, -18},
|
||||
Number{-1414213562373095049, -18},
|
||||
Number{1999999999999999999, -18}},
|
||||
Number{1999999999999999999, -18},
|
||||
__LINE__},
|
||||
{Number{3214285714285714278, -18},
|
||||
Number{3111111111111111119, -18},
|
||||
Number{10, 0}},
|
||||
// Maximum mantissa range - rounds down to maxMantissa/10e1
|
||||
Number{10, 0},
|
||||
__LINE__},
|
||||
// Maximum internal mantissa range - rounds down to
|
||||
// maxMantissa/10e1
|
||||
// 99'999'999'999'999'999'800'000'000'000'000'000'100
|
||||
{Number{false, maxInternalMantissa, 0, Number::normalized{}},
|
||||
Number{false, maxInternalMantissa, 0, Number::normalized{}},
|
||||
Number{false, maxInternalMantissa / 10 - 1, 20, Number::normalized{}},
|
||||
__LINE__},
|
||||
// Maximum actual mantissa range - same as int64
|
||||
{Number{false, maxMantissa, 0, Number::normalized{}},
|
||||
Number{false, maxMantissa, 0, Number::normalized{}},
|
||||
Number{false, maxMantissa / 10 - 1, 20, Number::normalized{}}},
|
||||
Number{85'070'591'730'234'615'84, 19},
|
||||
__LINE__},
|
||||
// Maximum int64 range
|
||||
// 85'070'591'730'234'615'847'396'907'784'232'501'249
|
||||
{Number{Number::maxRep, 0},
|
||||
Number{Number::maxRep, 0},
|
||||
Number{85'070'591'730'234'615'84, 19}},
|
||||
{Number{Number::largestMantissa, 0},
|
||||
Number{Number::largestMantissa, 0},
|
||||
Number{85'070'591'730'234'615'84, 19},
|
||||
__LINE__},
|
||||
});
|
||||
tests(cSmall, cLarge);
|
||||
}
|
||||
@@ -457,66 +533,90 @@ public:
|
||||
testcase << "test_mul " << to_string(Number::getMantissaScale()) << " downward";
|
||||
{
|
||||
auto const cSmall = std::to_array<Case>(
|
||||
{{Number{7}, Number{8}, Number{56}},
|
||||
{{Number{7}, Number{8}, Number{56}, __LINE__},
|
||||
{Number{1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{1999999999999999, -15}},
|
||||
Number{1999999999999999, -15},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{-2000000000000000, -15}},
|
||||
Number{-2000000000000000, -15},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{-1414213562373095, -15},
|
||||
Number{1999999999999999, -15}},
|
||||
Number{1999999999999999, -15},
|
||||
__LINE__},
|
||||
{Number{3214285714285706, -15},
|
||||
Number{3111111111111119, -15},
|
||||
Number{9999999999999999, -15}},
|
||||
{Number{1000000000000000, -32768}, Number{1000000000000000, -32768}, Number{0}}});
|
||||
Number{9999999999999999, -15},
|
||||
__LINE__},
|
||||
{Number{1000000000000000, -32768},
|
||||
Number{1000000000000000, -32768},
|
||||
Number{0},
|
||||
__LINE__}});
|
||||
auto const cLarge = std::to_array<Case>(
|
||||
// Note that items with extremely large mantissas need to be
|
||||
// calculated, because otherwise they overflow uint64. Items
|
||||
// from C with larger mantissa
|
||||
{
|
||||
{Number{7}, Number{8}, Number{56}},
|
||||
{Number{7}, Number{8}, Number{56}, __LINE__},
|
||||
{Number{1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{1999999999999999861, -18}},
|
||||
Number{1999999999999999861, -18},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{-1999999999999999862, -18}},
|
||||
Number{-1999999999999999862, -18},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{-1414213562373095, -15},
|
||||
Number{1999999999999999861, -18}},
|
||||
Number{1999999999999999861, -18},
|
||||
__LINE__},
|
||||
{Number{3214285714285706, -15},
|
||||
Number{3111111111111119, -15},
|
||||
Number{false, 9'999'999'999'999'999'579ULL, -18, Number::normalized{}}},
|
||||
Number{false, 9'999'999'999'999'999'579ULL, -18, Number::normalized{}},
|
||||
__LINE__},
|
||||
{Number{1000000000000000000, -32768},
|
||||
Number{1000000000000000000, -32768},
|
||||
Number{0}},
|
||||
Number{0},
|
||||
__LINE__},
|
||||
// Items from cSmall expanded for the larger mantissa,
|
||||
// except duplicates. Sadly, it looks like sqrt(2)^2 != 2
|
||||
// with higher precision
|
||||
{Number{1414213562373095049, -18},
|
||||
Number{1414213562373095049, -18},
|
||||
Number{2, 0}},
|
||||
Number{2, 0},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095048, -18},
|
||||
Number{1414213562373095048, -18},
|
||||
Number{-1999999999999999998, -18}},
|
||||
Number{-1999999999999999998, -18},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095048, -18},
|
||||
Number{-1414213562373095049, -18},
|
||||
Number{1999999999999999999, -18}},
|
||||
Number{1999999999999999999, -18},
|
||||
__LINE__},
|
||||
{Number{3214285714285714278, -18},
|
||||
Number{3111111111111111119, -18},
|
||||
Number{10, 0}},
|
||||
// Maximum mantissa range - rounds down to maxMantissa/10e1
|
||||
Number{10, 0},
|
||||
__LINE__},
|
||||
// Maximum internal mantissa range - rounds down to
|
||||
// maxInternalMantissa/10-1
|
||||
// 99'999'999'999'999'999'800'000'000'000'000'000'100
|
||||
{Number{false, maxInternalMantissa, 0, Number::normalized{}},
|
||||
Number{false, maxInternalMantissa, 0, Number::normalized{}},
|
||||
Number{false, maxInternalMantissa / 10 - 1, 20, Number::normalized{}},
|
||||
__LINE__},
|
||||
// Maximum external mantissa range - same as INT64_MAX (2^63-1)
|
||||
{Number{false, maxMantissa, 0, Number::normalized{}},
|
||||
Number{false, maxMantissa, 0, Number::normalized{}},
|
||||
Number{false, maxMantissa / 10 - 1, 20, Number::normalized{}}},
|
||||
Number{85'070'591'730'234'615'84, 19},
|
||||
__LINE__},
|
||||
// Maximum int64 range
|
||||
// 85'070'591'730'234'615'847'396'907'784'232'501'249
|
||||
{Number{Number::maxRep, 0},
|
||||
Number{Number::maxRep, 0},
|
||||
Number{85'070'591'730'234'615'84, 19}},
|
||||
{Number{Number::largestMantissa, 0},
|
||||
Number{Number::largestMantissa, 0},
|
||||
Number{85'070'591'730'234'615'84, 19},
|
||||
__LINE__},
|
||||
});
|
||||
tests(cSmall, cLarge);
|
||||
}
|
||||
@@ -524,66 +624,89 @@ public:
|
||||
testcase << "test_mul " << to_string(Number::getMantissaScale()) << " upward";
|
||||
{
|
||||
auto const cSmall = std::to_array<Case>(
|
||||
{{Number{7}, Number{8}, Number{56}},
|
||||
{{Number{7}, Number{8}, Number{56}, __LINE__},
|
||||
{Number{1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{2000000000000000, -15}},
|
||||
Number{2000000000000000, -15},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{-1999999999999999, -15}},
|
||||
Number{-1999999999999999, -15},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{-1414213562373095, -15},
|
||||
Number{2000000000000000, -15}},
|
||||
Number{2000000000000000, -15},
|
||||
__LINE__},
|
||||
{Number{3214285714285706, -15},
|
||||
Number{3111111111111119, -15},
|
||||
Number{1000000000000000, -14}},
|
||||
{Number{1000000000000000, -32768}, Number{1000000000000000, -32768}, Number{0}}});
|
||||
Number{1000000000000000, -14},
|
||||
__LINE__},
|
||||
{Number{1000000000000000, -32768},
|
||||
Number{1000000000000000, -32768},
|
||||
Number{0},
|
||||
__LINE__}});
|
||||
auto const cLarge = std::to_array<Case>(
|
||||
// Note that items with extremely large mantissas need to be
|
||||
// calculated, because otherwise they overflow uint64. Items
|
||||
// from C with larger mantissa
|
||||
{
|
||||
{Number{7}, Number{8}, Number{56}},
|
||||
{Number{7}, Number{8}, Number{56}, __LINE__},
|
||||
{Number{1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{1999999999999999862, -18}},
|
||||
Number{1999999999999999862, -18},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{1414213562373095, -15},
|
||||
Number{-1999999999999999861, -18}},
|
||||
Number{-1999999999999999861, -18},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095, -15},
|
||||
Number{-1414213562373095, -15},
|
||||
Number{1999999999999999862, -18}},
|
||||
Number{1999999999999999862, -18},
|
||||
__LINE__},
|
||||
{Number{3214285714285706, -15},
|
||||
Number{3111111111111119, -15},
|
||||
Number{999999999999999958, -17}},
|
||||
Number{999999999999999958, -17},
|
||||
__LINE__},
|
||||
{Number{1000000000000000000, -32768},
|
||||
Number{1000000000000000000, -32768},
|
||||
Number{0}},
|
||||
Number{0},
|
||||
__LINE__},
|
||||
// Items from cSmall expanded for the larger mantissa,
|
||||
// except duplicates. Sadly, it looks like sqrt(2)^2 != 2
|
||||
// with higher precision
|
||||
{Number{1414213562373095049, -18},
|
||||
Number{1414213562373095049, -18},
|
||||
Number{2000000000000000001, -18}},
|
||||
Number{2000000000000000001, -18},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095048, -18},
|
||||
Number{1414213562373095048, -18},
|
||||
Number{-1999999999999999997, -18}},
|
||||
Number{-1999999999999999997, -18},
|
||||
__LINE__},
|
||||
{Number{-1414213562373095048, -18},
|
||||
Number{-1414213562373095049, -18},
|
||||
Number{2, 0}},
|
||||
Number{2, 0},
|
||||
__LINE__},
|
||||
{Number{3214285714285714278, -18},
|
||||
Number{3111111111111111119, -18},
|
||||
Number{1000000000000000001, -17}},
|
||||
// Maximum mantissa range - rounds up to minMantissa*10
|
||||
// 1e19*1e19=1e38
|
||||
Number{1000000000000000001, -17},
|
||||
__LINE__},
|
||||
// Maximum internal mantissa range - rounds up to
|
||||
// minMantissa*10 1e19*1e19=1e38
|
||||
{Number{false, maxInternalMantissa, 0, Number::normalized{}},
|
||||
Number{false, maxInternalMantissa, 0, Number::normalized{}},
|
||||
Number{1, 38},
|
||||
__LINE__},
|
||||
// Maximum mantissa range - same as int64
|
||||
{Number{false, maxMantissa, 0, Number::normalized{}},
|
||||
Number{false, maxMantissa, 0, Number::normalized{}},
|
||||
Number{1, 38}},
|
||||
Number{85'070'591'730'234'615'85, 19},
|
||||
__LINE__},
|
||||
// Maximum int64 range
|
||||
// 85'070'591'730'234'615'847'396'907'784'232'501'249
|
||||
{Number{Number::maxRep, 0},
|
||||
Number{Number::maxRep, 0},
|
||||
Number{85'070'591'730'234'615'85, 19}},
|
||||
{Number{Number::largestMantissa, 0},
|
||||
Number{Number::largestMantissa, 0},
|
||||
Number{85'070'591'730'234'615'85, 19},
|
||||
__LINE__},
|
||||
});
|
||||
tests(cSmall, cLarge);
|
||||
}
|
||||
@@ -814,6 +937,8 @@ public:
|
||||
};
|
||||
*/
|
||||
|
||||
auto const maxInternalMantissa = getMaxInternalMantissa();
|
||||
|
||||
auto const cSmall = std::to_array<Case>(
|
||||
{{Number{2}, 2, Number{1414213562373095049, -18}},
|
||||
{Number{2'000'000}, 2, Number{1414213562373095049, -15}},
|
||||
@@ -825,16 +950,16 @@ public:
|
||||
{Number{0}, 5, Number{0}},
|
||||
{Number{5625, -4}, 2, Number{75, -2}}});
|
||||
auto const cLarge = std::to_array<Case>({
|
||||
{Number{false, Number::maxMantissa() - 9, -1, Number::normalized{}},
|
||||
{Number{false, maxInternalMantissa - 9, -1, Number::normalized{}},
|
||||
2,
|
||||
Number{false, 999'999'999'999'999'999, -9, Number::normalized{}}},
|
||||
{Number{false, Number::maxMantissa() - 9, 0, Number::normalized{}},
|
||||
{Number{false, maxInternalMantissa - 9, 0, Number::normalized{}},
|
||||
2,
|
||||
Number{false, 3'162'277'660'168'379'330, -9, Number::normalized{}}},
|
||||
{Number{Number::maxRep},
|
||||
{Number{Number::largestMantissa},
|
||||
2,
|
||||
Number{false, 3'037'000'499'976049692, -9, Number::normalized{}}},
|
||||
{Number{Number::maxRep},
|
||||
{Number{Number::largestMantissa},
|
||||
4,
|
||||
Number{false, 55'108'98747006743627, -14, Number::normalized{}}},
|
||||
});
|
||||
@@ -883,6 +1008,8 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
Number const maxInternalMantissa = power(10, Number::mantissaLog()) * 10 - 1;
|
||||
|
||||
auto const cSmall = std::to_array<Number>({
|
||||
Number{2},
|
||||
Number{2'000'000},
|
||||
@@ -892,7 +1019,10 @@ public:
|
||||
Number{5, -1},
|
||||
Number{0},
|
||||
Number{5625, -4},
|
||||
Number{Number::maxRep},
|
||||
Number{Number::largestMantissa},
|
||||
maxInternalMantissa,
|
||||
Number{Number::minMantissa(), 0, Number::unchecked{}},
|
||||
Number{Number::maxMantissa(), 0, Number::unchecked{}},
|
||||
});
|
||||
test(cSmall);
|
||||
bool caught = false;
|
||||
@@ -1243,18 +1373,18 @@ public:
|
||||
case MantissaRange::large:
|
||||
// Test the edges
|
||||
// ((exponent < -(28)) || (exponent > -(8)))))
|
||||
test(Number::min(), "1e-32750");
|
||||
test(Number::min(), "922337203685477581e-32768");
|
||||
test(Number::max(), "9223372036854775807e32768");
|
||||
test(Number::lowest(), "-9223372036854775807e32768");
|
||||
{
|
||||
NumberRoundModeGuard mg(Number::towards_zero);
|
||||
|
||||
auto const maxMantissa = Number::maxMantissa();
|
||||
BEAST_EXPECT(maxMantissa == 9'999'999'999'999'999'999ULL);
|
||||
BEAST_EXPECT(maxMantissa == 9'223'372'036'854'775'807ULL);
|
||||
test(
|
||||
Number{false, maxMantissa, 0, Number::normalized{}}, "9999999999999999990");
|
||||
Number{false, maxMantissa, 0, Number::normalized{}}, "9223372036854775807");
|
||||
test(
|
||||
Number{true, maxMantissa, 0, Number::normalized{}}, "-9999999999999999990");
|
||||
Number{true, maxMantissa, 0, Number::normalized{}}, "-9223372036854775807");
|
||||
|
||||
test(
|
||||
Number{std::numeric_limits<std::int64_t>::max(), 0}, "9223372036854775807");
|
||||
@@ -1490,7 +1620,7 @@ public:
|
||||
Number const initalXrp{INITIAL_XRP};
|
||||
BEAST_EXPECT(initalXrp.exponent() > 0);
|
||||
|
||||
Number const maxInt64{Number::maxRep};
|
||||
Number const maxInt64{Number::largestMantissa};
|
||||
BEAST_EXPECT(maxInt64.exponent() > 0);
|
||||
// 85'070'591'730'234'615'865'843'651'857'942'052'864 - 38 digits
|
||||
BEAST_EXPECT((power(maxInt64, 2) == Number{85'070'591'730'234'62, 22}));
|
||||
@@ -1507,21 +1637,213 @@ public:
|
||||
Number const initalXrp{INITIAL_XRP};
|
||||
BEAST_EXPECT(initalXrp.exponent() <= 0);
|
||||
|
||||
Number const maxInt64{Number::maxRep};
|
||||
Number const maxInt64{Number::largestMantissa};
|
||||
BEAST_EXPECT(maxInt64.exponent() <= 0);
|
||||
// 85'070'591'730'234'615'847'396'907'784'232'501'249 - 38 digits
|
||||
BEAST_EXPECT((power(maxInt64, 2) == Number{85'070'591'730'234'615'85, 19}));
|
||||
|
||||
NumberRoundModeGuard mg(Number::towards_zero);
|
||||
|
||||
auto const maxMantissa = Number::maxMantissa();
|
||||
Number const max = Number{false, maxMantissa, 0, Number::normalized{}};
|
||||
BEAST_EXPECT(max.mantissa() == maxMantissa / 10);
|
||||
BEAST_EXPECT(max.exponent() == 1);
|
||||
// 99'999'999'999'999'999'800'000'000'000'000'000'100 - also 38
|
||||
// digits
|
||||
BEAST_EXPECT(
|
||||
(power(max, 2) == Number{false, maxMantissa / 10 - 1, 20, Number::normalized{}}));
|
||||
{
|
||||
auto const maxInternalMantissa = getMaxInternalMantissa();
|
||||
|
||||
// Rounds down to fit under 2^63
|
||||
Number const max = Number{false, maxInternalMantissa, 0, Number::normalized{}};
|
||||
// No alterations by the accessors
|
||||
BEAST_EXPECT(max.mantissa() == maxInternalMantissa / 10);
|
||||
BEAST_EXPECT(max.exponent() == 1);
|
||||
// 99'999'999'999'999'999'800'000'000'000'000'000'100 - also 38
|
||||
// digits
|
||||
BEAST_EXPECT(
|
||||
(power(max, 2) ==
|
||||
Number{false, maxInternalMantissa / 10 - 1, 20, Number::normalized{}}));
|
||||
}
|
||||
|
||||
{
|
||||
auto const maxMantissa = Number::maxMantissa();
|
||||
Number const max = Number{false, maxMantissa, 0, Number::normalized{}};
|
||||
// No alterations by the accessors
|
||||
BEAST_EXPECT(max.mantissa() == maxMantissa);
|
||||
BEAST_EXPECT(max.exponent() == 0);
|
||||
// 85'070'591'730'234'615'847'396'907'784'232'501'249 - also 38
|
||||
// digits
|
||||
BEAST_EXPECT(
|
||||
(power(max, 2) ==
|
||||
Number{false, 85'070'591'730'234'615'84, 19, Number::normalized{}}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testNormalizeToRange()
|
||||
{
|
||||
// Test edge-cases of normalizeToRange
|
||||
auto const scale = Number::getMantissaScale();
|
||||
testcase << "normalizeToRange " << to_string(scale);
|
||||
|
||||
auto test = [this](
|
||||
Number const& n,
|
||||
auto const rangeMin,
|
||||
auto const rangeMax,
|
||||
auto const expectedMantissa,
|
||||
auto const expectedExponent,
|
||||
auto const line) {
|
||||
auto const normalized = n.normalizeToRange(rangeMin, rangeMax);
|
||||
BEAST_EXPECTS(
|
||||
normalized.first == expectedMantissa,
|
||||
"Number " + to_string(n) + " scaled to " + std::to_string(rangeMax) +
|
||||
". Expected mantissa:" + std::to_string(expectedMantissa) +
|
||||
", got: " + std::to_string(normalized.first) + " @ " + std::to_string(line));
|
||||
BEAST_EXPECTS(
|
||||
normalized.second == expectedExponent,
|
||||
"Number " + to_string(n) + " scaled to " + std::to_string(rangeMax) +
|
||||
". Expected exponent:" + std::to_string(expectedExponent) +
|
||||
", got: " + std::to_string(normalized.second) + " @ " + std::to_string(line));
|
||||
};
|
||||
|
||||
std::int64_t constexpr iRangeMin = 100;
|
||||
std::int64_t constexpr iRangeMax = 999;
|
||||
|
||||
std::uint64_t constexpr uRangeMin = 100;
|
||||
std::uint64_t constexpr uRangeMax = 999;
|
||||
|
||||
constexpr static MantissaRange largeRange{MantissaRange::large};
|
||||
|
||||
std::int64_t constexpr iBigMin = largeRange.min;
|
||||
std::int64_t constexpr iBigMax = largeRange.max;
|
||||
|
||||
auto const testSuite = [&](Number const& n,
|
||||
auto const expectedSmallMantissa,
|
||||
auto const expectedSmallExponent,
|
||||
auto const expectedLargeMantissa,
|
||||
auto const expectedLargeExponent,
|
||||
auto const line) {
|
||||
test(n, iRangeMin, iRangeMax, expectedSmallMantissa, expectedSmallExponent, line);
|
||||
test(n, iBigMin, iBigMax, expectedLargeMantissa, expectedLargeExponent, line);
|
||||
|
||||
// Only test non-negative. testing a negative number with an
|
||||
// unsigned range will assert, and asserts can't be tested.
|
||||
if (n.signum() >= 0)
|
||||
{
|
||||
test(n, uRangeMin, uRangeMax, expectedSmallMantissa, expectedSmallExponent, line);
|
||||
test(
|
||||
n,
|
||||
largeRange.min,
|
||||
largeRange.max,
|
||||
expectedLargeMantissa,
|
||||
expectedLargeExponent,
|
||||
line);
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
// zero
|
||||
Number const n{0};
|
||||
|
||||
testSuite(
|
||||
n,
|
||||
0,
|
||||
std::numeric_limits<int>::lowest(),
|
||||
0,
|
||||
std::numeric_limits<int>::lowest(),
|
||||
__LINE__);
|
||||
}
|
||||
{
|
||||
// Small positive number
|
||||
Number const n{2};
|
||||
|
||||
testSuite(n, 200, -2, 2'000'000'000'000'000'000, -18, __LINE__);
|
||||
}
|
||||
{
|
||||
// Negative number
|
||||
Number const n{-2};
|
||||
|
||||
testSuite(n, -200, -2, -2'000'000'000'000'000'000, -18, __LINE__);
|
||||
}
|
||||
{
|
||||
// Biggest valid mantissa
|
||||
Number const n{Number::largestMantissa, 0, Number::normalized{}};
|
||||
|
||||
if (scale == MantissaRange::small)
|
||||
// With the small mantissa range, the value rounds up. Because
|
||||
// it rounds up, when scaling up to the full int64 range, it
|
||||
// can't go over the max, so it is one digit smaller than the
|
||||
// full value.
|
||||
testSuite(n, 922, 16, 922'337'203'685'477'600, 1, __LINE__);
|
||||
else
|
||||
testSuite(n, 922, 16, Number::largestMantissa, 0, __LINE__);
|
||||
}
|
||||
{
|
||||
// Biggest valid mantissa + 1
|
||||
Number const n{Number::largestMantissa + 1, 0, Number::normalized{}};
|
||||
|
||||
if (scale == MantissaRange::small)
|
||||
// With the small mantissa range, the value rounds up. Because
|
||||
// it rounds up, when scaling up to the full int64 range, it
|
||||
// can't go over the max, so it is one digit smaller than the
|
||||
// full value.
|
||||
testSuite(n, 922, 16, 922'337'203'685'477'600, 1, __LINE__);
|
||||
else
|
||||
testSuite(n, 922, 16, 922'337'203'685'477'581, 1, __LINE__);
|
||||
}
|
||||
{
|
||||
// Biggest valid mantissa + 2
|
||||
Number const n{Number::largestMantissa + 2, 0, Number::normalized{}};
|
||||
|
||||
if (scale == MantissaRange::small)
|
||||
// With the small mantissa range, the value rounds up. Because
|
||||
// it rounds up, when scaling up to the full int64 range, it
|
||||
// can't go over the max, so it is one digit smaller than the
|
||||
// full value.
|
||||
testSuite(n, 922, 16, 922'337'203'685'477'600, 1, __LINE__);
|
||||
else
|
||||
testSuite(n, 922, 16, 922'337'203'685'477'581, 1, __LINE__);
|
||||
}
|
||||
{
|
||||
// Biggest valid mantissa + 3
|
||||
Number const n{Number::largestMantissa + 3, 0, Number::normalized{}};
|
||||
|
||||
if (scale == MantissaRange::small)
|
||||
// With the small mantissa range, the value rounds up. Because
|
||||
// it rounds up, when scaling up to the full int64 range, it
|
||||
// can't go over the max, so it is one digit smaller than the
|
||||
// full value.
|
||||
testSuite(n, 922, 16, 922'337'203'685'477'600, 1, __LINE__);
|
||||
else
|
||||
testSuite(n, 922, 16, 922'337'203'685'477'581, 1, __LINE__);
|
||||
}
|
||||
{
|
||||
// int64 min
|
||||
Number const n{std::numeric_limits<std::int64_t>::min(), 0};
|
||||
|
||||
if (scale == MantissaRange::small)
|
||||
testSuite(n, -922, 16, -922'337'203'685'477'600, 1, __LINE__);
|
||||
else
|
||||
testSuite(n, -922, 16, -922'337'203'685'477'581, 1, __LINE__);
|
||||
}
|
||||
{
|
||||
// int64 min + 1
|
||||
Number const n{std::numeric_limits<std::int64_t>::min() + 1, 0};
|
||||
|
||||
if (scale == MantissaRange::small)
|
||||
testSuite(n, -922, 16, -922'337'203'685'477'600, 1, __LINE__);
|
||||
else
|
||||
testSuite(n, -922, 16, -9'223'372'036'854'775'807, 0, __LINE__);
|
||||
}
|
||||
{
|
||||
// int64 min - 1
|
||||
// Need to cast to uint, even though we're dealing with a negative
|
||||
// number to avoid overflow and UB
|
||||
Number const n{
|
||||
true,
|
||||
-static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::min()) + 1,
|
||||
0,
|
||||
Number::normalized{}};
|
||||
|
||||
if (scale == MantissaRange::small)
|
||||
testSuite(n, -922, 16, -922'337'203'685'477'600, 1, __LINE__);
|
||||
else
|
||||
testSuite(n, -922, 16, -922'337'203'685'477'581, 1, __LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1552,6 +1874,7 @@ public:
|
||||
test_truncate();
|
||||
testRounding();
|
||||
testInt64();
|
||||
testNormalizeToRange();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -575,6 +575,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE_PRIO(AccountSet, app, xrpl, 1);
|
||||
BEAST_DEFINE_TESTSUITE_PRIO(AccountSet, rpc, xrpl, 1);
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -1,9 +1,6 @@
|
||||
#include <test/jtx.h>
|
||||
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SOTemplate.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -84,371 +81,43 @@ public:
|
||||
BEAST_EXPECT(types["Hash384"].asUInt() == 22);
|
||||
BEAST_EXPECT(types["Hash512"].asUInt() == 23);
|
||||
}
|
||||
|
||||
// test the properties of the LEDGER_ENTRY_FLAGS section
|
||||
{
|
||||
BEAST_EXPECT(result[jss::result].isMember(jss::LEDGER_ENTRY_FLAGS));
|
||||
Json::Value const& leFlags = result[jss::result][jss::LEDGER_ENTRY_FLAGS];
|
||||
|
||||
// sanity test the mapped value of a few arbitrarily chosen flags
|
||||
BEAST_EXPECT(leFlags["AccountRoot"]["lsfDisallowXRP"] == 0x00080000);
|
||||
BEAST_EXPECT(leFlags["AccountRoot"]["lsfDepositAuth"] == 0x01000000);
|
||||
BEAST_EXPECT(leFlags["AccountRoot"]["lsfAllowTrustLineClawback"] == 0x80000000);
|
||||
|
||||
BEAST_EXPECT(leFlags["RippleState"]["lsfHighFreeze"] == 0x00800000);
|
||||
BEAST_EXPECT(leFlags["RippleState"]["lsfAMMNode"] == 0x01000000);
|
||||
|
||||
BEAST_EXPECT(leFlags["DirNode"]["lsfNFTokenBuyOffers"] == 0x00000001);
|
||||
BEAST_EXPECT(leFlags["MPTokenIssuance"]["lsfMPTCanTrade"] == 0x00000010);
|
||||
BEAST_EXPECT(leFlags["Credential"]["lsfAccepted"] == 0x00010000);
|
||||
BEAST_EXPECT(leFlags["Loan"]["lsfLoanImpaired"] == 0x00020000);
|
||||
BEAST_EXPECT(leFlags["Vault"]["lsfVaultPrivate"] == 0x00010000);
|
||||
BEAST_EXPECT(leFlags["MPToken"]["lsfMPTAuthorized"] == 0x00000002);
|
||||
}
|
||||
|
||||
// validate the correctness of few chosen transaction flags
|
||||
{
|
||||
BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_FLAGS));
|
||||
Json::Value const& txFlags = result[jss::result][jss::TRANSACTION_FLAGS];
|
||||
|
||||
BEAST_EXPECT(txFlags["universal"]["tfFullyCanonicalSig"] == 0x80000000);
|
||||
BEAST_EXPECT(txFlags["universal"]["tfInnerBatchTxn"] == 0x40000000);
|
||||
|
||||
BEAST_EXPECT(txFlags["AccountSet"]["tfRequireAuth"] == 0x00040000);
|
||||
BEAST_EXPECT(txFlags["AccountSet"]["tfAllowXRP"] == 0x00200000);
|
||||
|
||||
BEAST_EXPECT(txFlags["MPTokenIssuanceSet"]["tfMPTLock"] == 0x00000001);
|
||||
BEAST_EXPECT(txFlags["MPTokenIssuanceSet"]["tfMPTUnlock"] == 0x00000002);
|
||||
|
||||
BEAST_EXPECT(txFlags["AMMDeposit"]["tfLPToken"] == 0x00010000);
|
||||
BEAST_EXPECT(txFlags["AMMDeposit"]["tfLimitLPToken"] == 0x00400000);
|
||||
}
|
||||
|
||||
// validate the correctness of the AccountSpecificFlags section
|
||||
{
|
||||
BEAST_EXPECT(result[jss::result].isMember(jss::ACCOUNT_SET_FLAGS));
|
||||
Json::Value const& asFlags = result[jss::result][jss::ACCOUNT_SET_FLAGS];
|
||||
|
||||
BEAST_EXPECT(asFlags["asfDisallowXRP"] == 3);
|
||||
BEAST_EXPECT(asFlags["asfGlobalFreeze"] == 7);
|
||||
BEAST_EXPECT(asFlags["asfDisallowIncomingNFTokenOffer"] == 12);
|
||||
BEAST_EXPECT(asFlags["asfDisallowIncomingTrustline"] == 15);
|
||||
}
|
||||
|
||||
// test the response fields of the TRANSACTION_FORMATS section
|
||||
{
|
||||
BEAST_EXPECT(result[jss::result].isMember(jss::TRANSACTION_FORMATS));
|
||||
Json::Value const& txnFormats = result[jss::result][jss::TRANSACTION_FORMATS];
|
||||
|
||||
// first validate the contents of "common"
|
||||
{
|
||||
BEAST_EXPECT(txnFormats.isMember("common"));
|
||||
Json::Value const& section = txnFormats["common"];
|
||||
|
||||
BEAST_EXPECT(section[0u][jss::name] == "TransactionType");
|
||||
BEAST_EXPECT(section[0u][jss::optionality] == soeREQUIRED);
|
||||
|
||||
BEAST_EXPECT(section[1u][jss::name] == "Flags");
|
||||
BEAST_EXPECT(section[1u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(section[2u][jss::name] == "SourceTag");
|
||||
BEAST_EXPECT(section[2u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(section[3u][jss::name] == "Account");
|
||||
BEAST_EXPECT(section[3u][jss::optionality] == soeREQUIRED);
|
||||
|
||||
BEAST_EXPECT(section[4u][jss::name] == "Sequence");
|
||||
BEAST_EXPECT(section[4u][jss::optionality] == soeREQUIRED);
|
||||
|
||||
BEAST_EXPECT(section[5u][jss::name] == "PreviousTxnID");
|
||||
BEAST_EXPECT(section[5u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(section[6u][jss::name] == "LastLedgerSequence");
|
||||
BEAST_EXPECT(section[6u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(section[7u][jss::name] == "AccountTxnID");
|
||||
BEAST_EXPECT(section[7u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(section[8u][jss::name] == "Fee");
|
||||
BEAST_EXPECT(section[8u][jss::optionality] == soeREQUIRED);
|
||||
|
||||
BEAST_EXPECT(section[9u][jss::name] == "OperationLimit");
|
||||
BEAST_EXPECT(section[9u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(section[10u][jss::name] == "Memos");
|
||||
BEAST_EXPECT(section[10u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(section[11u][jss::name] == "SigningPubKey");
|
||||
BEAST_EXPECT(section[11u][jss::optionality] == soeREQUIRED);
|
||||
|
||||
BEAST_EXPECT(section[12u][jss::name] == "TicketSequence");
|
||||
BEAST_EXPECT(section[12u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(section[13u][jss::name] == "TxnSignature");
|
||||
BEAST_EXPECT(section[13u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(section[14u][jss::name] == "Signers");
|
||||
BEAST_EXPECT(section[14u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(section[15u][jss::name] == "NetworkID");
|
||||
BEAST_EXPECT(section[15u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(section[16u][jss::name] == "Delegate");
|
||||
BEAST_EXPECT(section[16u][jss::optionality] == soeOPTIONAL);
|
||||
}
|
||||
|
||||
// validate the contents of four arbitrarily selected transactions validate the
|
||||
// format of the OracleSet transaction
|
||||
{
|
||||
BEAST_EXPECT(txnFormats.isMember("OracleSet"));
|
||||
Json::Value const& section = txnFormats["OracleSet"];
|
||||
|
||||
BEAST_EXPECT(section[0u][jss::name] == "OracleDocumentID");
|
||||
BEAST_EXPECT(section[0u][jss::optionality] == soeREQUIRED);
|
||||
|
||||
BEAST_EXPECT(section[1u][jss::name] == "Provider");
|
||||
BEAST_EXPECT(section[1u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(section[2u][jss::name] == "URI");
|
||||
BEAST_EXPECT(section[2u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(section[3u][jss::name] == "AssetClass");
|
||||
BEAST_EXPECT(section[3u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(section[4u][jss::name] == "LastUpdateTime");
|
||||
BEAST_EXPECT(section[4u][jss::optionality] == soeREQUIRED);
|
||||
|
||||
BEAST_EXPECT(section[5u][jss::name] == "PriceDataSeries");
|
||||
BEAST_EXPECT(section[5u][jss::optionality] == soeREQUIRED);
|
||||
}
|
||||
|
||||
// validate the format of the PermissionedDomainDelete transaction
|
||||
{
|
||||
BEAST_EXPECT(txnFormats.isMember("PermissionedDomainDelete"));
|
||||
Json::Value const& section = txnFormats["PermissionedDomainDelete"];
|
||||
|
||||
BEAST_EXPECT(section[0u][jss::name] == "DomainID");
|
||||
BEAST_EXPECT(section[0u][jss::optionality] == soeREQUIRED);
|
||||
}
|
||||
|
||||
// validate the format of the Clawback transaction
|
||||
{
|
||||
BEAST_EXPECT(txnFormats.isMember("Clawback"));
|
||||
Json::Value const& section = txnFormats["Clawback"];
|
||||
|
||||
BEAST_EXPECT(section[0u][jss::name] == "Amount");
|
||||
BEAST_EXPECT(section[0u][jss::optionality] == soeREQUIRED);
|
||||
|
||||
BEAST_EXPECT(section[1u][jss::name] == "Holder");
|
||||
BEAST_EXPECT(section[1u][jss::optionality] == soeOPTIONAL);
|
||||
}
|
||||
|
||||
// validate the format of the SetFee transaction
|
||||
{
|
||||
BEAST_EXPECT(txnFormats.isMember("SetFee"));
|
||||
Json::Value const& section = txnFormats["SetFee"];
|
||||
|
||||
BEAST_EXPECT(section[0u][jss::name] == "LedgerSequence");
|
||||
BEAST_EXPECT(section[0u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(section[1u][jss::name] == "BaseFee");
|
||||
BEAST_EXPECT(section[1u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(section[2u][jss::name] == "ReferenceFeeUnits");
|
||||
BEAST_EXPECT(section[2u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(section[3u][jss::name] == "ReserveBase");
|
||||
BEAST_EXPECT(section[3u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(section[4u][jss::name] == "ReserveIncrement");
|
||||
BEAST_EXPECT(section[4u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(section[5u][jss::name] == "BaseFeeDrops");
|
||||
BEAST_EXPECT(section[5u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(section[6u][jss::name] == "ReserveBaseDrops");
|
||||
BEAST_EXPECT(section[6u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(section[7u][jss::name] == "ReserveIncrementDrops");
|
||||
BEAST_EXPECT(section[7u][jss::optionality] == soeOPTIONAL);
|
||||
}
|
||||
}
|
||||
|
||||
// test the properties of the LEDGER_ENTRY_FORMATS section in server_definitions
|
||||
// response
|
||||
{
|
||||
BEAST_EXPECT(result[jss::result].isMember(jss::LEDGER_ENTRY_FORMATS));
|
||||
|
||||
// Note: For the purposes of software maintenance, this test does not exhaustively
|
||||
// validate all the LEDGER_ENTRY_FORMATS
|
||||
|
||||
// check "common" first
|
||||
{
|
||||
Json::Value const& observedCommonLedgerEntry =
|
||||
result[jss::result][jss::LEDGER_ENTRY_FORMATS]["common"];
|
||||
|
||||
BEAST_EXPECT(observedCommonLedgerEntry[0u][jss::name] == "LedgerIndex");
|
||||
BEAST_EXPECT(observedCommonLedgerEntry[0u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(observedCommonLedgerEntry[1u][jss::name] == "LedgerEntryType");
|
||||
BEAST_EXPECT(observedCommonLedgerEntry[1u][jss::optionality] == soeREQUIRED);
|
||||
|
||||
BEAST_EXPECT(observedCommonLedgerEntry[2u][jss::name] == "Flags");
|
||||
BEAST_EXPECT(observedCommonLedgerEntry[2u][jss::optionality] == soeREQUIRED);
|
||||
}
|
||||
|
||||
// test the contents of an arbitrary ledger-entry (DID)
|
||||
{
|
||||
Json::Value const& observedDIDLedgerEntry =
|
||||
result[jss::result][jss::LEDGER_ENTRY_FORMATS]["DID"];
|
||||
|
||||
BEAST_EXPECT(observedDIDLedgerEntry[0u][jss::name] == "Account");
|
||||
BEAST_EXPECT(observedDIDLedgerEntry[0u][jss::optionality] == soeREQUIRED);
|
||||
|
||||
BEAST_EXPECT(observedDIDLedgerEntry[1u][jss::name] == "DIDDocument");
|
||||
BEAST_EXPECT(observedDIDLedgerEntry[1u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(observedDIDLedgerEntry[2u][jss::name] == "URI");
|
||||
BEAST_EXPECT(observedDIDLedgerEntry[2u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(observedDIDLedgerEntry[3u][jss::name] == "Data");
|
||||
BEAST_EXPECT(observedDIDLedgerEntry[3u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(observedDIDLedgerEntry[4u][jss::name] == "OwnerNode");
|
||||
BEAST_EXPECT(observedDIDLedgerEntry[4u][jss::optionality] == soeREQUIRED);
|
||||
|
||||
BEAST_EXPECT(observedDIDLedgerEntry[5u][jss::name] == "PreviousTxnID");
|
||||
BEAST_EXPECT(observedDIDLedgerEntry[5u][jss::optionality] == soeREQUIRED);
|
||||
|
||||
BEAST_EXPECT(observedDIDLedgerEntry[6u][jss::name] == "PreviousTxnLgrSeq");
|
||||
BEAST_EXPECT(observedDIDLedgerEntry[6u][jss::optionality] == soeREQUIRED);
|
||||
}
|
||||
|
||||
// test the contents of an arbitrary ledger-entry (NegativeUNL)
|
||||
{
|
||||
Json::Value const& observedNunlLedgerEntry =
|
||||
result[jss::result][jss::LEDGER_ENTRY_FORMATS]["NegativeUNL"];
|
||||
|
||||
BEAST_EXPECT(observedNunlLedgerEntry[0u][jss::name] == "DisabledValidators");
|
||||
BEAST_EXPECT(observedNunlLedgerEntry[0u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(observedNunlLedgerEntry[1u][jss::name] == "ValidatorToDisable");
|
||||
BEAST_EXPECT(observedNunlLedgerEntry[1u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(observedNunlLedgerEntry[2u][jss::name] == "ValidatorToReEnable");
|
||||
BEAST_EXPECT(observedNunlLedgerEntry[2u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(observedNunlLedgerEntry[3u][jss::name] == "PreviousTxnID");
|
||||
BEAST_EXPECT(observedNunlLedgerEntry[3u][jss::optionality] == soeOPTIONAL);
|
||||
|
||||
BEAST_EXPECT(observedNunlLedgerEntry[4u][jss::name] == "PreviousTxnLgrSeq");
|
||||
BEAST_EXPECT(observedNunlLedgerEntry[4u][jss::optionality] == soeOPTIONAL);
|
||||
}
|
||||
}
|
||||
|
||||
// Exhaustive test: verify all transaction flags from getAllTxFlags() appear in the
|
||||
// output
|
||||
{
|
||||
Json::Value const& txFlags = result[jss::result][jss::TRANSACTION_FLAGS];
|
||||
|
||||
for (auto const& [txName, flagMap] : getAllTxFlags())
|
||||
{
|
||||
BEAST_EXPECT(txFlags.isMember(txName));
|
||||
if (txFlags.isMember(txName))
|
||||
{
|
||||
for (auto const& [flagName, flagValue] : flagMap)
|
||||
{
|
||||
BEAST_EXPECT(txFlags[txName].isMember(flagName));
|
||||
if (txFlags[txName].isMember(flagName))
|
||||
{
|
||||
BEAST_EXPECT(txFlags[txName][flagName].asUInt() == flagValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exhaustive test: verify all ledger entry flags from getAllLedgerFlags() appear in the
|
||||
// output
|
||||
{
|
||||
Json::Value const& leFlags = result[jss::result][jss::LEDGER_ENTRY_FLAGS];
|
||||
|
||||
for (auto const& [ledgerType, flagMap] : getAllLedgerFlags())
|
||||
{
|
||||
BEAST_EXPECT(leFlags.isMember(ledgerType));
|
||||
if (leFlags.isMember(ledgerType))
|
||||
{
|
||||
for (auto const& [flagName, flagValue] : flagMap)
|
||||
{
|
||||
BEAST_EXPECT(leFlags[ledgerType].isMember(flagName));
|
||||
if (leFlags[ledgerType].isMember(flagName))
|
||||
{
|
||||
BEAST_EXPECT(leFlags[ledgerType][flagName].asUInt() == flagValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exhaustive test: verify all AccountSet flags from getAsfFlagMap() appear in the
|
||||
// output
|
||||
{
|
||||
Json::Value const& asFlags = result[jss::result][jss::ACCOUNT_SET_FLAGS];
|
||||
|
||||
for (auto const& [flagName, flagValue] : getAsfFlagMap())
|
||||
{
|
||||
BEAST_EXPECT(asFlags.isMember(flagName));
|
||||
if (asFlags.isMember(flagName))
|
||||
{
|
||||
BEAST_EXPECT(asFlags[flagName].asInt() == flagValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test providing the same hash
|
||||
{
|
||||
Env env(*this);
|
||||
auto const firstResult = env.rpc("server_definitions");
|
||||
auto const hash = firstResult[jss::result][jss::hash].asString();
|
||||
auto const hashParam = std::string("{ ") + "\"hash\": \"" + hash + "\"}";
|
||||
|
||||
auto const result = env.rpc("json", "server_definitions", hashParam);
|
||||
BEAST_EXPECT(!result[jss::result].isMember(jss::error));
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == "success");
|
||||
BEAST_EXPECT(!result[jss::result].isMember(jss::FIELDS));
|
||||
BEAST_EXPECT(!result[jss::result].isMember(jss::LEDGER_ENTRY_TYPES));
|
||||
BEAST_EXPECT(!result[jss::result].isMember(jss::LEDGER_ENTRY_FLAGS));
|
||||
BEAST_EXPECT(!result[jss::result].isMember(jss::LEDGER_ENTRY_FORMATS));
|
||||
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_FORMATS));
|
||||
BEAST_EXPECT(!result[jss::result].isMember(jss::TYPES));
|
||||
BEAST_EXPECT(result[jss::result].isMember(jss::hash));
|
||||
}
|
||||
|
||||
// test providing a different hash
|
||||
{
|
||||
Env env(*this);
|
||||
std::string const hash =
|
||||
"54296160385A27154BFA70A239DD8E8FD4CC2DB7BA32D970BA3A5B132CF749"
|
||||
"D1";
|
||||
auto const hashParam = std::string("{ ") + "\"hash\": \"" + hash + "\"}";
|
||||
|
||||
auto const result = env.rpc("json", "server_definitions", hashParam);
|
||||
BEAST_EXPECT(!result[jss::result].isMember(jss::error));
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == "success");
|
||||
BEAST_EXPECT(result[jss::result].isMember(jss::FIELDS));
|
||||
BEAST_EXPECT(result[jss::result].isMember(jss::LEDGER_ENTRY_TYPES));
|
||||
BEAST_EXPECT(result[jss::result].isMember(jss::LEDGER_ENTRY_FLAGS));
|
||||
BEAST_EXPECT(result[jss::result].isMember(jss::LEDGER_ENTRY_FORMATS));
|
||||
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_FORMATS));
|
||||
BEAST_EXPECT(result[jss::result].isMember(jss::TYPES));
|
||||
BEAST_EXPECT(result[jss::result].isMember(jss::hash));
|
||||
}
|
||||
}
|
||||
|
||||
// test providing the same hash
|
||||
{
|
||||
Env env(*this);
|
||||
auto const firstResult = env.rpc("server_definitions");
|
||||
auto const hash = firstResult[jss::result][jss::hash].asString();
|
||||
auto const hashParam = std::string("{ ") + "\"hash\": \"" + hash + "\"}";
|
||||
|
||||
auto const result = env.rpc("json", "server_definitions", hashParam);
|
||||
BEAST_EXPECT(!result[jss::result].isMember(jss::error));
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == "success");
|
||||
BEAST_EXPECT(!result[jss::result].isMember(jss::FIELDS));
|
||||
BEAST_EXPECT(!result[jss::result].isMember(jss::LEDGER_ENTRY_TYPES));
|
||||
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::TYPES));
|
||||
BEAST_EXPECT(result[jss::result].isMember(jss::hash));
|
||||
}
|
||||
|
||||
// test providing a different hash
|
||||
{
|
||||
Env env(*this);
|
||||
std::string const hash =
|
||||
"54296160385A27154BFA70A239DD8E8FD4CC2DB7BA32D970BA3A5B132CF749"
|
||||
"D1";
|
||||
auto const hashParam = std::string("{ ") + "\"hash\": \"" + hash + "\"}";
|
||||
|
||||
auto const result = env.rpc("json", "server_definitions", hashParam);
|
||||
BEAST_EXPECT(!result[jss::result].isMember(jss::error));
|
||||
BEAST_EXPECT(result[jss::result][jss::status] == "success");
|
||||
BEAST_EXPECT(result[jss::result].isMember(jss::FIELDS));
|
||||
BEAST_EXPECT(result[jss::result].isMember(jss::LEDGER_ENTRY_TYPES));
|
||||
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::TYPES));
|
||||
BEAST_EXPECT(result[jss::result].isMember(jss::hash));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,15 +6,12 @@
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/protocol/TxFormats.h>
|
||||
#include <xrpl/protocol/digest.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <set>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -50,14 +47,13 @@ public:
|
||||
std::string
|
||||
ServerDefinitions::translate(std::string const& inp)
|
||||
{
|
||||
auto replace = [&](std::string_view oldStr, std::string_view newStr) -> std::string {
|
||||
auto replace = [&](char const* oldStr, char const* newStr) -> std::string {
|
||||
std::string out = inp;
|
||||
boost::replace_all(out, oldStr, newStr);
|
||||
return out;
|
||||
};
|
||||
|
||||
// TODO: use string::contains with C++23
|
||||
auto contains = [&](std::string_view s) -> bool { return inp.find(s) != std::string::npos; };
|
||||
auto contains = [&](char const* s) -> bool { return inp.find(s) != std::string::npos; };
|
||||
|
||||
if (contains("UINT"))
|
||||
{
|
||||
@@ -68,7 +64,7 @@ ServerDefinitions::translate(std::string const& inp)
|
||||
return replace("UINT", "UInt");
|
||||
}
|
||||
|
||||
static std::unordered_map<std::string_view, std::string_view> const replacements{
|
||||
std::unordered_map<std::string, std::string> replacements{
|
||||
{"OBJECT", "STObject"},
|
||||
{"ARRAY", "STArray"},
|
||||
{"ACCOUNT", "AccountID"},
|
||||
@@ -81,7 +77,7 @@ ServerDefinitions::translate(std::string const& inp)
|
||||
|
||||
if (auto const& it = replacements.find(inp); it != replacements.end())
|
||||
{
|
||||
return std::string(it->second);
|
||||
return it->second;
|
||||
}
|
||||
|
||||
std::string out;
|
||||
@@ -215,35 +211,36 @@ ServerDefinitions::ServerDefinitions() : defs_{Json::objectValue}
|
||||
defs_[jss::FIELDS][i++] = a;
|
||||
}
|
||||
|
||||
for (auto const& [code, field] : xrpl::SField::getKnownCodeToField())
|
||||
for (auto const& [code, f] : xrpl::SField::getKnownCodeToField())
|
||||
{
|
||||
if (field->fieldName == "")
|
||||
if (f->fieldName == "")
|
||||
continue;
|
||||
|
||||
Json::Value innerObj = Json::objectValue;
|
||||
|
||||
uint32_t type = field->fieldType;
|
||||
uint32_t type = f->fieldType;
|
||||
|
||||
innerObj[jss::nth] = field->fieldValue;
|
||||
innerObj[jss::nth] = f->fieldValue;
|
||||
|
||||
// whether the field is variable-length encoded this means that the length is included
|
||||
// before the content
|
||||
// whether the field is variable-length encoded
|
||||
// this means that the length is included before the content
|
||||
innerObj[jss::isVLEncoded] =
|
||||
(type == 7U /* Blob */ || type == 8U /* AccountID */ || type == 19U /* Vector256 */);
|
||||
(type == 7U /* Blob */ || type == 8U /* AccountID */ ||
|
||||
type == 19U /* Vector256 */);
|
||||
|
||||
// whether the field is included in serialization
|
||||
innerObj[jss::isSerialized] =
|
||||
(type < 10000 && field->fieldName != "hash" &&
|
||||
field->fieldName !=
|
||||
"index"); // hash, index, TRANSACTION, LEDGER_ENTRY, VALIDATION, METADATA
|
||||
(type < 10000 && f->fieldName != "hash" &&
|
||||
f->fieldName != "index"); /* hash, index, TRANSACTION,
|
||||
LEDGER_ENTRY, VALIDATION, METADATA */
|
||||
|
||||
// whether the field is included in serialization when signing
|
||||
innerObj[jss::isSigningField] = field->shouldInclude(false);
|
||||
innerObj[jss::isSigningField] = f->shouldInclude(false);
|
||||
|
||||
innerObj[jss::type] = typeMap[type];
|
||||
|
||||
Json::Value innerArray = Json::arrayValue;
|
||||
innerArray[0U] = field->fieldName;
|
||||
innerArray[0U] = f->fieldName;
|
||||
innerArray[1U] = innerObj;
|
||||
|
||||
defs_[jss::FIELDS][i++] = innerArray;
|
||||
@@ -265,92 +262,6 @@ ServerDefinitions::ServerDefinitions() : defs_{Json::objectValue}
|
||||
defs_[jss::TRANSACTION_TYPES][f.getName()] = f.getType();
|
||||
}
|
||||
|
||||
// populate TxFormats
|
||||
defs_[jss::TRANSACTION_FORMATS] = Json::objectValue;
|
||||
|
||||
defs_[jss::TRANSACTION_FORMATS][jss::common] = Json::arrayValue;
|
||||
auto txCommonFields = std::set<std::string>();
|
||||
for (auto const& element : TxFormats::getCommonFields())
|
||||
{
|
||||
Json::Value elementObj = Json::objectValue;
|
||||
elementObj[jss::name] = element.sField().getName();
|
||||
elementObj[jss::optionality] = element.style();
|
||||
defs_[jss::TRANSACTION_FORMATS][jss::common].append(elementObj);
|
||||
txCommonFields.insert(element.sField().getName());
|
||||
}
|
||||
|
||||
for (auto const& format : TxFormats::getInstance())
|
||||
{
|
||||
auto const& soTemplate = format.getSOTemplate();
|
||||
Json::Value templateArray = Json::arrayValue;
|
||||
for (auto const& element : soTemplate)
|
||||
{
|
||||
if (txCommonFields.contains(element.sField().getName()))
|
||||
continue; // skip common fields, already added
|
||||
Json::Value elementObj = Json::objectValue;
|
||||
elementObj[jss::name] = element.sField().getName();
|
||||
elementObj[jss::optionality] = element.style();
|
||||
templateArray.append(elementObj);
|
||||
}
|
||||
defs_[jss::TRANSACTION_FORMATS][format.getName()] = templateArray;
|
||||
}
|
||||
|
||||
// populate LedgerFormats
|
||||
defs_[jss::LEDGER_ENTRY_FORMATS] = Json::objectValue;
|
||||
defs_[jss::LEDGER_ENTRY_FORMATS][jss::common] = Json::arrayValue;
|
||||
auto ledgerCommonFields = std::set<std::string>();
|
||||
for (auto const& element : LedgerFormats::getCommonFields())
|
||||
{
|
||||
Json::Value elementObj = Json::objectValue;
|
||||
elementObj[jss::name] = element.sField().getName();
|
||||
elementObj[jss::optionality] = element.style();
|
||||
defs_[jss::LEDGER_ENTRY_FORMATS][jss::common].append(elementObj);
|
||||
ledgerCommonFields.insert(element.sField().getName());
|
||||
}
|
||||
for (auto const& format : LedgerFormats::getInstance())
|
||||
{
|
||||
auto const& soTemplate = format.getSOTemplate();
|
||||
Json::Value templateArray = Json::arrayValue;
|
||||
for (auto const& element : soTemplate)
|
||||
{
|
||||
if (ledgerCommonFields.contains(element.sField().getName()))
|
||||
continue; // skip common fields, already added
|
||||
Json::Value elementObj = Json::objectValue;
|
||||
elementObj[jss::name] = element.sField().getName();
|
||||
elementObj[jss::optionality] = element.style();
|
||||
templateArray.append(elementObj);
|
||||
}
|
||||
defs_[jss::LEDGER_ENTRY_FORMATS][format.getName()] = templateArray;
|
||||
}
|
||||
|
||||
defs_[jss::TRANSACTION_FLAGS] = Json::objectValue;
|
||||
for (auto const& [name, value] : getAllTxFlags())
|
||||
{
|
||||
Json::Value txObj = Json::objectValue;
|
||||
for (auto const& [flagName, flagValue] : value)
|
||||
{
|
||||
txObj[flagName] = flagValue;
|
||||
}
|
||||
defs_[jss::TRANSACTION_FLAGS][name] = txObj;
|
||||
}
|
||||
|
||||
defs_[jss::LEDGER_ENTRY_FLAGS] = Json::objectValue;
|
||||
for (auto const& [name, value] : getAllLedgerFlags())
|
||||
{
|
||||
Json::Value ledgerObj = Json::objectValue;
|
||||
for (auto const& [flagName, flagValue] : value)
|
||||
{
|
||||
ledgerObj[flagName] = flagValue;
|
||||
}
|
||||
defs_[jss::LEDGER_ENTRY_FLAGS][name] = ledgerObj;
|
||||
}
|
||||
|
||||
defs_[jss::ACCOUNT_SET_FLAGS] = Json::objectValue;
|
||||
for (auto const& [name, value] : getAsfFlagMap())
|
||||
{
|
||||
defs_[jss::ACCOUNT_SET_FLAGS][name] = value;
|
||||
}
|
||||
|
||||
// generate hash
|
||||
{
|
||||
std::string const out = Json::FastWriter().write(defs_);
|
||||
|
||||
Reference in New Issue
Block a user