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
10 changed files with 27 additions and 173 deletions

View File

@@ -10,7 +10,6 @@
#include <cctype>
#include <iterator>
#include <string>
#include <string_view>
#include <vector>
namespace beast {
@@ -182,7 +181,7 @@ split_commas(FwdIt first, FwdIt last)
template <class Result = std::vector<std::string>>
Result
split_commas(std::string_view const& s)
split_commas(boost::beast::string_view const& s)
{
return split_commas(s.begin(), s.end());
}

View File

@@ -1,19 +1,20 @@
#pragma once
#include <boost/beast/core/string.hpp>
#include <functional>
#include <string>
#include <string_view>
namespace Json {
class Value;
using Output = std::function<void(std::string_view const&)>;
using Output = std::function<void(boost::beast::string_view const&)>;
inline Output
stringOutput(std::string& s)
{
return [&](std::string_view const& b) { s.append(b.data(), b.size()); };
return [&](boost::beast::string_view const& b) { s.append(b.data(), b.size()); };
}
/** Writes a minimal representation of a Json value to an Output in O(n) time.

View File

@@ -17,7 +17,6 @@
#include <functional>
#include <list>
#include <string_view>
namespace xrpl {
@@ -49,7 +48,8 @@ private:
bool ping_active_ = false;
boost::beast::websocket::ping_data payload_;
error_code ec_;
std::function<void(boost::beast::websocket::frame_type, std::string_view)> control_callback_;
std::function<void(boost::beast::websocket::frame_type, boost::beast::string_view)>
control_callback_;
public:
template <class Body, class Headers>
@@ -137,7 +137,7 @@ protected:
on_ping(error_code const& ec);
void
on_ping_pong(boost::beast::websocket::frame_type kind, std::string_view payload);
on_ping_pong(boost::beast::websocket::frame_type kind, boost::beast::string_view payload);
void
on_timer(error_code ec);
@@ -414,11 +414,11 @@ template <class Handler, class Impl>
void
BaseWSPeer<Handler, Impl>::on_ping_pong(
boost::beast::websocket::frame_type kind,
std::string_view payload)
boost::beast::string_view payload)
{
if (kind == boost::beast::websocket::frame_type::pong)
{
std::string_view const p(payload_.begin(), payload_.size());
boost::beast::string_view const p(payload_.begin());
if (payload == p)
{
close_on_timer_ = false;

View File

@@ -8,7 +8,6 @@
#include <set>
#include <stack>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
@@ -88,14 +87,14 @@ public:
}
void
output(std::string_view const& bytes)
output(boost::beast::string_view const& bytes)
{
markStarted();
output_(bytes);
}
void
stringOutput(std::string_view const& bytes)
stringOutput(boost::beast::string_view const& bytes)
{
markStarted();
std::size_t position = 0, writtenUntil = 0;

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

@@ -56,7 +56,7 @@ to_string(ProtocolVersion const& p)
}
std::vector<ProtocolVersion>
parseProtocolVersions(std::string_view const& value)
parseProtocolVersions(boost::beast::string_view const& value)
{
static boost::regex const re(
"^" // start of line
@@ -126,7 +126,7 @@ negotiateProtocolVersion(std::vector<ProtocolVersion> const& versions)
}
std::optional<ProtocolVersion>
negotiateProtocolVersion(std::string_view const& versions)
negotiateProtocolVersion(boost::beast::string_view const& versions)
{
auto const them = parseProtocolVersions(versions);

View File

@@ -1,9 +1,10 @@
#pragma once
#include <boost/beast/core/string.hpp>
#include <cstdint>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
@@ -38,7 +39,7 @@ to_string(ProtocolVersion const& p);
no duplicates and will be sorted in ascending protocol order.
*/
std::vector<ProtocolVersion>
parseProtocolVersions(std::string_view const& s);
parseProtocolVersions(boost::beast::string_view const& s);
/** Given a list of supported protocol versions, choose the one we prefer. */
std::optional<ProtocolVersion>
@@ -46,7 +47,7 @@ negotiateProtocolVersion(std::vector<ProtocolVersion> const& versions);
/** Given a list of supported protocol versions, choose the one we prefer. */
std::optional<ProtocolVersion>
negotiateProtocolVersion(std::string_view const& versions);
negotiateProtocolVersion(boost::beast::string_view const& versions);
/** The list of all the protocol versions we support. */
std::string const&

View File

@@ -33,7 +33,6 @@
#include <algorithm>
#include <memory>
#include <stdexcept>
#include <string_view>
namespace xrpl {
@@ -230,7 +229,7 @@ ServerHandler::onHandoff(
static inline Json::Output
makeOutput(Session& session)
{
return [&](std::string_view const& b) { session.write(b.data(), b.size()); };
return [&](boost::beast::string_view const& b) { session.write(b.data(), b.size()); };
}
static std::map<std::string, std::string>
@@ -531,14 +530,11 @@ ServerHandler::processSession(
makeOutput(*session),
coro,
forwardedFor(session->request()),
[&]() -> std::string_view {
[&] {
auto const iter = session->request().find("X-User");
if (iter != session->request().end())
{
auto const val = iter->value();
return std::string_view(val.data(), val.size());
}
return std::string_view{};
return iter->value();
return boost::beast::string_view{};
}());
if (beast::rfc2616::is_keep_alive(session->request()))