Compare commits

...

10 Commits

Author SHA1 Message Date
Vladislav Vysokikh
df24ee0774 Version 2.6.2 2025-11-19 20:47:46 +00:00
Vladislav Vysokikh
c6aa80b0ef fixed macos build and removed ci flow publishing libxrpl 2025-11-19 20:47:32 +00:00
Bronek Kozicki
283bc3ea39 Remove directory size limit (#5935)
This change introduces the `fixDirectoryLimit` amendment to remove the directory pages limit. We found that the directory size limit is easier to hit than originally assumed, and there is no good reason to keep this limit, since the object reserve provides the necessary incentive to avoid creating unnecessary objects on the ledger.
2025-11-19 18:07:13 +00:00
Jingchen
ebc2a9a625 fix: Skip processing transaction batch if the batch is empty (#5670)
Avoids an assertion failure in NetworkOPsImp::apply in the unlikely event that all incoming transactions are invalid.
2025-11-18 09:17:48 +00:00
Ed Hennis
70d5c624e8 Set version to 2.6.1 2025-09-30 16:09:11 -04:00
Bronek Kozicki
c46888f8f7 Set version to 2.6.1-rc2 2025-09-18 18:09:04 +01:00
Bronek Kozicki
2ae65d2fdb Mark PermissionDelegation as unsupported 2025-09-18 18:04:12 +01:00
Bronek Kozicki
8d01f35eb9 Set version to 2.6.1-rc1 2025-09-16 15:35:54 -04:00
Bronek Kozicki
1020a32d76 Downgrade to boost 1.83 2025-09-16 15:35:47 -04:00
Michael Legleux
2df7dcfdeb Set version to 2.6.0 2025-08-27 10:25:53 -07:00
17 changed files with 495 additions and 125 deletions

View File

@@ -1,91 +0,0 @@
name: Check libXRPL compatibility with Clio
env:
CONAN_REMOTE_URL: https://conan.ripplex.io
CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.CONAN_REMOTE_USERNAME }}
CONAN_PASSWORD_XRPLF: ${{ secrets.CONAN_REMOTE_PASSWORD }}
on:
pull_request:
paths:
- "src/libxrpl/protocol/BuildInfo.cpp"
- ".github/workflows/libxrpl.yml"
types: [opened, reopened, synchronize, ready_for_review]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
publish:
if: ${{ github.event_name == 'push' || github.event.pull_request.draft != true || contains(github.event.pull_request.labels.*.name, 'DraftRunCI') }}
name: Publish libXRPL
outputs:
outcome: ${{ steps.upload.outputs.outcome }}
version: ${{ steps.version.outputs.version }}
channel: ${{ steps.channel.outputs.channel }}
runs-on: [self-hosted, heavy]
container: ghcr.io/xrplf/rippled-build-ubuntu:aaf5e3e
steps:
- name: Wait for essential checks to succeed
uses: lewagon/wait-on-check-action@v1.3.4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
running-workflow-name: wait-for-check-regexp
check-regexp: "(dependencies|test).*linux.*" # Ignore windows and mac tests but make sure linux passes
repo-token: ${{ secrets.GITHUB_TOKEN }}
wait-interval: 10
- name: Checkout
uses: actions/checkout@v4
- name: Generate channel
id: channel
shell: bash
run: |
echo channel="clio/pr_${{ github.event.pull_request.number }}" | tee ${GITHUB_OUTPUT}
- name: Export new package
shell: bash
run: |
conan export . ${{ steps.channel.outputs.channel }}
- name: Add Conan remote
shell: bash
run: |
echo "Adding Conan remote 'xrplf' at ${{ env.CONAN_REMOTE_URL }}."
conan remote add xrplf ${{ env.CONAN_REMOTE_URL }} --insert 0 --force
echo "Listing Conan remotes."
conan remote list
- name: Parse new version
id: version
shell: bash
run: |
echo version="$(cat src/libxrpl/protocol/BuildInfo.cpp | grep "versionString =" \
| awk -F '"' '{print $2}')" | tee ${GITHUB_OUTPUT}
- name: Try to authenticate to Conan remote
id: remote
shell: bash
run: |
# `conan user` implicitly uses the environment variables CONAN_LOGIN_USERNAME_<REMOTE> and CONAN_PASSWORD_<REMOTE>.
# https://docs.conan.io/1/reference/commands/misc/user.html#using-environment-variables
# https://docs.conan.io/1/reference/env_vars.html#conan-login-username-conan-login-username-remote-name
# https://docs.conan.io/1/reference/env_vars.html#conan-password-conan-password-remote-name
echo outcome=$(conan user --remote xrplf --password >&2 \
&& echo success || echo failure) | tee ${GITHUB_OUTPUT}
- name: Upload new package
id: upload
if: (steps.remote.outputs.outcome == 'success')
shell: bash
run: |
echo "conan upload version ${{ steps.version.outputs.version }} on channel ${{ steps.channel.outputs.channel }}"
echo outcome=$(conan upload xrpl/${{ steps.version.outputs.version }}@${{ steps.channel.outputs.channel }} --remote ripple --confirm >&2 \
&& echo success || echo failure) | tee ${GITHUB_OUTPUT}
notify_clio:
name: Notify Clio
runs-on: ubuntu-latest
needs: publish
env:
GH_TOKEN: ${{ secrets.CLIO_NOTIFY_TOKEN }}
steps:
- name: Notify Clio about new version
if: (needs.publish.outputs.outcome == 'success')
shell: bash
run: |
gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" \
/repos/xrplf/clio/dispatches -f "event_type=check_libxrpl" \
-F "client_payload[version]=${{ needs.publish.outputs.version }}@${{ needs.publish.outputs.channel }}" \
-F "client_payload[pr]=${{ github.event.pull_request.number }}"

View File

@@ -50,30 +50,35 @@ jobs:
steps:
- name: checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- name: install Conan
- name: Delete old build tools installed using Homebrew
run: |
brew install conan
- name: install Ninja
if: matrix.generator == 'Ninja'
run: brew install ninja
- name: install python
brew uninstall --force \
cmake \
conan
- name: Install build tools using Homebrew
run: |
if which python > /dev/null 2>&1; then
echo "Python executable exists"
else
brew install python@3.13
ln -s /opt/homebrew/bin/python3 /opt/homebrew/bin/python
fi
- name: install cmake
brew install --quiet \
ca-certificates \
ninja \
python@3.14
- name: Remove old fmt using Homebrew
run: |
if which cmake > /dev/null 2>&1; then
echo "cmake executable exists"
else
brew install cmake
fi
- name: install nproc
brew unlink fmt
brew cleanup
brew link fmt
- name: List software installed using Homebrew
run: brew list --version
- name: Install build tools using pip
shell: bash
run: |
brew install coreutils
pip3 install --break-system-packages --upgrade pip
pip3 install --break-system-packages \
cmake==4.1.2 \
conan==2.22.1
- name: check environment
run: |
env | sort

View File

@@ -26,6 +26,9 @@ tools.build:cxxflags=['-Wno-missing-template-arg-list-after-template-kw']
{% if compiler == "apple-clang" and compiler_version >= 17 %}
tools.build:cxxflags=['-Wno-missing-template-arg-list-after-template-kw']
{% endif %}
{% if compiler == "clang" and compiler_version == 16 %}
tools.build:cxxflags=['-DBOOST_ASIO_DISABLE_CONCEPTS']
{% endif %}
{% if compiler == "gcc" and compiler_version < 13 %}
tools.build:cxxflags=['-Wno-restrict']
{% endif %}

View File

@@ -104,7 +104,7 @@ class Xrpl(ConanFile):
def requirements(self):
# Conan 2 requires transitive headers to be specified
transitive_headers_opt = {'transitive_headers': True} if conan_version.split('.')[0] == '2' else {}
self.requires('boost/1.86.0', force=True, **transitive_headers_opt)
self.requires('boost/1.83.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/3.21.12', force=True)

View File

@@ -56,7 +56,10 @@ std::size_t constexpr oversizeMetaDataCap = 5200;
/** The maximum number of entries per directory page */
std::size_t constexpr dirNodeMaxEntries = 32;
/** The maximum number of pages allowed in a directory */
/** The maximum number of pages allowed in a directory
Made obsolete by fixDirectoryLimit amendment.
*/
std::uint64_t constexpr dirNodeMaxPages = 262144;
/** The maximum number of items in an NFT page */

View File

@@ -29,9 +29,8 @@
// Add new amendments to the top of this list.
// Keep it sorted in reverse chronological order.
// If you add an amendment here, then do not forget to increment `numFeatures`
// in include/xrpl/protocol/Feature.h.
XRPL_FIX (DirectoryLimit, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (PriceOracleOrder, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (MPTDeliveredAmount, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (AMMClawbackRounding, Supported::no, VoteBehavior::DefaultNo)
@@ -41,7 +40,7 @@ XRPL_FIX (AMMv1_3, Supported::yes, VoteBehavior::DefaultNo
XRPL_FEATURE(PermissionedDEX, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(Batch, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(SingleAssetVault, Supported::no, VoteBehavior::DefaultNo)
XRPL_FEATURE(PermissionDelegation, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(PermissionDelegation, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (PayChanCancelAfter, Supported::yes, VoteBehavior::DefaultNo)
// Check flags in Credential transactions
XRPL_FIX (InvalidTxFlags, Supported::yes, VoteBehavior::DefaultNo)

View File

@@ -36,7 +36,7 @@ namespace BuildInfo {
// and follow the format described at http://semver.org/
//------------------------------------------------------------------------------
// clang-format off
char const* const versionString = "2.6.0-rc3"
char const* const versionString = "2.6.2"
// clang-format on
#if defined(DEBUG) || defined(SANITIZER)

View File

@@ -568,6 +568,39 @@ struct Credentials_test : public beast::unit_test::suite
jle[jss::result][jss::node]["CredentialType"] ==
strHex(std::string_view(credType)));
}
{
testcase("Credentials fail, directory full");
std::uint32_t const issuerSeq{env.seq(issuer) + 1};
env(ticket::create(issuer, 63));
env.close();
// Everything below can only be tested on open ledger.
auto const res1 = directory::bumpLastPage(
env,
directory::maximumPageIndex(env),
keylet::ownerDir(issuer.id()),
directory::adjustOwnerNode);
BEAST_EXPECT(res1);
auto const jv = credentials::create(issuer, subject, credType);
env(jv, ter(tecDIR_FULL));
// Free one directory entry by using a ticket
env(noop(issuer), ticket::use(issuerSeq + 40));
// Fill subject directory
env(ticket::create(subject, 63));
auto const res2 = directory::bumpLastPage(
env,
directory::maximumPageIndex(env),
keylet::ownerDir(subject.id()),
directory::adjustOwnerNode);
BEAST_EXPECT(res2);
env(jv, ter(tecDIR_FULL));
// End test
env.close();
}
}
{
@@ -1094,6 +1127,7 @@ struct Credentials_test : public beast::unit_test::suite
testSuccessful(all);
testCredentialsDelete(all);
testCreateFailed(all);
testCreateFailed(all - fixDirectoryLimit);
testAcceptFailed(all);
testDeleteFailed(all);
testFeatureFailed(all - featureCredentials);

View File

@@ -0,0 +1,80 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2020 Dev Null Productions
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <test/jtx.h>
#include <test/jtx/CaptureLogs.h>
#include <test/jtx/Env.h>
#include <xrpld/app/misc/HashRouter.h>
namespace ripple {
namespace test {
class NetworkOPs_test : public beast::unit_test::suite
{
public:
void
run() override
{
testAllBadHeldTransactions();
}
void
testAllBadHeldTransactions()
{
// All trasactions are already marked as SF_BAD, and we should be able
// to handle the case properly without an assertion failure
testcase("No valid transactions in batch");
std::string logs;
{
using namespace jtx;
auto const alice = Account{"alice"};
Env env{
*this,
envconfig(),
std::make_unique<CaptureLogs>(&logs),
beast::severities::kAll};
env.memoize(env.master);
env.memoize(alice);
auto const jtx = env.jt(ticket::create(alice, 1), seq(1), fee(10));
auto transacionId = jtx.stx->getTransactionID();
env.app().getHashRouter().setFlags(
transacionId, HashRouterFlags::HELD);
env(jtx, json(jss::Sequence, 1), ter(terNO_ACCOUNT));
env.app().getHashRouter().setFlags(
transacionId, HashRouterFlags::BAD);
env.close();
}
BEAST_EXPECT(
logs.find("No transaction to process!") != std::string::npos);
}
};
BEAST_DEFINE_TESTSUITE(NetworkOPs, app, ripple);
} // namespace test
} // namespace ripple

View File

@@ -39,6 +39,7 @@
#include <test/jtx/delivermin.h>
#include <test/jtx/deposit.h>
#include <test/jtx/did.h>
#include <test/jtx/directory.h>
#include <test/jtx/domain.h>
#include <test/jtx/escrow.h>
#include <test/jtx/fee.h>

81
src/test/jtx/directory.h Normal file
View File

@@ -0,0 +1,81 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2025 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_TEST_JTX_DIRECTORY_H_INCLUDED
#define RIPPLE_TEST_JTX_DIRECTORY_H_INCLUDED
#include <test/jtx/Env.h>
#include <xrpl/basics/Expected.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <cstdint>
#include <limits>
namespace ripple::test::jtx {
/** Directory operations. */
namespace directory {
enum Error {
DirectoryRootNotFound,
DirectoryTooSmall,
DirectoryPageDuplicate,
DirectoryPageNotFound,
InvalidLastPage,
AdjustmentError
};
/// Move the position of the last page in the user's directory on open ledger to
/// newLastPage. Requirements:
/// - directory must have at least two pages (root and one more)
/// - adjust should be used to update owner nodes of the objects affected
/// - newLastPage must be greater than index of the last page in the directory
///
/// Use this to test tecDIR_FULL errors in open ledger.
/// NOTE: effects will be DISCARDED on env.close()
auto
bumpLastPage(
Env& env,
std::uint64_t newLastPage,
Keylet directory,
std::function<bool(ApplyView&, uint256, std::uint64_t)> adjust)
-> Expected<void, Error>;
/// Implementation of adjust for the most common ledger entry, i.e. one where
/// page index is stored in sfOwnerNode (and only there). Pass this function
/// to bumpLastPage if the last page of directory has only objects
/// of this kind (e.g. ticket, DID, offer, deposit preauth, MPToken etc.)
bool
adjustOwnerNode(ApplyView& view, uint256 key, std::uint64_t page);
inline auto
maximumPageIndex(Env const& env) -> std::uint64_t
{
if (env.enabled(fixDirectoryLimit))
return std::numeric_limits<std::uint64_t>::max();
return dirNodeMaxPages - 1;
}
} // namespace directory
} // namespace ripple::test::jtx
#endif

View File

@@ -0,0 +1,145 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2025 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <test/jtx/directory.h>
#include <xrpld/ledger/Sandbox.h>
namespace ripple::test::jtx {
/** Directory operations. */
namespace directory {
auto
bumpLastPage(
Env& env,
std::uint64_t newLastPage,
Keylet directory,
std::function<bool(ApplyView&, uint256, std::uint64_t)> adjust)
-> Expected<void, Error>
{
Expected<void, Error> res{};
env.app().openLedger().modify(
[&](OpenView& view, beast::Journal j) -> bool {
Sandbox sb(&view, tapNONE);
// Find the root page
auto sleRoot = sb.peek(directory);
if (!sleRoot)
{
res = Unexpected<Error>(DirectoryRootNotFound);
return false;
}
// Find last page
auto const lastIndex = sleRoot->getFieldU64(sfIndexPrevious);
if (lastIndex == 0)
{
res = Unexpected<Error>(DirectoryTooSmall);
return false;
}
if (sb.exists(keylet::page(directory, newLastPage)))
{
res = Unexpected<Error>(DirectoryPageDuplicate);
return false;
}
if (lastIndex >= newLastPage)
{
res = Unexpected<Error>(InvalidLastPage);
return false;
}
auto slePage = sb.peek(keylet::page(directory, lastIndex));
if (!slePage)
{
res = Unexpected<Error>(DirectoryPageNotFound);
return false;
}
// Copy its data and delete the page
auto indexes = slePage->getFieldV256(sfIndexes);
auto prevIndex = slePage->at(~sfIndexPrevious);
auto owner = slePage->at(~sfOwner);
sb.erase(slePage);
// Create new page to replace slePage
auto sleNew =
std::make_shared<SLE>(keylet::page(directory, newLastPage));
sleNew->setFieldH256(sfRootIndex, directory.key);
sleNew->setFieldV256(sfIndexes, indexes);
if (owner)
sleNew->setAccountID(sfOwner, *owner);
if (prevIndex)
sleNew->setFieldU64(sfIndexPrevious, *prevIndex);
sb.insert(sleNew);
// Adjust root previous and previous node's next
sleRoot->setFieldU64(sfIndexPrevious, newLastPage);
if (prevIndex.value_or(0) == 0)
sleRoot->setFieldU64(sfIndexNext, newLastPage);
else
{
auto slePrev = sb.peek(keylet::page(directory, *prevIndex));
if (!slePrev)
{
res = Unexpected<Error>(DirectoryPageNotFound);
return false;
}
slePrev->setFieldU64(sfIndexNext, newLastPage);
sb.update(slePrev);
}
sb.update(sleRoot);
// Fixup page numbers in the objects referred by indexes
if (adjust)
for (auto const key : indexes)
{
if (!adjust(sb, key, newLastPage))
{
res = Unexpected<Error>(AdjustmentError);
return false;
}
}
sb.apply(view);
return true;
});
return res;
}
bool
adjustOwnerNode(ApplyView& view, uint256 key, std::uint64_t page)
{
auto sle = view.peek({ltANY, key});
if (sle && sle->isFieldPresent(sfOwnerNode))
{
sle->setFieldU64(sfOwnerNode, page);
view.update(sle);
return true;
}
return false;
}
} // namespace directory
} // namespace ripple::test::jtx

View File

@@ -23,9 +23,11 @@
#include <xrpl/basics/random.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/jss.h>
#include <algorithm>
#include <limits>
namespace ripple {
namespace test {
@@ -490,6 +492,91 @@ struct Directory_test : public beast::unit_test::suite
}
}
void
testDirectoryFull()
{
using namespace test::jtx;
Account alice("alice");
auto const testCase = [&, this](FeatureBitset features, auto setup) {
using namespace test::jtx;
Env env(*this, features);
env.fund(XRP(20000), alice);
env.close();
auto const [lastPage, full] = setup(env);
// Populate root page and last page
for (int i = 0; i < 63; ++i)
env(credentials::create(alice, alice, std::to_string(i)));
env.close();
// NOTE, everything below can only be tested on open ledger because
// there is no transaction type to express what bumpLastPage does.
// Bump position of last page from 1 to highest possible
auto const res = directory::bumpLastPage(
env,
lastPage,
keylet::ownerDir(alice.id()),
[lastPage, this](
ApplyView& view, uint256 key, std::uint64_t page) {
auto sle = view.peek({ltCREDENTIAL, key});
if (!BEAST_EXPECT(sle))
return false;
BEAST_EXPECT(page == lastPage);
sle->setFieldU64(sfIssuerNode, page);
// sfSubjectNode is not set in self-issued credentials
view.update(sle);
return true;
});
BEAST_EXPECT(res);
// Create one more credential
env(credentials::create(alice, alice, std::to_string(63)));
// Not enough space for another object if full
auto const expected = full ? ter{tecDIR_FULL} : ter{tesSUCCESS};
env(credentials::create(alice, alice, "foo"), expected);
// Destroy all objects in directory
for (int i = 0; i < 64; ++i)
env(credentials::deleteCred(
alice, alice, alice, std::to_string(i)));
if (!full)
env(credentials::deleteCred(alice, alice, alice, "foo"));
// Verify directory is empty.
auto const sle = env.le(keylet::ownerDir(alice.id()));
BEAST_EXPECT(sle == nullptr);
// Test completed
env.close();
};
testCase(
testable_amendments() - fixDirectoryLimit,
[this](Env&) -> std::tuple<std::uint64_t, bool> {
testcase("directory full without fixDirectoryLimit");
return {dirNodeMaxPages - 1, true};
});
testCase(
testable_amendments(), //
[this](Env&) -> std::tuple<std::uint64_t, bool> {
testcase("directory not full with fixDirectoryLimit");
return {dirNodeMaxPages - 1, false};
});
testCase(
testable_amendments(), //
[this](Env&) -> std::tuple<std::uint64_t, bool> {
testcase("directory full with fixDirectoryLimit");
return {std::numeric_limits<std::uint64_t>::max(), true};
});
}
void
run() override
{
@@ -498,6 +585,7 @@ struct Directory_test : public beast::unit_test::suite
testRipd1353();
testEmptyChain();
testPreviousTxnID();
testDirectoryFull();
}
};

View File

@@ -681,7 +681,7 @@ class ServerStatus_test : public beast::unit_test::suite,
resp["Upgrade"] == "websocket");
BEAST_EXPECT(
resp.find("Connection") != resp.end() &&
resp["Connection"] == "Upgrade");
resp["Connection"] == "upgrade");
}
void

View File

@@ -1448,6 +1448,11 @@ NetworkOPsImp::processTransactionSet(CanonicalTXSet const& set)
for (auto& t : transactions)
mTransactions.push_back(std::move(t));
}
if (mTransactions.empty())
{
JLOG(m_journal.debug()) << "No transaction to process!";
return;
}
doTransactionSyncBatch(lock, [&](std::unique_lock<std::mutex> const&) {
XRPL_ASSERT(

View File

@@ -23,6 +23,9 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/protocol/Protocol.h>
#include <limits>
#include <type_traits>
namespace ripple {
std::optional<std::uint64_t>
@@ -92,8 +95,21 @@ ApplyView::dirAdd(
return page;
}
// We rely on modulo arithmetic of unsigned integers (guaranteed in
// [basic.fundamental] paragraph 2) to detect page representation overflow.
// For signed integers this would be UB, hence static_assert here.
static_assert(std::is_unsigned_v<decltype(page)>);
// Defensive check against breaking changes in compiler.
static_assert([]<typename T>(std::type_identity<T>) constexpr -> T {
T tmp = std::numeric_limits<T>::max();
return ++tmp;
}(std::type_identity<decltype(page)>{}) == 0);
++page;
// Check whether we're out of pages.
if (++page >= dirNodeMaxPages)
if (page == 0)
return std::nullopt;
if (!rules().enabled(fixDirectoryLimit) &&
page >= dirNodeMaxPages) // Old pages limit
return std::nullopt;
// We are about to create a new node; we'll link it to

View File

@@ -1286,8 +1286,7 @@ PeerImp::handleTransaction(
// Charge strongly for attempting to relay a txn with tfInnerBatchTxn
// LCOV_EXCL_START
if (stx->isFlag(tfInnerBatchTxn) &&
getCurrentTransactionRules()->enabled(featureBatch))
if (stx->isFlag(tfInnerBatchTxn))
{
JLOG(p_journal_.warn()) << "Ignoring Network relayed Tx containing "
"tfInnerBatchTxn (handleTransaction).";
@@ -2851,8 +2850,7 @@ PeerImp::checkTransaction(
{
// charge strongly for relaying batch txns
// LCOV_EXCL_START
if (stx->isFlag(tfInnerBatchTxn) &&
getCurrentTransactionRules()->enabled(featureBatch))
if (stx->isFlag(tfInnerBatchTxn))
{
JLOG(p_journal_.warn()) << "Ignoring Network relayed Tx containing "
"tfInnerBatchTxn (checkSignature).";
@@ -2866,6 +2864,9 @@ PeerImp::checkTransaction(
(stx->getFieldU32(sfLastLedgerSequence) <
app_.getLedgerMaster().getValidLedgerIndex()))
{
JLOG(p_journal_.info())
<< "Marking transaction " << stx->getTransactionID()
<< "as BAD because it's expired";
app_.getHashRouter().setFlags(
stx->getTransactionID(), HashRouterFlags::BAD);
charge(Resource::feeUselessData, "expired tx");
@@ -2922,7 +2923,7 @@ PeerImp::checkTransaction(
{
if (!validReason.empty())
{
JLOG(p_journal_.trace())
JLOG(p_journal_.debug())
<< "Exception checking transaction: " << validReason;
}
@@ -2949,7 +2950,7 @@ PeerImp::checkTransaction(
{
if (!reason.empty())
{
JLOG(p_journal_.trace())
JLOG(p_journal_.debug())
<< "Exception checking transaction: " << reason;
}
app_.getHashRouter().setFlags(