Compare commits

..

26 Commits

Author SHA1 Message Date
Bart
b6a09a4d2c release: Bump version to 3.2.0-b3 2026-04-01 13:27:58 -04:00
Valentin Balaschenko
3adad49c86 fix: Remove fatal assertion on Linux thread name truncation (#6690) 2026-04-01 13:27:23 -04:00
Alex Kremer
f04943fc50 chore: Enable clang-tidy coreguidelines checks (#6698)
Co-authored-by: Ayaz Salikhov <mathbunnyru@users.noreply.github.com>
2026-04-01 13:27:23 -04:00
Ayaz Salikhov
ab7f683b2f ci: Allow uploading artifacts for XRPLF org (#6702) 2026-04-01 13:27:23 -04:00
Vito Tumas
14749fdfb0 fix: Enforce aggregate MaximumAmount in multi-send MPT (#6644)
Co-authored-by: xrplf-ai-reviewer[bot] <266832837+xrplf-ai-reviewer[bot]@users.noreply.github.com>
2026-04-01 13:27:22 -04:00
Ayaz Salikhov
c6ddf6aa1c chore: Use nudb recipe from the upstream (#6701) 2026-04-01 13:27:22 -04:00
Mayukha Vadari
ee3a08c8d4 fix: Fix previous ledger size typo in RCLConsensus (#6696) 2026-04-01 13:27:22 -04:00
Alex Kremer
2dc705bd99 chore: Enable clang-tidy misc checks (#6655) 2026-04-01 13:27:22 -04:00
Ayaz Salikhov
7315c57edc ci: Use pull_request_target to check for signed commits (#6697) 2026-04-01 13:27:22 -04:00
Bart
5a66086fce chore: Remove unnecessary clang-format off/on directives (#6682)
Co-authored-by: Bart <11445373+bthomee@users.noreply.github.com>
2026-04-01 13:27:21 -04:00
Pratik Mankawde
682dda8bfc fix: Fix Workers::stop() race between m_allPaused and m_runningTaskCount (#6574)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 13:27:21 -04:00
Bart
aac17f588a release: Bump version to 3.2.0-b2 2026-03-30 14:52:51 -04:00
Ayaz Salikhov
cbc09b2999 ci: Only publish docs in public repos (#6687) 2026-03-30 14:52:09 -04:00
Alex Kremer
453d94da17 chore: Enable remaining clang-tidy performance checks (#6648)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-30 14:52:09 -04:00
Jingchen
b36aedb4d5 refactor: Address PR comments after the modularisation PRs (#6389)
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Co-authored-by: Bart <bthomee@users.noreply.github.com>
2026-03-30 14:52:09 -04:00
Alex Kremer
18540c97a5 chore: Fix clang-tidy header filter (#6686) 2026-03-30 14:52:08 -04:00
dependabot[bot]
8e3d87fce3 ci: [DEPENDABOT] bump actions/deploy-pages from 4.0.5 to 5.0.0 (#6684)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-30 14:52:08 -04:00
dependabot[bot]
b83dc9aa16 ci: [DEPENDABOT] bump codecov/codecov-action from 5.5.3 to 6.0.0 (#6685)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-30 14:52:08 -04:00
Pratik Mankawde
0c76bf991a fix: Guard Coro::resume() against completed coroutines (#6608)
Signed-off-by: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 14:52:08 -04:00
Vito Tumas
29aba28f5b refactor: Split LoanInvariant into LoanBrokerInvariant and LoanInvariant (#6674) 2026-03-30 14:52:08 -04:00
Ayaz Salikhov
b3f14c4052 ci: Don't publish docs on release branches (#6673) 2026-03-30 14:52:08 -04:00
Jingchen
e26624dcd1 refactor: Make function naming in ServiceRegistry consistent (#6390)
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
Co-authored-by: Ed Hennis <ed@ripple.com>
2026-03-30 14:52:08 -04:00
Valentin Balaschenko
885f7b8c33 chore: Shorten job names to stay within Linux 15-char thread limit (#6669) 2026-03-30 14:52:07 -04:00
Vito Tumas
dcf973bc50 fix: Improve loan invariant message (#6668) 2026-03-30 14:52:07 -04:00
Ayaz Salikhov
68596f60d8 ci: Upload artifacts only in public repositories (#6670) 2026-03-30 14:52:07 -04:00
Bart
aac64d3b85 Update version (#6672) 2026-03-26 09:50:04 -04:00
12 changed files with 175 additions and 260 deletions

View File

@@ -64,7 +64,7 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
if os["distro_version"] == "bookworm":
if (
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-13"
and build_type == "Debug"
and build_type == "Release"
and architecture["platform"] == "linux/amd64"
):
cmake_args = f"-DUNIT_TEST_REFERENCE_FEE=500 {cmake_args}"
@@ -197,7 +197,7 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
# linux/amd64
if (
f"{os['distro_name']}-{os['distro_version']}" == "debian-bookworm"
and f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-13"
and f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-15"
and build_type == "Debug"
and architecture["platform"] == "linux/amd64"
):
@@ -236,36 +236,21 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
# names get truncated.
# Add Address and Thread (both coupled with UB) sanitizers for specific bookworm distros.
# GCC-Asan rippled-embedded tests are failing because of https://github.com/google/sanitizers/issues/856
if os[
"distro_version"
] == "bookworm" and f"{os['compiler_name']}-{os['compiler_version']}" in [
"gcc-15",
"clang-20",
]:
# Add ASAN configuration.
if (
os["distro_version"] == "bookworm"
and f"{os['compiler_name']}-{os['compiler_version']}" == "clang-20"
):
# Add ASAN + UBSAN configuration.
configurations.append(
{
"config_name": config_name + "-asan",
"config_name": config_name + "-asan-ubsan",
"cmake_args": cmake_args,
"cmake_target": cmake_target,
"build_only": build_only,
"build_type": build_type,
"os": os,
"architecture": architecture,
"sanitizers": "address",
}
)
# Add UBSAN configuration.
configurations.append(
{
"config_name": config_name + "-ubsan",
"cmake_args": cmake_args,
"cmake_target": cmake_target,
"build_only": build_only,
"build_type": build_type,
"os": os,
"architecture": architecture,
"sanitizers": "undefinedbehavior",
"sanitizers": "address,undefinedbehavior",
}
)
# TSAN is deactivated due to seg faults with latest compilers.

View File

@@ -67,10 +67,8 @@ private:
}
else
{
for (; elapsed > 0; --elapsed)
{
while (elapsed--)
m_value -= (m_value + Window - 1) / Window;
}
}
}

View File

@@ -44,7 +44,7 @@ public:
: work_(boost::asio::make_work_guard(ios_))
{
threads_.reserve(concurrency);
for (std::size_t i = 0; i < concurrency; ++i)
while (concurrency--)
threads_.emplace_back([&] { ios_.run(); });
}

View File

@@ -53,9 +53,8 @@ read_varint(void const* buf, std::size_t buflen, std::size_t& t)
return 1;
}
auto const used = n;
while (n > 0)
while (n--)
{
--n;
auto const d = p[n];
auto const t0 = t;
t *= 127;

View File

@@ -11,6 +11,7 @@ float-cast-overflow:external
float-divide-by-zero:external
function:external
implicit-integer-sign-change:external
implicit-signed-integer-truncation::external
implicit-signed-integer-truncation:external
implicit-unsigned-integer-truncation:external
integer-divide-by-zero:external
@@ -70,15 +71,145 @@ vla-bound:boost
vptr_check:boost
vptr:boost
# Google protobuf - intentional overflows in hash functions
# Google protobuf
undefined:protobuf
# Suppress UBSan errors in rippled code by source file path
undefined:src/libxrpl/basics/base64.cpp
undefined:src/libxrpl/basics/Number.cpp
undefined:src/libxrpl/beast/utility/beast_Journal.cpp
undefined:src/libxrpl/crypto/RFC1751.cpp
undefined:src/libxrpl/ledger/ApplyView.cpp
undefined:src/libxrpl/ledger/View.cpp
undefined:src/libxrpl/protocol/Permissions.cpp
undefined:src/libxrpl/protocol/STAmount.cpp
undefined:src/libxrpl/protocol/STPathSet.cpp
undefined:src/libxrpl/protocol/tokens.cpp
undefined:src/libxrpl/shamap/SHAMap.cpp
undefined:src/test/app/Batch_test.cpp
undefined:src/test/app/Invariants_test.cpp
undefined:src/test/app/NFToken_test.cpp
undefined:src/test/app/Offer_test.cpp
undefined:src/test/app/Path_test.cpp
undefined:src/test/basics/XRPAmount_test.cpp
undefined:src/test/beast/LexicalCast_test.cpp
undefined:src/test/jtx/impl/acctdelete.cpp
undefined:src/test/ledger/SkipList_test.cpp
undefined:src/test/rpc/Subscribe_test.cpp
undefined:src/tests/libxrpl/basics/RangeSet.cpp
undefined:src/xrpld/app/main/BasicApp.cpp
undefined:src/xrpld/app/main/BasicApp.cpp
undefined:src/xrpld/app/misc/detail/AmendmentTable.cpp
undefined:src/xrpld/app/misc/NetworkOPs.cpp
undefined:src/libxrpl/json/json_value.cpp
undefined:src/xrpld/app/paths/detail/StrandFlow.h
undefined:src/xrpld/app/tx/detail/NFTokenMint.cpp
undefined:src/xrpld/app/tx/detail/OracleSet.cpp
undefined:src/xrpld/core/detail/JobQueue.cpp
undefined:src/xrpld/core/detail/Workers.cpp
undefined:src/xrpld/rpc/detail/Role.cpp
undefined:src/xrpld/rpc/handlers/GetAggregatePrice.cpp
undefined:xrpl/basics/base_uint.h
undefined:xrpl/basics/DecayingSample.h
undefined:xrpl/beast/test/yield_to.h
undefined:xrpl/beast/xor_shift_engine.h
undefined:xrpl/nodestore/detail/varint.h
undefined:xrpl/peerfinder/detail/Counts.h
undefined:xrpl/protocol/nft.h
# basic_string.h:483:51: runtime error: unsigned integer overflow
unsigned-integer-overflow:basic_string.h
unsigned-integer-overflow:bits/chrono.h
unsigned-integer-overflow:bits/random.h
unsigned-integer-overflow:bits/random.tcc
unsigned-integer-overflow:bits/stl_algobase.h
unsigned-integer-overflow:bits/uniform_int_dist.h
unsigned-integer-overflow:string_view
# runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'std::size_t' (aka 'unsigned long')
unsigned-integer-overflow:src/libxrpl/basics/base64.cpp
unsigned-integer-overflow:src/libxrpl/basics/Number.cpp
unsigned-integer-overflow:src/libxrpl/crypto/RFC1751.cpp
unsigned-integer-overflow:rc/libxrpl/json/json_value.cpp
unsigned-integer-overflow:src/libxrpl/ledger/ApplyView.cpp
unsigned-integer-overflow:src/libxrpl/ledger/View.cpp
unsigned-integer-overflow:src/libxrpl/protocol/Permissions.cpp
unsigned-integer-overflow:src/libxrpl/protocol/STAmount.cpp
unsigned-integer-overflow:src/libxrpl/protocol/STPathSet.cpp
unsigned-integer-overflow:src/libxrpl/protocol/tokens.cpp
unsigned-integer-overflow:src/libxrpl/shamap/SHAMap.cpp
unsigned-integer-overflow:src/test/app/Batch_test.cpp
unsigned-integer-overflow:src/test/app/Invariants_test.cpp
unsigned-integer-overflow:src/test/app/NFToken_test.cpp
unsigned-integer-overflow:src/test/app/Offer_test.cpp
unsigned-integer-overflow:src/test/app/Path_test.cpp
unsigned-integer-overflow:src/test/basics/XRPAmount_test.cpp
unsigned-integer-overflow:src/test/beast/LexicalCast_test.cpp
unsigned-integer-overflow:src/test/jtx/impl/acctdelete.cpp
unsigned-integer-overflow:src/test/ledger/SkipList_test.cpp
unsigned-integer-overflow:src/test/rpc/Subscribe_test.cpp
unsigned-integer-overflow:src/tests/libxrpl/basics/RangeSet.cpp
unsigned-integer-overflow:src/xrpld/app/main/BasicApp.cpp
unsigned-integer-overflow:src/xrpld/app/misc/detail/AmendmentTable.cpp
unsigned-integer-overflow:src/xrpld/app/misc/NetworkOPs.cpp
unsigned-integer-overflow:src/xrpld/app/paths/detail/StrandFlow.h
unsigned-integer-overflow:src/xrpld/app/tx/detail/NFTokenMint.cpp
unsigned-integer-overflow:src/xrpld/app/tx/detail/OracleSet.cpp
unsigned-integer-overflow:src/xrpld/rpc/detail/Role.cpp
unsigned-integer-overflow:src/xrpld/rpc/handlers/GetAggregatePrice.cpp
unsigned-integer-overflow:xrpl/basics/base_uint.h
unsigned-integer-overflow:xrpl/basics/DecayingSample.h
unsigned-integer-overflow:xrpl/beast/test/yield_to.h
unsigned-integer-overflow:xrpl/beast/xor_shift_engine.h
unsigned-integer-overflow:xrpl/nodestore/detail/varint.h
unsigned-integer-overflow:xrpl/peerfinder/detail/Counts.h
unsigned-integer-overflow:xrpl/protocol/nft.h
# Rippled intentional overflows and operations
# STAmount uses intentional negation of INT64_MIN and overflow in arithmetic
signed-integer-overflow:src/libxrpl/protocol/STAmount.cpp
unsigned-integer-overflow:src/libxrpl/protocol/STAmount.cpp
# XRPAmount test intentional overflows
signed-integer-overflow:src/test/basics/XRPAmount_test.cpp
# Peerfinder intentional overflow in counter arithmetic
unsigned-integer-overflow:src/xrpld/peerfinder/detail/Counts.h
# Signed integer overflow suppressions
signed-integer-overflow:src/test/beast/LexicalCast_test.cpp
# External library suppressions
unsigned-integer-overflow:nudb/detail/xxhash.hpp
# Loan_test.cpp intentional underflow in test arithmetic
unsigned-integer-overflow:src/test/app/Loan_test.cpp
undefined:src/test/app/Loan_test.cpp
# Source tree restructured paths (libxrpl/tx/transactors/)
# These duplicate the xrpld/app/tx/detail entries above for the new layout
unsigned-integer-overflow:src/libxrpl/tx/transactors/oracle/OracleSet.cpp
undefined:src/libxrpl/tx/transactors/oracle/OracleSet.cpp
unsigned-integer-overflow:src/libxrpl/tx/transactors/nft/NFTokenMint.cpp
undefined:src/libxrpl/tx/transactors/nft/NFTokenMint.cpp
# Protobuf intentional overflows in hash functions
# Protobuf uses intentional unsigned overflow for hash computation (stringpiece.h:393)
unsigned-integer-overflow:google/protobuf/stubs/stringpiece.h
# gRPC intentional overflows in timer calculations
# gRPC intentional overflows
# gRPC uses intentional overflow in timer calculations
unsigned-integer-overflow:grpc
unsigned-integer-overflow:timer_manager.cc
# RocksDB intentional unsigned integer overflows in hash functions and CRC calculations
# Standard library intentional overflows
# These are intentional overflows in random number generation and character conversion
unsigned-integer-overflow:__random/seed_seq.h
unsigned-integer-overflow:__charconv/traits.h
# Suppress errors in RocksDB
# RocksDB uses intentional unsigned integer overflows in hash functions and CRC calculations
unsigned-integer-overflow:rocks*/*/util/xxhash.h
unsigned-integer-overflow:rocks*/*/util/xxph3.h
unsigned-integer-overflow:rocks*/*/util/hash.cc
@@ -90,14 +221,13 @@ unsigned-integer-overflow:rocks*/*/table/format.cc
unsigned-integer-overflow:rocks*/*/table/block_based/block_based_table_builder.cc
unsigned-integer-overflow:rocks*/*/table/block_based/reader_common.cc
unsigned-integer-overflow:rocks*/*/db/version_set.cc
# RocksDB misaligned loads (intentional for performance on ARM64)
alignment:rocks*/*/util/crc32c_arm64.cc
undefined:rocks.*/*/util/crc32c_arm64.cc
undefined:rocks.*/*/util/xxhash.h
# nudb intentional overflows in hash functions
unsigned-integer-overflow:nudb/detail/xxhash.hpp
alignment:nudb/detail/xxhash.hpp
undefined:nudb
# Snappy compression library intentional overflows
unsigned-integer-overflow:snappy.cc
@@ -109,39 +239,10 @@ unsigned-integer-overflow:absl/base/internal/low_level_alloc.cc
unsigned-integer-overflow:absl/hash/internal/hash.h
unsigned-integer-overflow:absl/container/internal/raw_hash_set.h
# Standard library intentional overflows
unsigned-integer-overflow:basic_string.h
unsigned-integer-overflow:bits/chrono.h
unsigned-integer-overflow:bits/random.h
unsigned-integer-overflow:bits/random.tcc
unsigned-integer-overflow:bits/stl_algobase.h
unsigned-integer-overflow:bits/uniform_int_dist.h
unsigned-integer-overflow:string_view
unsigned-integer-overflow:__random/seed_seq.h
unsigned-integer-overflow:__charconv/traits.h
# Standard library intentional overflows in chrono duration arithmetic
unsigned-integer-overflow:__chrono/duration.h
# =============================================================================
# Rippled code suppressions
# =============================================================================
# Signed integer negation (-value) in amount types.
# INT64_MIN cannot occur in practice due to domain invariants (mantissa ranges
# are well within int64_t bounds), but UBSan flags the pattern as potential
# signed overflow.
signed-integer-overflow:IOUAmount
signed-integer-overflow:XRPAmount
signed-integer-overflow:MPTAmount
signed-integer-overflow:STAmount
# STAmount::operator+ signed addition — operands are bounded by total supply
# (~10^17 for XRP, ~10^18 for MPT) so overflow cannot occur in practice.
signed-integer-overflow:operator+*STAmount*
# STAmount::getRate uses unsigned shift and addition
unsigned-integer-overflow:getRate*
# STAmount::serialize uses unsigned bitwise operations
unsigned-integer-overflow:*STAmount*serialize*
# nft::cipheredTaxon uses intentional uint32 wraparound (LCG permutation)
unsigned-integer-overflow:cipheredTaxon
# Suppress undefined errors in RocksDB and nudb
undefined:rocks.*/*/util/crc32c_arm64.cc
undefined:rocks.*/*/util/xxhash.h
undefined:nudb

View File

@@ -107,7 +107,7 @@ encode(void* dest, void const* src, std::size_t len)
char const* in = static_cast<char const*>(src);
auto const tab = base64::get_alphabet();
for (auto n = len / 3; n > 0; --n)
for (auto n = len / 3; n != 0u; --n)
{
*out++ = tab[(in[0] & 0xfc) >> 2];
*out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)];

View File

@@ -23,7 +23,7 @@ namespace {
// and follow the format described at http://semver.org/
//------------------------------------------------------------------------------
// clang-format off
char const* const versionString = "3.2.0-b0"
char const* const versionString = "3.2.0-b3"
// clang-format on
;

View File

@@ -43,10 +43,10 @@ VaultWithdraw::preclaim(PreclaimContext const& ctx)
if (!vault)
return tecNO_ENTRY;
auto const amount = ctx.tx[sfAmount];
auto const assets = ctx.tx[sfAmount];
auto const vaultAsset = vault->at(sfAsset);
auto const vaultShare = vault->at(sfShareMPTID);
if (amount.asset() != vaultAsset && amount.asset() != vaultShare)
if (assets.asset() != vaultAsset && assets.asset() != vaultShare)
return tecWRONG_ASSET;
auto const& vaultAccount = vault->at(sfAccount);
@@ -67,53 +67,8 @@ VaultWithdraw::preclaim(PreclaimContext const& ctx)
// LCOV_EXCL_STOP
}
if (ctx.view.rules().enabled(fixSecurity3_1_3) && amount.asset() == vaultShare)
{
// Post-fixSecurity3_1_3: if the user specified shares, convert
// to the equivalent asset amount before checking withdrawal
// limits. Pre-amendment the limit check was skipped for
// share-denominated withdrawals.
auto const sleIssuance = ctx.view.read(keylet::mptIssuance(vaultShare));
if (!sleIssuance)
{
// LCOV_EXCL_START
JLOG(ctx.j.error()) << "VaultWithdraw: missing issuance of vault shares.";
return tefINTERNAL;
// LCOV_EXCL_STOP
}
try
{
auto const maybeAssets = sharesToAssetsWithdraw(vault, sleIssuance, amount);
if (!maybeAssets)
return tefINTERNAL; // LCOV_EXCL_LINE
if (auto const ret = canWithdraw(
ctx.view,
account,
dstAcct,
*maybeAssets,
ctx.tx.isFieldPresent(sfDestinationTag)))
return ret;
}
catch (std::overflow_error const&)
{
// It's easy to hit this exception from Number with large enough Scale
// so we avoid spamming the log and only use debug here.
JLOG(ctx.j.debug()) //
<< "VaultWithdraw: overflow error with"
<< " scale=" << (int)vault->at(sfScale) //
<< ", assetsTotal=" << vault->at(sfAssetsTotal)
<< ", sharesTotal=" << sleIssuance->at(sfOutstandingAmount)
<< ", amount=" << amount.value();
return tecPATH_DRY;
}
}
else
{
if (auto const ret = canWithdraw(ctx.view, ctx.tx))
return ret;
}
if (auto const ret = canWithdraw(ctx.view, ctx.tx))
return ret;
// If sending to Account (i.e. not a transfer), we will also create (only
// if authorized) a trust line or MPToken as needed, in doApply().

View File

@@ -5231,102 +5231,6 @@ class Vault_test : public beast::unit_test::suite
}
}
// Reproduction: canWithdraw IOU limit check bypassed when
// withdrawal amount is specified in shares (MPT) rather than in assets.
void
testBug6_LimitBypassWithShares()
{
using namespace test::jtx;
testcase("Bug6 - limit bypass with share-denominated withdrawal");
auto const allAmendments = testable_amendments() | featureSingleAssetVault;
for (auto const& features : {allAmendments, allAmendments - fixSecurity3_1_3})
{
bool const withFix = features[fixSecurity3_1_3];
Env env{*this, features};
Account const owner{"owner"};
Account const issuer{"issuer"};
Account const depositor{"depositor"};
Account const charlie{"charlie"};
Vault const vault{env};
env.fund(XRP(1000), issuer, owner, depositor, charlie);
env(fset(issuer, asfAllowTrustLineClawback));
env.close();
PrettyAsset const asset = issuer["IOU"];
env.trust(asset(1000), owner);
env.trust(asset(1000), depositor);
env(pay(issuer, owner, asset(200)));
env(pay(issuer, depositor, asset(200)));
env.close();
// Charlie gets a LOW trustline limit of 5
env.trust(asset(5), charlie);
env.close();
auto const [tx, keylet] = vault.create({.owner = owner, .asset = asset});
env(tx);
env.close();
auto const depositTx =
vault.deposit({.depositor = depositor, .id = keylet.key, .amount = asset(100)});
env(depositTx);
env.close();
// Get the share MPT info
auto const vaultSle = env.le(keylet);
if (!BEAST_EXPECT(vaultSle))
return;
auto const mptIssuanceID = vaultSle->at(sfShareMPTID);
MPTIssue const shares(mptIssuanceID);
PrettyAsset const share(shares);
// CONTROL: Withdraw 10 IOU (asset-denominated) to charlie.
// Charlie's limit is 5, so this should be rejected with tecNO_LINE
// regardless of the amendment.
{
auto withdrawTx =
vault.withdraw({.depositor = depositor, .id = keylet.key, .amount = asset(10)});
withdrawTx[sfDestination] = charlie.human();
env(withdrawTx, ter{tecNO_LINE});
env.close();
}
auto const charlieBalanceBefore = env.balance(charlie, asset.raw().get<Issue>());
// Withdraw the equivalent amount in shares to charlie.
// Post-fix: rejected (tecNO_LINE) because the share amount is
// converted to assets and the trustline limit is checked.
// Pre-fix: succeeds (tesSUCCESS) because the limit check was
// skipped for share-denominated withdrawals.
{
auto withdrawTx = vault.withdraw(
{.depositor = depositor,
.id = keylet.key,
.amount = STAmount(share, 10'000'000)});
withdrawTx[sfDestination] = charlie.human();
env(withdrawTx, ter{withFix ? TER{tecNO_LINE} : TER{tesSUCCESS}});
env.close();
auto const charlieBalanceAfter = env.balance(charlie, asset.raw().get<Issue>());
if (withFix)
{
// Post-fix: charlie's balance is unchanged — the withdrawal
// was correctly rejected despite being share-denominated.
BEAST_EXPECT(charlieBalanceAfter == charlieBalanceBefore);
}
else
{
// Pre-fix: charlie received the assets, bypassing the
// trustline limit.
BEAST_EXPECT(charlieBalanceAfter > charlieBalanceBefore);
}
}
}
}
public:
void
run() override
@@ -5347,7 +5251,6 @@ public:
testVaultClawbackBurnShares();
testVaultClawbackAssets();
testAssetsMaximum();
testBug6_LimitBypassWithShares();
}
};

View File

@@ -19,7 +19,7 @@ public:
testInteger(IntType in)
{
std::string s;
IntType out{}; // Initialize to avoid relying on overflow behavior
IntType out(in + 1);
expect(lexicalCastChecked(s, in));
expect(lexicalCastChecked(out, s));

View File

@@ -9,10 +9,10 @@ BasicApp::BasicApp(std::size_t numberOfThreads)
work_.emplace(boost::asio::make_work_guard(io_context_));
threads_.reserve(numberOfThreads);
for (std::size_t i = 0; i < numberOfThreads; ++i)
while ((numberOfThreads--) != 0u)
{
threads_.emplace_back([this, i]() {
beast::setCurrentThreadName("io svc #" + std::to_string(i));
threads_.emplace_back([this, numberOfThreads]() {
beast::setCurrentThreadName("io svc #" + std::to_string(numberOfThreads));
this->io_context_.run();
});
}

View File

@@ -9,9 +9,6 @@
namespace xrpl {
namespace PeerFinder {
/** Direction of a slot count adjustment. */
enum class CountAdjustment : int { Decrement = -1, Increment = 1 };
/** Manages the count of available connections for the various slots. */
class Counts
{
@@ -20,14 +17,14 @@ public:
void
add(Slot const& s)
{
adjust(s, CountAdjustment::Increment);
adjust(s, 1);
}
/** Removes the slot state and properties from the slot counts. */
void
remove(Slot const& s)
{
adjust(s, CountAdjustment::Decrement);
adjust(s, -1);
}
/** Returns `true` if the slot can become active. */
@@ -214,40 +211,21 @@ public:
//--------------------------------------------------------------------------
private:
/** Increments or decrements a counter based on the adjustment direction. */
template <typename T>
static void
adjustCounter(T& counter, CountAdjustment dir)
{
switch (dir)
{
case CountAdjustment::Increment:
++counter;
break;
case CountAdjustment::Decrement:
--counter;
break;
}
}
// Adjusts counts based on the specified slot, in the direction indicated.
// Using ++/-- instead of += on std::size_t counters avoids UBSan
// unsigned-integer-overflow from implicit conversion of -1 to SIZE_MAX.
// A decrement on a zero counter is a real bug that UBSan should catch.
void
adjust(Slot const& s, CountAdjustment const dir)
adjust(Slot const& s, int const n)
{
if (s.fixed())
adjustCounter(m_fixed, dir);
m_fixed += n;
if (s.reserved())
adjustCounter(m_reserved, dir);
m_reserved += n;
switch (s.state())
{
case Slot::accept:
XRPL_ASSERT(s.inbound(), "xrpl::PeerFinder::Counts::adjust : input is inbound");
adjustCounter(m_acceptCount, dir);
m_acceptCount += n;
break;
case Slot::connect:
@@ -256,28 +234,24 @@ private:
!s.inbound(),
"xrpl::PeerFinder::Counts::adjust : input is not "
"inbound");
adjustCounter(m_attempts, dir);
m_attempts += n;
break;
case Slot::active:
if (s.fixed())
adjustCounter(m_fixed_active, dir);
m_fixed_active += n;
if (!s.fixed() && !s.reserved())
{
if (s.inbound())
{
adjustCounter(m_in_active, dir);
}
m_in_active += n;
else
{
adjustCounter(m_out_active, dir);
}
m_out_active += n;
}
adjustCounter(m_active, dir);
m_active += n;
break;
case Slot::closing:
adjustCounter(m_closingCount, dir);
m_closingCount += n;
break;
// LCOV_EXCL_START