Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/scripts/strategy-matrix/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
# The default CMake target is 'all' for Linux and MacOS and 'install'
# for Windows, but it can get overridden for certain configurations.
cmake_target = "install" if os["distro_name"] == "windows" else "all"
unittest_args = ""

# We build and test all configurations by default, except for Windows in
# Debug, because it is too slow, as well as when code coverage is
Expand Down Expand Up @@ -67,7 +68,7 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
and build_type == "Release"
and architecture["platform"] == "linux/amd64"
):
cmake_args = f"-DUNIT_TEST_REFERENCE_FEE=500 {cmake_args}"
unittest_args = f"{unittest_args} --unittest-fee=500"
skip = False
if (
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-15"
Expand All @@ -87,7 +88,7 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
and build_type == "Release"
and architecture["platform"] == "linux/amd64"
):
cmake_args = f"-DUNIT_TEST_REFERENCE_FEE=1000 {cmake_args}"
unittest_args = f"{unittest_args} --unittest-fee=1000"
skip = False
if (
f"{os['compiler_name']}-{os['compiler_version']}" == "clang-20"
Expand Down Expand Up @@ -245,6 +246,7 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
{
"config_name": config_name + "-asan-ubsan",
"cmake_args": cmake_args,
"unittest_args": unittest_args,
"cmake_target": cmake_target,
"build_only": build_only,
"build_type": build_type,
Expand All @@ -260,6 +262,7 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
{
"config_name": config_name + "-tsan-ubsan",
"cmake_args": cmake_args,
"unittest_args": unittest_args,
"cmake_target": cmake_target,
"build_only": build_only,
"build_type": build_type,
Expand All @@ -273,6 +276,7 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
{
"config_name": config_name,
"cmake_args": cmake_args,
"unittest_args": unittest_args,
"cmake_target": cmake_target,
"build_only": build_only,
"build_type": build_type,
Expand Down
9 changes: 8 additions & 1 deletion .github/workflows/reusable-build-test-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ on:
type: string
default: ""

unittest_args:
description: "Additional arguments to pass to rippled when running tests"
required: false
type: string
default: ""

cmake_target:
description: "The CMake target to build."
required: true
Expand Down Expand Up @@ -260,11 +266,12 @@ jobs:
working-directory: ${{ runner.os == 'Windows' && format('{0}/{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }}
env:
BUILD_NPROC: ${{ steps.nproc.outputs.nproc }}
UNITTEST_ARGS: ${{ inputs.unittest_args }}
run: |
set -o pipefail
# Coverage builds are slower due to instrumentation; use fewer parallel jobs to avoid flakiness
[ "$COVERAGE_ENABLED" = "true" ] && BUILD_NPROC=$(( BUILD_NPROC - 2 ))
./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" 2>&1 | tee unittest.log
./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" ${UNITTEST_ARGS} 2>&1 | tee unittest.log

Comment on lines 270 to 275
Copy link
Copy Markdown

@semgrep-companion-app semgrep-companion-app bot Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using variable interpolation ${{...}} with github context data in a run: step could allow an attacker to inject their own code into the runner. This would allow them to steal secrets and code. github context data can have arbitrary user input and should be treated as untrusted. Instead, use an intermediate environment variable with env: to store the data and use the environment variable in the run: script. Be sure to use double-quotes the environment variable, like this: "$ENVVAR".

🍰 Removed in commit 21315d6 🍰

Comment on lines 270 to 275
Copy link
Copy Markdown

@semgrep-companion-app semgrep-companion-app bot Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using variable interpolation ${{...}} with github context data in a run: step could allow an attacker to inject their own code into the runner. This would allow them to steal secrets and code. github context data can have arbitrary user input and should be treated as untrusted. Instead, use an intermediate environment variable with env: to store the data and use the environment variable in the run: script. Be sure to use double-quotes the environment variable, like this: "$ENVVAR".

Fixed in commit cdcc6b6

- name: Show test failure summary
if: ${{ failure() && !inputs.build_only }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/reusable-build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ jobs:
build_type: ${{ matrix.build_type }}
ccache_enabled: ${{ inputs.ccache_enabled }}
cmake_args: ${{ matrix.cmake_args }}
unittest_args: ${{ matrix.unittest_args }}
cmake_target: ${{ matrix.cmake_target }}
runs_on: ${{ toJSON(matrix.architecture.runner) }}
image: ${{ contains(matrix.architecture.platform, 'linux') && format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}-sha-{4}', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version, matrix.os.image_sha) || '' }}
Expand Down
4 changes: 0 additions & 4 deletions cmake/XrplCore.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -239,10 +239,6 @@ if(xrpld)
add_executable(xrpld)
if(tests)
target_compile_definitions(xrpld PUBLIC ENABLE_TESTS)
target_compile_definitions(
xrpld
PRIVATE UNIT_TEST_REFERENCE_FEE=${UNIT_TEST_REFERENCE_FEE}
)
endif()
target_include_directories(
xrpld
Expand Down
6 changes: 0 additions & 6 deletions cmake/XrplSettings.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,6 @@ option(assert "Enables asserts, even in release builds" OFF)
option(xrpld "Build xrpld" ON)

option(tests "Build tests" ON)
if(tests)
# This setting allows making a separate workflow to test fees other than default 10
if(NOT UNIT_TEST_REFERENCE_FEE)
set(UNIT_TEST_REFERENCE_FEE "10" CACHE STRING "")
endif()
endif()

option(unity "Creates a build using UNITY support in cmake." OFF)
if(unity)
Expand Down
20 changes: 20 additions & 0 deletions include/xrpl/beast/unit_test/runner.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <boost/assert.hpp>

#include <mutex>
#include <optional>
#include <string>

namespace beast {
Expand All @@ -22,6 +23,7 @@ namespace unit_test {
class runner
{
std::string arg_;
std::optional<std::int64_t> referenceFee_;
bool default_ = false;
bool failed_ = false;
bool cond_ = false;
Expand Down Expand Up @@ -54,6 +56,24 @@ class runner
return arg_;
}

/** Set the reference fee (in drops) for tests.

If provided, this value is used in every suite that
does not override it.
*/
void
referenceFee(std::int64_t fee)
{
referenceFee_ = fee;
}

/** Returns the reference fee, if any. */
std::optional<std::int64_t> const&
referenceFee() const
{
return referenceFee_;
}

/** Run the specified suite.
@return `true` if any conditions failed.
*/
Expand Down
10 changes: 10 additions & 0 deletions include/xrpl/beast/unit_test/suite.h
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,16 @@ class suite
return runner_->arg();
}

/** Return the reference fee associated with the runner. */
std::optional<std::int64_t>
referenceFee() const
{
assert(runner_);
if (!runner_)
return {};
return runner_->referenceFee();
}

// DEPRECATED
// @return `true` if the test condition indicates success(a false value)
template <class Condition, class String>
Expand Down
4 changes: 4 additions & 0 deletions include/xrpl/ledger/helpers/NFTokenHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ removeTokenOffersWithLimit(
Keylet const& directory,
std::size_t maxDeletableOffers);

/** Returns tesSUCCESS if NFToken has few enough offers that it can be burned */
TER
notTooManyOffers(ReadView const& view, uint256 const& nftokenID);

/** Finds the specified token in the owner's token directory. */
std::optional<STObject>
findToken(ReadView const& view, AccountID const& owner, uint256 const& nftokenID);
Expand Down
27 changes: 27 additions & 0 deletions src/libxrpl/ledger/helpers/NFTokenHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,33 @@ removeTokenOffersWithLimit(ApplyView& view, Keylet const& directory, std::size_t
return deletedOffersCount;
}

TER
notTooManyOffers(ReadView const& view, uint256 const& nftokenID)
{
std::size_t totalOffers = 0;

{
Dir const buys(view, keylet::nft_buys(nftokenID));
for (auto iter = buys.begin(); iter != buys.end(); iter.next_page())
{
totalOffers += iter.page_size();
if (totalOffers > maxDeletableTokenOfferEntries)
return tefTOO_BIG;
}
}

{
Dir const sells(view, keylet::nft_sells(nftokenID));
for (auto iter = sells.begin(); iter != sells.end(); iter.next_page())
{
totalOffers += iter.page_size();
if (totalOffers > maxDeletableTokenOfferEntries)
return tefTOO_BIG;
}
}
return tesSUCCESS;
}

bool
deleteTokenOffer(ApplyView& view, std::shared_ptr<SLE> const& offer)
{
Expand Down
16 changes: 2 additions & 14 deletions src/test/app/AMM_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5043,13 +5043,7 @@ struct AMM_test : public jtx::AMMTest
FeatureBitset const all{testable_amendments()};

{
Env env(
*this,
envconfig([](std::unique_ptr<Config> cfg) {
cfg->FEES.reference_fee = XRPAmount(1);
return cfg;
}),
all);
Env env(*this, all, XRPAmount(1));
fund(env, gw, {alice}, XRP(20'000), {USD(10'000)});
AMM amm(env, gw, XRP(10'000), USD(10'000));
for (auto i = 0; i < maxDeletableAMMTrustLines + 10; ++i)
Expand Down Expand Up @@ -5101,13 +5095,7 @@ struct AMM_test : public jtx::AMMTest
}

{
Env env(
*this,
envconfig([](std::unique_ptr<Config> cfg) {
cfg->FEES.reference_fee = XRPAmount(1);
return cfg;
}),
all);
Env env(*this, all, XRPAmount(1));
fund(env, gw, {alice}, XRP(20'000), {USD(10'000)});
AMM amm(env, gw, XRP(10'000), USD(10'000));
for (auto i = 0; i < (maxDeletableAMMTrustLines * 2) + 10; ++i)
Expand Down
2 changes: 1 addition & 1 deletion src/test/app/FeeVote_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@ class FeeVote_test : public beast::unit_test::suite
Env env(*this, testable_amendments() | featureXRPFees);

// establish what the current fees are
BEAST_EXPECT(env.current()->fees().base == XRPAmount{UNIT_TEST_REFERENCE_FEE});
BEAST_EXPECT(env.current()->fees().base == env.app().config().FEES.reference_fee);
BEAST_EXPECT(env.current()->fees().reserve == XRPAmount{200'000'000});
BEAST_EXPECT(env.current()->fees().increment == XRPAmount{50'000'000});

Expand Down
5 changes: 2 additions & 3 deletions src/test/app/LedgerMaster_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ class LedgerMaster_test : public beast::unit_test::suite
using namespace jtx;
return envconfig([&](std::unique_ptr<Config> cfg) {
cfg->NETWORK_ID = networkID;
// This test relies on ledger hash so must lock it to fee 10.
cfg->FEES.reference_fee = 10;
return cfg;
});
}
Expand All @@ -28,7 +26,8 @@ class LedgerMaster_test : public beast::unit_test::suite
using namespace test::jtx;
using namespace std::literals;

test::jtx::Env env{*this, makeNetworkConfig(11111)};
// This test relies on ledger hash so must lock it to fee 10.
test::jtx::Env env{*this, makeNetworkConfig(11111), XRPAmount(10)};

auto const alice = Account("alice");
env.fund(XRP(1000), alice);
Expand Down
4 changes: 1 addition & 3 deletions src/test/app/MPToken_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2060,9 +2060,7 @@ class MPToken_test : public beast::unit_test::suite

Account const alice{"alice"};

auto cfg = envconfig();
cfg->FEES.reference_fee = 10;
Env env{*this, std::move(cfg), features};
Env env{*this, features, XRPAmount(10)};
MPTTester mptAlice(env, alice);

mptAlice.create();
Expand Down
12 changes: 7 additions & 5 deletions src/test/app/Regression_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,13 @@ struct Regression_test : public beast::unit_test::suite
{
testcase("Autofilled fee should use the escalated fee");
using namespace jtx;
Env env(*this, envconfig([](std::unique_ptr<Config> cfg) {
cfg->section("transaction_queue").set("minimum_txn_in_ledger_standalone", "3");
cfg->FEES.reference_fee = 10;
return cfg;
}));
Env env(
*this,
envconfig([](std::unique_ptr<Config> cfg) {
cfg->section("transaction_queue").set("minimum_txn_in_ledger_standalone", "3");
return cfg;
}),
XRPAmount(10));
Env_ss envs(env);

auto const alice = Account("alice");
Expand Down
6 changes: 2 additions & 4 deletions src/test/app/TxQ_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1229,8 +1229,7 @@ class TxQPosNegFlows_test : public beast::unit_test::suite
testcase("tie breaking");

auto cfg = makeConfig({{"minimum_txn_in_ledger_standalone", "4"}});
cfg->FEES.reference_fee = 10;
Env env(*this, std::move(cfg));
Env env(*this, std::move(cfg), XRPAmount(10));

auto alice = Account("alice");
auto bob = Account("bob");
Expand Down Expand Up @@ -2621,8 +2620,7 @@ class TxQPosNegFlows_test : public beast::unit_test::suite
{{"minimum_txn_in_ledger_standalone", "1"},
{"ledgers_in_queue", "10"},
{"maximum_txn_per_account", "11"}});
cfg->FEES.reference_fee = 10;
Env env(*this, std::move(cfg));
Env env(*this, std::move(cfg), XRPAmount(10));

auto const baseFee = env.current()->fees().base.drops();

Expand Down
Loading
Loading