Compare commits

..

9 Commits

Author SHA1 Message Date
Jingchen
bb529d0317 fix: Stop embedded tests from hanging on ARM by using atomic_flag (#6248)
This change replaces the mutex `stoppingMutex_`, the `atomic_bool` variable `isTimeToStop`, and the conditional variable `stoppingCondition_` with an `atomic_flag` variable.

When `xrpld` is running the embedded tests as a child process, it has a control thread (the app bundle thread) that starts the application, and an application thread (the thread that executes `app_->run()`). Due to the relaxed memory ordering on ARM, it's not guaranteed that the application thread can see the change of the value resulting from the `isTimeToStop.exchange(true)` call before it is notified by `stoppingCondition_.notify_all()`, even though they do happen in the right order in the app bundle thread in `ApplicationImp::signalStop`. We therefore often get into the situation where `isTimeToStop` is `true`, but the application thread is waiting for `stoppingCondition_` to notify, because the app bundle thread may have already notified before the application thread actually starts waiting.

Switching to a single `atomic_flag` variable makes sure that there's only one synchronisation object and then the memory order guarantee provided by c++ can make sure that `notify_all` gets synchronised after `test_and_set` does.

Fixing this issue will stop the unit tests hanging forever and then we should see less (or hopefully no) time out errors in daily github action runs
2026-01-26 21:39:28 +00:00
Ed Hennis
a2f1973574 fix: Remove DEFAULT fields that change to the default in associateAsset (#6259) (#6273)
- Add Vault creation tests for showing valid range for AssetsMaximum
2026-01-26 19:58:12 +00:00
Bart
847e875635 refactor: Update Boost to 1.90 (#6280)
Upcoming feature work requires functionality present in a newer Boost version. These newer versions also have improvements for sanitizers.
2026-01-26 18:54:43 +00:00
Mayukha Vadari
778da954b4 refactor: clean up uses of std::source_location (#6272)
Since the minimum Clang version we support is 16, the checks for version < 15 are no longer necessary. This change therefore removes the macros checking if the clang version is < 15 and simplifies uses of `std::source_location`.
2026-01-23 14:09:00 -05:00
Bart
0586b5678e ci: Pass missing sanitizers input to actions (#6266)
The `upload-conan-deps` workflow that's triggered on push is supposed to upload the Conan dependencies to our remote, so future PR commits can pull those dependencies from the remote. However, as the `sanitize` argument is missing, it was building different dependencies than what the PRs are building for the asan/tsan/ubsan job, so the latter would not find anything in the remote that they could use. This change sets the missing `sanitizers` input variable when running the `build-deps` action. 

Separately, the `setup-conan` action showed the default profile, while we are using the `ci` profile. To ensure the profile is correctly printed when sanitizers are enabled, the environment variable the profile uses is set before calling the action.
2026-01-23 06:40:55 -05:00
Bart
66158d786f ci: Properly propagate Conan credentials (#6265)
The export and upload steps were initially in a separate action, where GitHub Actions does not support the `secrets` keyword, but only `inputs` for the credentials. After they were moved to a reusable workflow, only part of the references to the credentials were updated. This change correctly references to the Conan credentials via `secrets` instead of `inputs`.
2026-01-22 16:05:15 -05:00
Bart
c57ffdbcb8 ci: Explicitly set version when exporting the Conan recipe (#6264)
By default the Conan recipe extracts the version from `BuildInfo.cpp`, but in some of the cases we want to upload a recipe with a suffix derived from the commit hash. This currently then results in the uploading to fail, since there is a version mismatch.

Here we explicitly set the version, and then simplify the steps in the upload workflow since we now need the recipe name (embedded within the conanfile.py but also needed when uploading), the recipe version, and the recipe ref (name/version).
2026-01-22 19:05:59 +00:00
Bart
4e3f953fc4 ci: Use plus instead of hyphen for Conan recipe version suffix (#6261)
Conan recipes use semantic versioning, and since our version already contains a hyphen the second hyphen causes Conan to ignore it. The plus sign is a valid separator we can use instead, so this change uses a `+` to separate a version suffix (commit hash) instead of a `-`.
2026-01-22 16:42:53 +00:00
Pratik Mankawde
a4f8aa623f chore: Detect uninitialized variables in CMake files (#6247)
There were a few uninitialized variables in CMake files. This change will make sure we always check if a variable has been initialized before using them, or in come cases initialize them by default. This change will raise an error on CI if a developer introduced an uninitialized variable in CMake files.
2026-01-22 11:16:18 -05:00
25 changed files with 366 additions and 71 deletions

View File

@@ -17,8 +17,10 @@ runs:
VERSION: ${{ github.ref_name }}
run: echo "VERSION=${VERSION}" >> "${GITHUB_ENV}"
# When a tag is not pushed, then the version is extracted from the
# BuildInfo.cpp file and the shortened commit hash appended to it.
# When a tag is not pushed, then the version (e.g. 1.2.3-b0) is extracted
# from the BuildInfo.cpp file and the shortened commit hash appended to it.
# We use a plus sign instead of a hyphen because Conan recipe versions do
# not support two hyphens.
- name: Generate version for non-tag event
if: ${{ github.event_name != 'tag' }}
shell: bash
@@ -32,7 +34,7 @@ runs:
echo 'Appending shortened commit hash to version.'
SHA='${{ github.sha }}'
VERSION="${VERSION}-${SHA:0:7}"
VERSION="${VERSION}+${SHA:0:7}"
echo "VERSION=${VERSION}" >> "${GITHUB_ENV}"

View File

@@ -31,7 +31,7 @@ runs:
conan config install conan/profiles/ -tf $(conan config home)/profiles/
echo 'Conan profile:'
conan profile show
conan profile show --profile ci
- name: Set up Conan remote
shell: bash

View File

@@ -125,6 +125,8 @@ jobs:
subtract: ${{ inputs.nproc_subtract }}
- name: Setup Conan
env:
SANITIZERS: ${{ inputs.sanitizers }}
uses: ./.github/actions/setup-conan
- name: Build dependencies

View File

@@ -49,10 +49,6 @@ jobs:
id: version
uses: ./.github/actions/generate-version
- name: Determine recipe reference
id: ref
run: echo "ref=xrpl/${{ steps.version.outputs.version }}" >> "${GITHUB_OUTPUT}"
- name: Set up Conan
uses: ./.github/actions/setup-conan
with:
@@ -62,17 +58,16 @@ jobs:
- name: Log into Conan remote
env:
REMOTE_NAME: ${{ inputs.remote_name }}
REMOTE_USERNAME: ${{ inputs.remote_username }}
REMOTE_PASSWORD: ${{ inputs.remote_password }}
REMOTE_USERNAME: ${{ secrets.remote_username }}
REMOTE_PASSWORD: ${{ secrets.remote_password }}
run: conan remote login "${REMOTE_NAME}" "${REMOTE_USERNAME}" --password "${REMOTE_PASSWORD}"
- name: Upload Conan recipe
env:
RECIPE_REF: ${{ steps.ref.outputs.ref }}
REMOTE_NAME: ${{ inputs.remote_name }}
run: |
conan export .
conan upload --confirm --check --remote="${REMOTE_NAME}" ${RECIPE_REF}
conan export . --version=${{ steps.version.outputs.version }}
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/${{ steps.version.outputs.version }}
outputs:
ref: ${{ steps.ref.outputs.ref }}
ref: xrpl/${{ steps.version.outputs.version }}

View File

@@ -84,6 +84,8 @@ jobs:
subtract: ${{ env.NPROC_SUBTRACT }}
- name: Setup Conan
env:
SANITIZERS: ${{ matrix.sanitizers }}
uses: ./.github/actions/setup-conan
with:
remote_name: ${{ env.CONAN_REMOTE_NAME }}
@@ -98,6 +100,7 @@ jobs:
# Set the verbosity to "quiet" for Windows to avoid an excessive
# amount of logs. For other OSes, the "verbose" logs are more useful.
log_verbosity: ${{ runner.os == 'Windows' && 'quiet' || 'verbose' }}
sanitizers: ${{ matrix.sanitizers }}
- name: Log into Conan remote
if: ${{ github.repository_owner == 'XRPLF' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }}

View File

@@ -148,8 +148,8 @@ function extract_version {
}
# Define which recipes to export.
recipes=('ed25519' 'grpc' 'openssl' 'secp256k1' 'snappy' 'soci')
folders=('all' 'all' '3.x.x' 'all' 'all' 'all')
recipes=('ed25519' 'grpc' 'nudb' 'openssl' 'secp256k1' 'snappy' 'soci')
folders=('all' 'all' 'all' '3.x.x' 'all' 'all' 'all')
# Selectively check out the recipes from our CCI fork.
cd external

View File

@@ -8,7 +8,9 @@ if(POLICY CMP0077)
endif()
# Fix "unrecognized escape" issues when passing CMAKE_MODULE_PATH on Windows.
file(TO_CMAKE_PATH "${CMAKE_MODULE_PATH}" CMAKE_MODULE_PATH)
if(DEFINED CMAKE_MODULE_PATH)
file(TO_CMAKE_PATH "${CMAKE_MODULE_PATH}" CMAKE_MODULE_PATH)
endif()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
project(xrpl)

View File

@@ -13,6 +13,7 @@ include_guard(GLOBAL)
set(is_clang FALSE)
set(is_gcc FALSE)
set(is_msvc FALSE)
set(is_xcode FALSE)
if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") # Clang or AppleClang
set(is_clang TRUE)
@@ -24,6 +25,11 @@ else()
message(FATAL_ERROR "Unsupported C++ compiler: ${CMAKE_CXX_COMPILER_ID}")
endif()
# Xcode generator detection
if(CMAKE_GENERATOR STREQUAL "Xcode")
set(is_xcode TRUE)
endif()
# --------------------------------------------------------------------
# Operating system detection

View File

@@ -32,14 +32,14 @@ target_protobuf_sources(xrpl.libpb xrpl/proto
target_compile_options(xrpl.libpb
PUBLIC
$<$<BOOL:${MSVC}>:-wd4996>
$<$<BOOL:${XCODE}>:
$<$<BOOL:${is_msvc}>:-wd4996>
$<$<BOOL:${is_xcode}>:
--system-header-prefix="google/protobuf"
-Wno-deprecated-dynamic-exception-spec
>
PRIVATE
$<$<BOOL:${MSVC}>:-wd4065>
$<$<NOT:$<BOOL:${MSVC}>>:-Wno-deprecated-declarations>
$<$<BOOL:${is_msvc}>:-wd4065>
$<$<NOT:$<BOOL:${is_msvc}>>:-Wno-deprecated-declarations>
)
target_link_libraries(xrpl.libpb

View File

@@ -4,6 +4,12 @@
include(create_symbolic_link)
# 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

View File

@@ -4,6 +4,11 @@
include(CompilationEnv)
# Set defaults for optional variables to avoid uninitialized variable warnings
if(NOT DEFINED voidstar)
set(voidstar OFF)
endif()
add_library (opts INTERFACE)
add_library (Xrpl::opts ALIAS opts)
target_compile_definitions (opts
@@ -52,7 +57,7 @@ add_library (xrpl_syslibs INTERFACE)
add_library (Xrpl::syslibs ALIAS xrpl_syslibs)
target_link_libraries (xrpl_syslibs
INTERFACE
$<$<BOOL:${MSVC}>:
$<$<BOOL:${is_msvc}>:
legacy_stdio_definitions.lib
Shlwapi
kernel32
@@ -69,10 +74,10 @@ target_link_libraries (xrpl_syslibs
odbccp32
crypt32
>
$<$<NOT:$<BOOL:${MSVC}>>:dl>
$<$<NOT:$<OR:$<BOOL:${MSVC}>,$<BOOL:${APPLE}>>>:rt>)
$<$<NOT:$<BOOL:${is_msvc}>>:dl>
$<$<NOT:$<OR:$<BOOL:${is_msvc}>,$<BOOL:${is_macos}>>>:rt>)
if (NOT MSVC)
if (NOT is_msvc)
set (THREADS_PREFER_PTHREAD_FLAG ON)
find_package (Threads)
target_link_libraries (xrpl_syslibs INTERFACE Threads::Threads)

View File

@@ -43,7 +43,10 @@
include(CompilationEnv)
# Read environment variable
set(SANITIZERS $ENV{SANITIZERS})
set(SANITIZERS "")
if(DEFINED ENV{SANITIZERS})
set(SANITIZERS "$ENV{SANITIZERS}")
endif()
# Set SANITIZERS_ENABLED flag for use in other modules
if(SANITIZERS MATCHES "address|thread|undefinedbehavior")

View File

@@ -4,10 +4,11 @@
include(CompilationEnv)
if("$ENV{CI}" STREQUAL "true" OR "$ENV{CONTINUOUS_INTEGRATION}" STREQUAL "true")
set(is_ci TRUE)
else()
set(is_ci FALSE)
set(is_ci FALSE)
if(DEFINED ENV{CI})
if("$ENV{CI}" STREQUAL "true")
set(is_ci TRUE)
endif()
endif()
get_directory_property(has_parent PARENT_DIRECTORY)

View File

@@ -30,7 +30,6 @@ target_link_libraries(xrpl_boost
Boost::process
Boost::program_options
Boost::regex
Boost::system
Boost::thread)
if(Boost_COMPILER)
target_link_libraries(xrpl_boost INTERFACE Boost::disable_autolinking)

View File

@@ -11,7 +11,7 @@
"re2/20230301#ca3b241baec15bd31ea9187150e0b333%1765850148.103",
"protobuf/6.32.1#f481fd276fc23a33b85a3ed1e898b693%1765850161.038",
"openssl/3.5.4#1b986e61b38fdfda3b40bebc1b234393%1768312656.257",
"nudb/2.0.9#fb8dfd1a5557f5e0528114c2da17721e%1765850143.957",
"nudb/2.0.9#0432758a24204da08fee953ec9ea03cb%1769436073.32",
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914",
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492",
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03",
@@ -23,7 +23,7 @@
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1765850143.772",
"c-ares/1.34.5#5581c2b62a608b40bb85d965ab3ec7c8%1765850144.336",
"bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1765850143.837",
"boost/1.88.0#8852c0b72ce8271fb8ff7c53456d4983%1765850172.862",
"boost/1.90.0#d5e8defe7355494953be18524a7f135b%1765955095.179",
"abseil/20250127.0#99262a368bd01c0ccca8790dfced9719%1766517936.993"
],
"build_requires": [
@@ -42,18 +42,22 @@
],
"python_requires": [],
"overrides": {
"boost/1.90.0#d5e8defe7355494953be18524a7f135b": [
null,
"boost/1.90.0"
],
"protobuf/5.27.0": [
"protobuf/6.32.1"
],
"lz4/1.9.4": [
"lz4/1.10.0"
],
"boost/1.83.0": [
"boost/1.88.0"
],
"sqlite3/3.44.2": [
"sqlite3/3.49.1"
],
"boost/1.83.0": [
"boost/1.90.0"
],
"lz4/[>=1.9.4 <2]": [
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504"
]

View File

@@ -131,7 +131,7 @@ class Xrpl(ConanFile):
transitive_headers_opt = (
{"transitive_headers": True} if conan_version.split(".")[0] == "2" else {}
)
self.requires("boost/1.88.0", force=True, **transitive_headers_opt)
self.requires("boost/1.90.0", force=True, **transitive_headers_opt)
self.requires("date/3.0.4", **transitive_headers_opt)
self.requires("lz4/1.10.0", force=True)
self.requires("protobuf/6.32.1", force=True)

View File

@@ -213,9 +213,9 @@ class Number
using rep = std::int64_t;
using internalrep = MantissaRange::rep;
bool negative_{false};
internalrep mantissa_{0};
int exponent_{std::numeric_limits<int>::lowest()};
bool negative_{false};
public:
// The range for the exponent when normalized
@@ -524,7 +524,7 @@ inline constexpr Number::Number(
internalrep mantissa,
int exponent,
unchecked) noexcept
: mantissa_{mantissa}, exponent_{exponent}, negative_(negative)
: negative_(negative), mantissa_{mantissa}, exponent_{exponent}
{
}

View File

@@ -397,6 +397,9 @@ public:
void
delField(int index);
SOEStyle
getStyle(SField const& field) const;
bool
hasMatchingEntry(STBase const&);

View File

@@ -100,6 +100,8 @@ areComparable(STAmount const& v1, STAmount const& v2)
return false;
}
static_assert(INITIAL_XRP.drops() == STAmount::cMaxNativeN);
STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name)
{
std::uint64_t value = sit.get64();

View File

@@ -580,6 +580,12 @@ STObject::delField(int index)
v_.erase(v_.begin() + index);
}
SOEStyle
STObject::getStyle(SField const& field) const
{
return mType ? mType->style(field) : soeINVALID;
}
unsigned char
STObject::getFieldU8(SField const& field) const
{

View File

@@ -18,10 +18,28 @@ associateAsset(SLE& sle, Asset const& asset)
// If the field is not set or present, skip it.
if (type == STI_NOTPRESENT)
continue;
// If the type doesn't downcast, then the flag shouldn't be on the
// SField
auto& ta = entry.downcast<STTakesAsset>();
auto const style = sle.getStyle(ta.getFName());
XRPL_ASSERT_PARTS(
style != soeINVALID,
"xrpl::associateAsset",
"valid template element style");
XRPL_ASSERT_PARTS(
style != soeDEFAULT || !ta.isDefault(),
"xrpl::associateAsset",
"non-default value");
ta.associateAsset(asset);
// associateAsset in derived classes may change the underlying
// value, but it won't know anything about how the value relates to
// the SLE. If the template element is soeDEFAULT, and the value
// changed to the default value, remove the field.
if (style == soeDEFAULT && ta.isDefault())
sle.makeFieldAbsent(field);
}
}
}

View File

@@ -5782,6 +5782,263 @@ class Vault_test : public beast::unit_test::suite
testCase(MPT, "MPT", owner, depositor, issuer);
}
void
testAssetsMaximum()
{
testcase("Assets Maximum");
using namespace test::jtx;
Env env{*this, testable_amendments() | featureSingleAssetVault};
Account const owner{"owner"};
Account const issuer{"issuer"};
Vault vault{env};
env.fund(XRP(1'000'000), issuer, owner);
env.close();
auto const maxInt64 =
std::to_string(std::numeric_limits<std::int64_t>::max());
BEAST_EXPECT(maxInt64 == "9223372036854775807");
// Naming things is hard
auto const maxInt64Plus1 = std::to_string(
static_cast<std::uint64_t>(
std::numeric_limits<std::int64_t>::max()) +
1);
BEAST_EXPECT(maxInt64Plus1 == "9223372036854775808");
auto const initialXRP = to_string(INITIAL_XRP);
BEAST_EXPECT(initialXRP == "100000000000000000");
auto const initialXRPPlus1 = to_string(INITIAL_XRP + 1);
BEAST_EXPECT(initialXRPPlus1 == "100000000000000001");
{
testcase("Assets Maximum: XRP");
PrettyAsset const xrpAsset = xrpIssue();
auto [tx, keylet] =
vault.create({.owner = owner, .asset = xrpAsset});
tx[sfData] = "4D65746144617461";
tx[sfAssetsMaximum] = maxInt64;
env(tx, ter(tefEXCEPTION), THISLINE);
env.close();
tx[sfAssetsMaximum] = initialXRPPlus1;
env(tx, ter(tefEXCEPTION), THISLINE);
env.close();
tx[sfAssetsMaximum] = initialXRP;
env(tx, THISLINE);
env.close();
tx[sfAssetsMaximum] = maxInt64Plus1;
env(tx, ter(tefEXCEPTION), THISLINE);
env.close();
// This value will be rounded
auto const insertAt = maxInt64Plus1.size() - 3;
auto const decimalTest = maxInt64Plus1.substr(0, insertAt) + "." +
maxInt64Plus1.substr(insertAt); // (max int64+1) / 1000
BEAST_EXPECT(decimalTest == "9223372036854775.808");
tx[sfAssetsMaximum] = decimalTest;
auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
env(tx, THISLINE);
env.close();
auto const vaultSle = env.le(newKeylet);
if (!BEAST_EXPECT(vaultSle))
return;
BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) == 9223372036854776);
}
{
testcase("Assets Maximum: MPT");
PrettyAsset const mptAsset = [&]() {
MPTTester mptt{env, issuer, mptInitNoFund};
mptt.create(
{.flags =
tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
env.close();
PrettyAsset const mptAsset = mptt["MPT"];
mptt.authorize({.account = owner});
env.close();
return mptAsset;
}();
env(pay(issuer, owner, mptAsset(100'000)), THISLINE);
env.close();
auto [tx, keylet] =
vault.create({.owner = owner, .asset = mptAsset});
tx[sfData] = "4D65746144617461";
tx[sfAssetsMaximum] = maxInt64;
env(tx, THISLINE);
env.close();
tx[sfAssetsMaximum] = initialXRPPlus1;
env(tx, THISLINE);
env.close();
tx[sfAssetsMaximum] = initialXRP;
env(tx, THISLINE);
env.close();
tx[sfAssetsMaximum] = maxInt64Plus1;
env(tx, ter(tefEXCEPTION), THISLINE);
env.close();
// This value will be rounded
auto const insertAt = maxInt64Plus1.size() - 1;
auto const decimalTest = maxInt64Plus1.substr(0, insertAt) + "." +
maxInt64Plus1.substr(insertAt); // (max int64+1) / 10
BEAST_EXPECT(decimalTest == "922337203685477580.8");
tx[sfAssetsMaximum] = decimalTest;
auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
env(tx, THISLINE);
env.close();
auto const vaultSle = env.le(newKeylet);
if (!BEAST_EXPECT(vaultSle))
return;
BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) == 922337203685477581);
}
{
testcase("Assets Maximum: IOU");
// Almost anything goes with IOUs
PrettyAsset iouAsset = issuer["IOU"];
env.trust(iouAsset(1000), owner);
env(pay(issuer, owner, iouAsset(200)));
env.close();
auto [tx, keylet] =
vault.create({.owner = owner, .asset = iouAsset});
tx[sfData] = "4D65746144617461";
tx[sfAssetsMaximum] = maxInt64;
env(tx, THISLINE);
env.close();
tx[sfAssetsMaximum] = initialXRPPlus1;
env(tx, THISLINE);
env.close();
tx[sfAssetsMaximum] = initialXRP;
env(tx, THISLINE);
env.close();
tx[sfAssetsMaximum] = maxInt64Plus1;
env(tx, THISLINE);
env.close();
tx[sfAssetsMaximum] = "1000000000000000e80";
env.close();
tx[sfAssetsMaximum] = "1000000000000000e-96";
env.close();
// These values will be rounded to 15 significant digits
{
auto const insertAt = maxInt64Plus1.size() - 1;
auto const decimalTest = maxInt64Plus1.substr(0, insertAt) +
"." + maxInt64Plus1.substr(insertAt); // (max int64+1) / 10
BEAST_EXPECT(decimalTest == "922337203685477580.8");
tx[sfAssetsMaximum] = decimalTest;
auto const newKeylet =
keylet::vault(owner.id(), env.seq(owner));
env(tx, THISLINE);
env.close();
auto const vaultSle = env.le(newKeylet);
if (!BEAST_EXPECT(vaultSle))
return;
BEAST_EXPECT(
(vaultSle->at(sfAssetsMaximum) ==
Number{9223372036854776, 2, Number::normalized{}}));
}
{
tx[sfAssetsMaximum] =
"9223372036854775807e40"; // max int64 * 10^40
auto const newKeylet =
keylet::vault(owner.id(), env.seq(owner));
env(tx, THISLINE);
env.close();
auto const vaultSle = env.le(newKeylet);
if (!BEAST_EXPECT(vaultSle))
return;
BEAST_EXPECT(
(vaultSle->at(sfAssetsMaximum) ==
Number{9223372036854776, 43, Number::normalized{}}));
}
{
tx[sfAssetsMaximum] =
"9223372036854775807e-40"; // max int64 * 10^-40
auto const newKeylet =
keylet::vault(owner.id(), env.seq(owner));
env(tx, THISLINE);
env.close();
auto const vaultSle = env.le(newKeylet);
if (!BEAST_EXPECT(vaultSle))
return;
BEAST_EXPECT(
(vaultSle->at(sfAssetsMaximum) ==
Number{9223372036854776, -37, Number::normalized{}}));
}
{
tx[sfAssetsMaximum] =
"9223372036854775807e-100"; // max int64 * 10^-100
auto const newKeylet =
keylet::vault(owner.id(), env.seq(owner));
env(tx, THISLINE);
env.close();
// Field 'AssetsMaximum' may not be explicitly set to default.
auto const vaultSle = env.le(newKeylet);
if (!BEAST_EXPECT(vaultSle))
return;
BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) == numZero);
}
// What _can't_ IOUs do?
// 1. Exceed maximum exponent / offset
tx[sfAssetsMaximum] = "1000000000000000e81";
env(tx, ter(tefEXCEPTION), THISLINE);
env.close();
// 2. Mantissa larger than uint64 max
try
{
tx[sfAssetsMaximum] =
"18446744073709551617e5"; // uint64 max + 1
env(tx, THISLINE);
BEAST_EXPECT(false);
}
catch (parse_error const& e)
{
using namespace std::string_literals;
BEAST_EXPECT(
e.what() ==
"invalidParamsField 'tx_json.AssetsMaximum' has invalid "
"data."s);
}
}
}
public:
void
run() override
@@ -5802,6 +6059,7 @@ public:
testDelegate();
testVaultClawbackBurnShares();
testVaultClawbackAssets();
testAssetsMaximum();
}
};

View File

@@ -15,15 +15,8 @@
#include <xrpl/protocol/Units.h>
#include <xrpl/protocol/jss.h>
#include <vector>
#if (defined(__clang_major__) && __clang_major__ < 15)
#include <experimental/source_location>
using source_location = std::experimental::source_location;
#else
#include <source_location>
using std::source_location;
#endif
#include <vector>
namespace xrpl {
namespace test {
@@ -640,7 +633,7 @@ checkMetrics(
std::size_t expectedPerLedger,
std::uint64_t expectedMinFeeLevel = baseFeeLevel.fee(),
std::uint64_t expectedMedFeeLevel = minEscalationFeeLevel.fee(),
source_location const location = source_location::current())
std::source_location const location = std::source_location::current())
{
int line = location.line();
char const* file = location.file_name();

View File

@@ -14,13 +14,7 @@
#include <xrpl/protocol/STXChainBridge.h>
#include <xrpl/protocol/jss.h>
#if (defined(__clang_major__) && __clang_major__ < 15)
#include <experimental/source_location>
using source_location = std::experimental::source_location;
#else
#include <source_location>
using std::source_location;
#endif
namespace xrpl {
namespace test {
@@ -114,7 +108,7 @@ class LedgerEntry_test : public beast::unit_test::suite
Json::Value const& jv,
std::string const& err,
std::string const& msg,
source_location const location = source_location::current())
std::source_location const location = std::source_location::current())
{
if (BEAST_EXPECT(jv.isMember(jss::status)))
BEAST_EXPECTS(
@@ -297,7 +291,7 @@ class LedgerEntry_test : public beast::unit_test::suite
FieldType const typeID,
std::string const& expectedError,
bool required = true,
source_location const location = source_location::current())
std::source_location const location = std::source_location::current())
{
forAllApiVersions([&, this](unsigned apiVersion) {
if (required)
@@ -350,7 +344,7 @@ class LedgerEntry_test : public beast::unit_test::suite
FieldType typeID,
std::string const& expectedError,
bool required = true,
source_location const location = source_location::current())
std::source_location const location = std::source_location::current())
{
forAllApiVersions([&, this](unsigned apiVersion) {
if (required)
@@ -407,7 +401,7 @@ class LedgerEntry_test : public beast::unit_test::suite
runLedgerEntryTest(
test::jtx::Env& env,
Json::StaticString const& parentField,
source_location const location = source_location::current())
std::source_location const location = std::source_location::current())
{
testMalformedField(
env,
@@ -431,7 +425,7 @@ class LedgerEntry_test : public beast::unit_test::suite
test::jtx::Env& env,
Json::StaticString const& parentField,
std::vector<Subfield> const& subfields,
source_location const location = source_location::current())
std::source_location const location = std::source_location::current())
{
testMalformedField(
env,

View File

@@ -203,11 +203,7 @@ public:
boost::asio::signal_set m_signals;
// Once we get C++20, we could use `std::atomic_flag` for `isTimeToStop`
// and eliminate the need for the condition variable and the mutex.
std::condition_variable stoppingCondition_;
mutable std::mutex stoppingMutex_;
std::atomic<bool> isTimeToStop = false;
std::atomic_flag isTimeToStop;
std::atomic<bool> checkSigs_;
@@ -1539,10 +1535,7 @@ ApplicationImp::run()
getLoadManager().activateStallDetector();
}
{
std::unique_lock<std::mutex> lk{stoppingMutex_};
stoppingCondition_.wait(lk, [this] { return isTimeToStop.load(); });
}
isTimeToStop.wait(false, std::memory_order_relaxed);
JLOG(m_journal.debug()) << "Application stopping";
@@ -1629,14 +1622,14 @@ ApplicationImp::run()
void
ApplicationImp::signalStop(std::string msg)
{
if (!isTimeToStop.exchange(true))
if (!isTimeToStop.test_and_set(std::memory_order_acquire))
{
if (msg.empty())
JLOG(m_journal.warn()) << "Server stopping";
else
JLOG(m_journal.warn()) << "Server stopping: " << msg;
stoppingCondition_.notify_all();
isTimeToStop.notify_all();
}
}
@@ -1655,7 +1648,7 @@ ApplicationImp::checkSigs(bool check)
bool
ApplicationImp::isStopping() const
{
return isTimeToStop.load();
return isTimeToStop.test(std::memory_order_relaxed);
}
int