Merge branch 'develop' into ximinez/after-is-never-null

This commit is contained in:
Ed Hennis
2026-06-04 10:09:23 -04:00
committed by GitHub
25 changed files with 219 additions and 147 deletions

View File

@@ -134,6 +134,7 @@ words:
- iou
- ious
- isrdc
- isystem
- itype
- jemalloc
- jlog

View File

@@ -8,13 +8,17 @@ clang++ --version
clang-format --version
cmake --version
conan --version
curl --version
g++ --version
gcc --version
gcov --version
gcovr --version
git --version
gpg --version
less --version
make --version
mold --version
netstat --version
ninja --version
perl --version
pkg-config --version
@@ -22,3 +26,9 @@ pre-commit --version
python3 --version
run-clang-tidy --help
vim --version
# A simple test to verify that git can clone a repository over HTTPS
# (i.e. the CA bundle is wired up). Clone to a temp dir and clean up.
tmp_clone="$(mktemp -d)"
git clone --depth 1 https://github.com/XRPLF/actions.git "${tmp_clone}/actions"
rm -rf "${tmp_clone}"

View File

@@ -47,6 +47,12 @@ COPY --from=builder /tmp/build/result /nix/ci-env
ENV PATH="/nix/ci-env/bin:${PATH}"
# Point HTTPS clients (git, curl, conan, ...) at the CA bundle shipped in the
# Nix CI environment, so TLS verification works without ca-certificates being
# installed in the system.
ENV SSL_CERT_FILE="/nix/ci-env/etc/ssl/certs/ca-bundle.crt"
ENV GIT_SSL_CAINFO="/nix/ci-env/etc/ssl/certs/ca-bundle.crt"
# Externally-built dynamically-linked ELF binaries hard-code the loader path
# (e.g. /lib64/ld-linux-x86-64.so.2) in their PT_INTERP header. Install it
# from the Nix store when the base image doesn't already provide one.
@@ -65,8 +71,8 @@ if [ ! -e "${target}" ]; then
fi
EOF
COPY docker/check-tool-versions.sh /tmp/check-tool-versions.sh
RUN /tmp/check-tool-versions.sh
COPY docker/check-tools.sh /tmp/check-tools.sh
RUN /tmp/check-tools.sh
# Sanity-check that the g++/clang++ are able to build binaries, including sanitizer-instrumented ones.
COPY docker/test_files/cpp_sources/ /tmp/cpp_sources/

View File

@@ -2,6 +2,13 @@
#include <cstddef>
#include <iostream>
// Regression test: the compiler-rt sanitizer interface headers must be on the
// include path. A bare on-PATH clang in the Nix CI env doesn't get them
// propagated automatically, so this include would fail to compile with clang++
// if the env isn't wired up correctly. abseil hits the same include during
// sanitizer builds. LeakSanitizer ships with AddressSanitizer.
#include <sanitizer/lsan_interface.h>
#if defined(__clang__) || defined(__GNUC__)
__attribute__((noinline))
#elif defined(_MSC_VER)

6
flake.lock generated
View File

@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1777954456,
"narHash": "sha256-hGdgeU2Nk87RAuZyYjyDjFL6LK7dAZN5RE9+hrDTkDU=",
"lastModified": 1780243769,
"narHash": "sha256-x5UQuRsH3MqI0U9afaXSNqzTPSeZlRLvFAav2Ux1pNw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "549bd84d6279f9852cae6225e372cc67fb91a4c1",
"rev": "331800de5053fcebacf6813adb5db9c9dca22a0c",
"type": "github"
},
"original": {

View File

@@ -85,7 +85,7 @@ private:
/** The sequence of the ledger that this map references, if any. */
std::uint32_t ledgerSeq_ = 0;
intr_ptr::SharedPtr<SHAMapTreeNode> root_;
SHAMapTreeNodePtr root_;
mutable SHAMapState state_;
SHAMapType const type_;
bool backed_ = true; // Map is backed by the database
@@ -326,36 +326,32 @@ public:
invariants() const;
private:
using SharedPtrNodeStack =
std::stack<std::pair<intr_ptr::SharedPtr<SHAMapTreeNode>, SHAMapNodeID>>;
using SharedPtrNodeStack = std::stack<std::pair<SHAMapTreeNodePtr, SHAMapNodeID>>;
using DeltaRef =
std::pair<boost::intrusive_ptr<SHAMapItem const>, boost::intrusive_ptr<SHAMapItem const>>;
// tree node cache operations
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
cacheLookup(SHAMapHash const& hash) const;
void
canonicalize(SHAMapHash const& hash, intr_ptr::SharedPtr<SHAMapTreeNode>&) const;
canonicalize(SHAMapHash const& hash, SHAMapTreeNodePtr&) const;
// database operations
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
fetchNodeFromDB(SHAMapHash const& hash) const;
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
fetchNodeNT(SHAMapHash const& hash) const;
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
fetchNodeNT(SHAMapHash const& hash, SHAMapSyncFilter* filter) const;
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
fetchNode(SHAMapHash const& hash) const;
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
checkFilter(SHAMapHash const& hash, SHAMapSyncFilter* filter) const;
/** Update hashes up to the root */
void
dirtyUp(
SharedPtrNodeStack& stack,
uint256 const& target,
intr_ptr::SharedPtr<SHAMapTreeNode> terminal);
dirtyUp(SharedPtrNodeStack& stack, uint256 const& target, SHAMapTreeNodePtr terminal);
/** Walk towards the specified id, returning the node. Caller must check
if the return is nullptr, and if not, if the node->peekItem()->key() ==
@@ -377,25 +373,21 @@ private:
preFlushNode(intr_ptr::SharedPtr<Node> node) const;
/** write and canonicalize modified node */
intr_ptr::SharedPtr<SHAMapTreeNode>
writeNode(NodeObjectType t, intr_ptr::SharedPtr<SHAMapTreeNode> node) const;
SHAMapTreeNodePtr
writeNode(NodeObjectType t, SHAMapTreeNodePtr node) const;
// returns the first item at or below this node
SHAMapLeafNode*
firstBelow(intr_ptr::SharedPtr<SHAMapTreeNode>, SharedPtrNodeStack& stack, int branch = 0)
const;
firstBelow(SHAMapTreeNodePtr node, SharedPtrNodeStack& stack, int branch = 0) const;
// returns the last item at or below this node
SHAMapLeafNode*
lastBelow(
intr_ptr::SharedPtr<SHAMapTreeNode> node,
SharedPtrNodeStack& stack,
int branch = kBranchFactor) const;
lastBelow(SHAMapTreeNodePtr node, SharedPtrNodeStack& stack, int branch = kBranchFactor) const;
// helper function for firstBelow and lastBelow
SHAMapLeafNode*
belowHelper(
intr_ptr::SharedPtr<SHAMapTreeNode> node,
SHAMapTreeNodePtr node,
SharedPtrNodeStack& stack,
int branch,
std::tuple<int, std::function<bool(int)>, std::function<void(int&)>> const& loopParams)
@@ -407,15 +399,14 @@ private:
descend(SHAMapInnerNode*, int branch) const;
SHAMapTreeNode*
descendThrow(SHAMapInnerNode*, int branch) const;
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
descend(SHAMapInnerNode&, int branch) const;
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
descendThrow(SHAMapInnerNode&, int branch) const;
// Descend with filter
// If pending, callback is called as if it called fetchNodeNT
using descendCallback =
std::function<void(intr_ptr::SharedPtr<SHAMapTreeNode>, SHAMapHash const&)>;
using descendCallback = std::function<void(SHAMapTreeNodePtr, SHAMapHash const&)>;
SHAMapTreeNode*
descendAsync(
SHAMapInnerNode* parent,
@@ -433,7 +424,7 @@ private:
// Non-storing
// Does not hook the returned node to its parent
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
descendNoStore(SHAMapInnerNode&, int branch) const;
/** If there is only one leaf below this node, get its contents */
@@ -495,10 +486,10 @@ private:
// nodes we may have acquired from deferred reads
using DeferredNode = std::tuple<
SHAMapInnerNode*, // parent node
SHAMapNodeID, // parent node ID
int, // branch
intr_ptr::SharedPtr<SHAMapTreeNode>>; // node
SHAMapInnerNode*, // parent node
SHAMapNodeID, // parent node ID
int, // branch
SHAMapTreeNodePtr>; // node
int deferred;
std::mutex deferLock;
@@ -524,7 +515,7 @@ private:
gmnProcessDeferredReads(MissingNodes&);
// fetch from DB helper function
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
finishFetch(SHAMapHash const& hash, std::shared_ptr<NodeObject> const& object) const;
};

View File

@@ -27,7 +27,7 @@ public:
{
}
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
clone(std::uint32_t cowid) const final
{
return intr_ptr::makeShared<SHAMapAccountStateLeafNode>(item_, cowid, hash_);

View File

@@ -87,7 +87,7 @@ public:
void
partialDestructor() override;
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
clone(std::uint32_t cowid) const override;
SHAMapNodeType
@@ -121,19 +121,19 @@ public:
getChildHash(int m) const;
void
setChild(int m, intr_ptr::SharedPtr<SHAMapTreeNode> child);
setChild(int m, SHAMapTreeNodePtr child);
void
shareChild(int m, intr_ptr::SharedPtr<SHAMapTreeNode> const& child);
shareChild(int m, SHAMapTreeNodePtr const& child);
SHAMapTreeNode*
getChildPointer(int branch);
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
getChild(int branch);
intr_ptr::SharedPtr<SHAMapTreeNode>
canonicalizeChild(int branch, intr_ptr::SharedPtr<SHAMapTreeNode> node);
SHAMapTreeNodePtr
canonicalizeChild(int branch, SHAMapTreeNodePtr node);
// sync functions
bool
@@ -161,10 +161,10 @@ public:
void
invariants(bool isRoot = false) const override;
static intr_ptr::SharedPtr<SHAMapTreeNode>
static SHAMapTreeNodePtr
makeFullInner(Slice data, SHAMapHash const& hash, bool hashValid);
static intr_ptr::SharedPtr<SHAMapTreeNode>
static SHAMapTreeNodePtr
makeCompressedInner(Slice data);
};

View File

@@ -13,6 +13,9 @@
namespace xrpl {
class SHAMapTreeNode;
using SHAMapTreeNodePtr = intr_ptr::SharedPtr<SHAMapTreeNode>;
// These are wire-protocol identifiers used during serialization to encode the
// type of a node. They should not be arbitrarily be changed.
static constexpr unsigned char const kWireTypeTransaction = 0;
@@ -112,7 +115,7 @@ public:
}
/** Make a copy of this node, setting the owner. */
virtual intr_ptr::SharedPtr<SHAMapTreeNode>
virtual SHAMapTreeNodePtr
clone(std::uint32_t cowid) const = 0;
/** @} */
@@ -153,20 +156,20 @@ public:
virtual void
invariants(bool isRoot = false) const = 0;
static intr_ptr::SharedPtr<SHAMapTreeNode>
static SHAMapTreeNodePtr
makeFromPrefix(Slice rawNode, SHAMapHash const& hash);
static intr_ptr::SharedPtr<SHAMapTreeNode>
static SHAMapTreeNodePtr
makeFromWire(Slice rawNode);
private:
static intr_ptr::SharedPtr<SHAMapTreeNode>
static SHAMapTreeNodePtr
makeTransaction(Slice data, SHAMapHash const& hash, bool hashValid);
static intr_ptr::SharedPtr<SHAMapTreeNode>
static SHAMapTreeNodePtr
makeAccountState(Slice data, SHAMapHash const& hash, bool hashValid);
static intr_ptr::SharedPtr<SHAMapTreeNode>
static SHAMapTreeNodePtr
makeTransactionWithMeta(Slice data, SHAMapHash const& hash, bool hashValid);
};

View File

@@ -26,7 +26,7 @@ public:
{
}
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
clone(std::uint32_t cowid) const final
{
return intr_ptr::makeShared<SHAMapTxLeafNode>(item_, cowid, hash_);

View File

@@ -27,7 +27,7 @@ public:
{
}
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
clone(std::uint32_t cowid) const override
{
return intr_ptr::makeShared<SHAMapTxPlusMetaLeafNode>(item_, cowid, hash_);

View File

@@ -11,5 +11,5 @@ using TreeNodeCache = TaggedCache<
SHAMapTreeNode,
/*IsKeyCache*/ false,
intr_ptr::SharedWeakUnionPtr<SHAMapTreeNode>,
intr_ptr::SharedPtr<SHAMapTreeNode>>;
SHAMapTreeNodePtr>;
} // namespace xrpl

View File

@@ -148,7 +148,7 @@ public:
/** Get the number of elements in each array and a pointer to the start
of each array.
*/
[[nodiscard]] std::tuple<std::uint8_t, SHAMapHash*, intr_ptr::SharedPtr<SHAMapTreeNode>*>
[[nodiscard]] std::tuple<std::uint8_t, SHAMapHash*, SHAMapTreeNodePtr*>
getHashesAndChildren() const;
/** Get the `hashes` array */
@@ -156,7 +156,7 @@ public:
getHashes() const;
/** Get the `children` array */
[[nodiscard]] intr_ptr::SharedPtr<SHAMapTreeNode>*
[[nodiscard]] SHAMapTreeNodePtr*
getChildren() const;
/** Call the `f` callback for all 16 (branchFactor) branches - even if

View File

@@ -26,8 +26,7 @@ static_assert(
// Terminology: A chunk is the memory being allocated from a block. A block
// contains multiple chunks. This is the terminology the boost documentation
// uses. Pools use "Simple Segregated Storage" as their storage format.
constexpr size_t kElementSizeBytes =
(sizeof(SHAMapHash) + sizeof(intr_ptr::SharedPtr<SHAMapTreeNode>));
constexpr size_t kElementSizeBytes = sizeof(SHAMapHash) + sizeof(SHAMapTreeNodePtr);
constexpr size_t kBlockSizeBytes = kilobytes(512);
@@ -364,8 +363,7 @@ inline TaggedPointer::TaggedPointer(
// keep
new (&dstHashes[dstIndex]) SHAMapHash{srcHashes[srcIndex]};
new (&dstChildren[dstIndex])
intr_ptr::SharedPtr<SHAMapTreeNode>{std::move(srcChildren[srcIndex])};
new (&dstChildren[dstIndex]) SHAMapTreeNodePtr{std::move(srcChildren[srcIndex])};
++dstIndex;
++srcIndex;
}
@@ -376,7 +374,7 @@ inline TaggedPointer::TaggedPointer(
if (dstIsDense)
{
new (&dstHashes[dstIndex]) SHAMapHash{};
new (&dstChildren[dstIndex]) intr_ptr::SharedPtr<SHAMapTreeNode>{};
new (&dstChildren[dstIndex]) SHAMapTreeNodePtr{};
++dstIndex;
}
}
@@ -384,7 +382,7 @@ inline TaggedPointer::TaggedPointer(
{
// add
new (&dstHashes[dstIndex]) SHAMapHash{};
new (&dstChildren[dstIndex]) intr_ptr::SharedPtr<SHAMapTreeNode>{};
new (&dstChildren[dstIndex]) SHAMapTreeNodePtr{};
++dstIndex;
if (srcIsDense)
{
@@ -397,7 +395,7 @@ inline TaggedPointer::TaggedPointer(
if (dstIsDense)
{
new (&dstHashes[dstIndex]) SHAMapHash{};
new (&dstChildren[dstIndex]) intr_ptr::SharedPtr<SHAMapTreeNode>{};
new (&dstChildren[dstIndex]) SHAMapTreeNodePtr{};
++dstIndex;
}
if (srcIsDense)
@@ -414,7 +412,7 @@ inline TaggedPointer::TaggedPointer(
for (int i = dstIndex; i < dstNumAllocated; ++i)
{
new (&dstHashes[i]) SHAMapHash{};
new (&dstChildren[i]) intr_ptr::SharedPtr<SHAMapTreeNode>{};
new (&dstChildren[i]) SHAMapTreeNodePtr{};
}
*this = std::move(dst);
}
@@ -433,8 +431,10 @@ inline TaggedPointer::TaggedPointer(
// allocate hashes and children, but do not run constructors
TaggedPointer newHashesAndChildren{RawAllocateTag{}, toAllocate};
SHAMapHash *newHashes = nullptr, *oldHashes = nullptr;
intr_ptr::SharedPtr<SHAMapTreeNode>*newChildren = nullptr, *oldChildren = nullptr;
SHAMapHash* newHashes = nullptr;
SHAMapHash* oldHashes = nullptr;
SHAMapTreeNodePtr* newChildren = nullptr;
SHAMapTreeNodePtr* oldChildren = nullptr;
std::uint8_t newNumAllocated = 0;
// structured bindings can't be captured in c++ 17; use tie instead
std::tie(newNumAllocated, newHashes, newChildren) = newHashesAndChildren.getHashesAndChildren();
@@ -445,8 +445,7 @@ inline TaggedPointer::TaggedPointer(
// new arrays are dense, old arrays are sparse
iterNonEmptyChildIndexes(isBranch, [&](auto branchNum, auto indexNum) {
new (&newHashes[branchNum]) SHAMapHash{oldHashes[indexNum]};
new (&newChildren[branchNum])
intr_ptr::SharedPtr<SHAMapTreeNode>{std::move(oldChildren[indexNum])};
new (&newChildren[branchNum]) SHAMapTreeNodePtr{std::move(oldChildren[indexNum])};
});
// Run the constructors for the remaining elements
for (int i = 0; i < SHAMapInnerNode::kBranchFactor; ++i)
@@ -454,7 +453,7 @@ inline TaggedPointer::TaggedPointer(
if (((1 << i) & isBranch) != 0)
continue;
new (&newHashes[i]) SHAMapHash{};
new (&newChildren[i]) intr_ptr::SharedPtr<SHAMapTreeNode>{};
new (&newChildren[i]) SHAMapTreeNodePtr{};
}
}
else
@@ -464,14 +463,14 @@ inline TaggedPointer::TaggedPointer(
iterNonEmptyChildIndexes(isBranch, [&](auto branchNum, auto indexNum) {
new (&newHashes[curCompressedIndex]) SHAMapHash{oldHashes[indexNum]};
new (&newChildren[curCompressedIndex])
intr_ptr::SharedPtr<SHAMapTreeNode>{std::move(oldChildren[indexNum])};
SHAMapTreeNodePtr{std::move(oldChildren[indexNum])};
++curCompressedIndex;
});
// Run the constructors for the remaining elements
for (int i = curCompressedIndex; i < newNumAllocated; ++i)
{
new (&newHashes[i]) SHAMapHash{};
new (&newChildren[i]) intr_ptr::SharedPtr<SHAMapTreeNode>{};
new (&newChildren[i]) SHAMapTreeNodePtr{};
}
}
@@ -485,7 +484,7 @@ inline TaggedPointer::TaggedPointer(std::uint8_t numChildren)
for (std::size_t i = 0; i < numAllocated; ++i)
{
new (&hashes[i]) SHAMapHash{};
new (&children[i]) intr_ptr::SharedPtr<SHAMapTreeNode>{};
new (&children[i]) SHAMapTreeNodePtr{};
}
}
@@ -523,14 +522,13 @@ TaggedPointer::isDense() const
return (tp_ & kTagMask) == kBoundaries.size() - 1;
}
[[nodiscard]] inline std::tuple<std::uint8_t, SHAMapHash*, intr_ptr::SharedPtr<SHAMapTreeNode>*>
[[nodiscard]] inline std::tuple<std::uint8_t, SHAMapHash*, SHAMapTreeNodePtr*>
TaggedPointer::getHashesAndChildren() const
{
auto const [tag, ptr] = decode();
auto const hashes = reinterpret_cast<SHAMapHash*>(ptr);
std::uint8_t const numAllocated = kBoundaries[tag];
auto const children =
reinterpret_cast<intr_ptr::SharedPtr<SHAMapTreeNode>*>(hashes + numAllocated);
auto const children = reinterpret_cast<SHAMapTreeNodePtr*>(hashes + numAllocated);
return {numAllocated, hashes, children};
};
@@ -540,7 +538,7 @@ TaggedPointer::getHashes() const
return reinterpret_cast<SHAMapHash*>(tp_ & kPtrMask);
};
[[nodiscard]] inline intr_ptr::SharedPtr<SHAMapTreeNode>*
[[nodiscard]] inline SHAMapTreeNodePtr*
TaggedPointer::getChildren() const
{
auto [unused1, unused2, result] = getHashesAndChildren();

View File

@@ -43,6 +43,15 @@ let
bintools = customBinutils;
};
# gcov ships in gcc's `cc` output, but the cc-wrapper doesn't expose it.
# Surface the gcov from our rebuilt gcc (linked against the custom glibc, so
# it runs under the loader installed in the image) and matching the exact
# compiler version, so gcovr can produce coverage reports in the CI env.
customGcov = pkgs.runCommand "gcov-custom-for-ci-env" { } ''
mkdir -p "$out/bin"
ln -s "${customGccCc}/bin/gcov" "$out/bin/gcov"
'';
# stdenv built around the rebuilt gcc / custom glibc. Used to rebuild
# compiler-rt below so its sanitizer runtimes see the custom glibc
# headers.
@@ -85,6 +94,14 @@ let
ln -s "${customCompilerRt.out}/lib" "$rsrc/lib"
ln -s "${customCompilerRt.out}/share" "$rsrc/share" || true
echo "-resource-dir=$rsrc" >> $out/nix-support/cc-cflags
# compiler-rt ships the sanitizer/profile/xray interface headers (e.g.
# <sanitizer/lsan_interface.h>) in its `dev` output. In a normal Nix
# build these reach the include path because compiler-rt is propagated
# via depsTargetTargetPropagated and stdenv's setup hooks add its
# dev/include. The CI image runs clang outside a Nix stdenv (binaries
# on PATH, no setup hooks), so that never happens; add the headers
# explicitly. gcc ships its own copy, which is why this is clang-only.
echo "-isystem ${customCompilerRt.dev}/include" >> $out/nix-support/cc-cflags
'';
};
@@ -105,11 +122,16 @@ in
name = "xrpld-ci-env";
paths = commonPackages ++ [
customGcc
customGcov
customClangForCiEnv
customBinutils
# CA certificate bundle so HTTPS clients (git, curl, conan) can verify
# TLS connections without ca-certificates being installed in the system.
pkgs.cacert
];
pathsToLink = [
"/bin"
"/etc/ssl/certs"
"/lib"
"/include"
"/share"

View File

@@ -11,12 +11,15 @@ in
ccache
cmake
conan
curlMinimal # needed for codecov/codecov-action
gcovr
git
gnumake
gnupg # needed for signing commits & codecov/codecov-action
llvmPackages_22.clang-tools
less # needed for git diff
mold
nettools # provides netstat, used to debug failures in CI
ninja
patchelf
perl # needed for openssl

View File

@@ -97,10 +97,7 @@ SHAMap::snapShot(bool isMutable) const
}
void
SHAMap::dirtyUp(
SharedPtrNodeStack& stack,
uint256 const& target,
intr_ptr::SharedPtr<SHAMapTreeNode> child)
SHAMap::dirtyUp(SharedPtrNodeStack& stack, uint256 const& target, SHAMapTreeNodePtr child)
{
// walk the tree up from through the inner nodes to the root_
// update hashes and links
@@ -165,7 +162,7 @@ SHAMap::findKey(uint256 const& id) const
return leaf;
}
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
SHAMap::fetchNodeFromDB(SHAMapHash const& hash) const
{
XRPL_ASSERT(backed_, "xrpl::SHAMap::fetchNodeFromDB : is backed");
@@ -173,7 +170,7 @@ SHAMap::fetchNodeFromDB(SHAMapHash const& hash) const
return finishFetch(hash, obj);
}
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
SHAMap::finishFetch(SHAMapHash const& hash, std::shared_ptr<NodeObject> const& object) const
{
XRPL_ASSERT(backed_, "xrpl::SHAMap::finishFetch : is backed");
@@ -208,7 +205,7 @@ SHAMap::finishFetch(SHAMapHash const& hash, std::shared_ptr<NodeObject> const& o
}
// See if a sync filter has a node
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
SHAMap::checkFilter(SHAMapHash const& hash, SHAMapSyncFilter* filter) const
{
if (auto nodeData = filter->getNode(hash))
@@ -234,7 +231,7 @@ SHAMap::checkFilter(SHAMapHash const& hash, SHAMapSyncFilter* filter) const
// Get a node without throwing
// Used on maps where missing nodes are expected
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
SHAMap::fetchNodeNT(SHAMapHash const& hash, SHAMapSyncFilter* filter) const
{
auto node = cacheLookup(hash);
@@ -257,7 +254,7 @@ SHAMap::fetchNodeNT(SHAMapHash const& hash, SHAMapSyncFilter* filter) const
return node;
}
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
SHAMap::fetchNodeNT(SHAMapHash const& hash) const
{
auto node = cacheLookup(hash);
@@ -269,7 +266,7 @@ SHAMap::fetchNodeNT(SHAMapHash const& hash) const
}
// Throw if the node is missing
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
SHAMap::fetchNode(SHAMapHash const& hash) const
{
auto node = fetchNodeNT(hash);
@@ -291,10 +288,10 @@ SHAMap::descendThrow(SHAMapInnerNode* parent, int branch) const
return ret;
}
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
SHAMap::descendThrow(SHAMapInnerNode& parent, int branch) const
{
intr_ptr::SharedPtr<SHAMapTreeNode> ret = descend(parent, branch);
SHAMapTreeNodePtr ret = descend(parent, branch);
if (!ret && !parent.isEmptyBranch(branch))
Throw<SHAMapMissingNode>(type_, parent.getChildHash(branch));
@@ -309,7 +306,7 @@ SHAMap::descend(SHAMapInnerNode* parent, int branch) const
if ((ret != nullptr) || !backed_)
return ret;
intr_ptr::SharedPtr<SHAMapTreeNode> node = fetchNodeNT(parent->getChildHash(branch));
SHAMapTreeNodePtr node = fetchNodeNT(parent->getChildHash(branch));
if (!node)
return nullptr;
@@ -317,10 +314,10 @@ SHAMap::descend(SHAMapInnerNode* parent, int branch) const
return node.get();
}
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
SHAMap::descend(SHAMapInnerNode& parent, int branch) const
{
intr_ptr::SharedPtr<SHAMapTreeNode> node = parent.getChild(branch);
SHAMapTreeNodePtr node = parent.getChild(branch);
if (node || !backed_)
return node;
@@ -334,10 +331,10 @@ SHAMap::descend(SHAMapInnerNode& parent, int branch) const
// Gets the node that would be hooked to this branch,
// but doesn't hook it up.
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
SHAMap::descendNoStore(SHAMapInnerNode& parent, int branch) const
{
intr_ptr::SharedPtr<SHAMapTreeNode> ret = parent.getChild(branch);
SHAMapTreeNodePtr ret = parent.getChild(branch);
if (!ret && backed_)
ret = fetchNode(parent.getChildHash(branch));
return ret;
@@ -361,7 +358,7 @@ SHAMap::descend(
if (child == nullptr)
{
auto const& childHash = parent->getChildHash(branch);
intr_ptr::SharedPtr<SHAMapTreeNode> childNode = fetchNodeNT(childHash, filter);
SHAMapTreeNodePtr childNode = fetchNodeNT(childHash, filter);
if (childNode)
{
@@ -434,7 +431,7 @@ SHAMap::unshareNode(intr_ptr::SharedPtr<Node> node, SHAMapNodeID const& nodeID)
SHAMapLeafNode*
SHAMap::belowHelper(
intr_ptr::SharedPtr<SHAMapTreeNode> node,
SHAMapTreeNodePtr node,
SharedPtrNodeStack& stack,
int branch,
std::tuple<int, std::function<bool(int)>, std::function<void(int&)>> const& loopParams) const
@@ -479,8 +476,7 @@ SHAMap::belowHelper(
return nullptr;
}
SHAMapLeafNode*
SHAMap::lastBelow(intr_ptr::SharedPtr<SHAMapTreeNode> node, SharedPtrNodeStack& stack, int branch)
const
SHAMap::lastBelow(SHAMapTreeNodePtr node, SharedPtrNodeStack& stack, int branch) const
{
auto init = kBranchFactor - 1;
auto cmp = [](int i) { return i >= 0; };
@@ -489,8 +485,7 @@ SHAMap::lastBelow(intr_ptr::SharedPtr<SHAMapTreeNode> node, SharedPtrNodeStack&
return belowHelper(node, stack, branch, {init, cmp, incr});
}
SHAMapLeafNode*
SHAMap::firstBelow(intr_ptr::SharedPtr<SHAMapTreeNode> node, SharedPtrNodeStack& stack, int branch)
const
SHAMap::firstBelow(SHAMapTreeNodePtr node, SharedPtrNodeStack& stack, int branch) const
{
auto init = 0;
auto cmp = [](int i) { return i <= kBranchFactor; };
@@ -699,10 +694,8 @@ SHAMap::delItem(uint256 const& id)
SHAMapNodeType const type = leaf->getType();
using TreeNodeType = intr_ptr::SharedPtr<SHAMapTreeNode>;
// What gets attached to the end of the chain (For now, nothing, since we deleted the leaf)
TreeNodeType prevNode;
SHAMapTreeNodePtr prevNode;
while (!stack.empty())
{
@@ -728,7 +721,7 @@ SHAMap::delItem(uint256 const& id)
// no children below this branch
//
// Note: This is unnecessary due to the std::move above but left here for safety
prevNode = TreeNodeType{};
prevNode = SHAMapTreeNodePtr{};
}
else if (bc == 1)
{
@@ -741,7 +734,7 @@ SHAMap::delItem(uint256 const& id)
{
if (!node->isEmptyBranch(i))
{
node->setChild(i, TreeNodeType{});
node->setChild(i, SHAMapTreeNodePtr{});
break;
}
}
@@ -937,8 +930,8 @@ SHAMap::fetchRoot(SHAMapHash const& hash, SHAMapSyncFilter* filter)
@note The node must have already been unshared by having the caller
first call SHAMapTreeNode::unshare().
*/
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMap::writeNode(NodeObjectType t, intr_ptr::SharedPtr<SHAMapTreeNode> node) const
SHAMapTreeNodePtr
SHAMap::writeNode(NodeObjectType t, SHAMapTreeNodePtr node) const
{
XRPL_ASSERT(node->cowid() == 0, "xrpl::SHAMap::writeNode : valid input node");
XRPL_ASSERT(backed_, "xrpl::SHAMap::writeNode : is backed");
@@ -1155,7 +1148,7 @@ SHAMap::dump(bool hash) const
JLOG(journal_.info()) << leafCount << " resident leaves";
}
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
SHAMap::cacheLookup(SHAMapHash const& hash) const
{
auto ret = f_.getTreeNodeCache()->fetch(hash.asUInt256());
@@ -1164,7 +1157,7 @@ SHAMap::cacheLookup(SHAMapHash const& hash) const
}
void
SHAMap::canonicalize(SHAMapHash const& hash, intr_ptr::SharedPtr<SHAMapTreeNode>& node) const
SHAMap::canonicalize(SHAMapHash const& hash, SHAMapTreeNodePtr& node) const
{
XRPL_ASSERT(backed_, "xrpl::SHAMap::canonicalize : is backed");
XRPL_ASSERT(node->cowid() == 0, "xrpl::SHAMap::canonicalize : valid node input");

View File

@@ -261,7 +261,7 @@ SHAMap::walkMap(std::vector<SHAMapMissingNode>& missingNodes, int maxMissing) co
{
if (!node->isEmptyBranch(i))
{
intr_ptr::SharedPtr<SHAMapTreeNode> const nextNode = descendNoStore(*node, i);
SHAMapTreeNodePtr const nextNode = descendNoStore(*node, i);
if (nextNode)
{
@@ -286,7 +286,7 @@ SHAMap::walkMapParallel(std::vector<SHAMapMissingNode>& missingNodes, int maxMis
return false;
using StackEntry = intr_ptr::SharedPtr<SHAMapInnerNode>;
std::array<intr_ptr::SharedPtr<SHAMapTreeNode>, 16> topChildren;
std::array<SHAMapTreeNodePtr, 16> topChildren;
{
auto const& innerRoot = intr_ptr::staticPointerCast<SHAMapInnerNode>(root_);
for (int i = 0; i < 16; ++i)
@@ -331,8 +331,7 @@ SHAMap::walkMapParallel(std::vector<SHAMapMissingNode>& missingNodes, int maxMis
{
if (node->isEmptyBranch(i))
continue;
intr_ptr::SharedPtr<SHAMapTreeNode> const nextNode =
descendNoStore(*node, i);
SHAMapTreeNodePtr const nextNode = descendNoStore(*node, i);
if (nextNode)
{

View File

@@ -37,7 +37,7 @@ SHAMapInnerNode::~SHAMapInnerNode() = default;
void
SHAMapInnerNode::partialDestructor()
{
intr_ptr::SharedPtr<SHAMapTreeNode>* children = nullptr;
SHAMapTreeNodePtr* children = nullptr;
// structured bindings can't be captured in c++ 17; use tie instead
std::tie(std::ignore, std::ignore, children) = hashesAndChildren_.getHashesAndChildren();
iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) { children[indexNum].reset(); });
@@ -69,7 +69,7 @@ SHAMapInnerNode::getChildIndex(int i) const
return hashesAndChildren_.getChildIndex(isBranch_, i);
}
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
SHAMapInnerNode::clone(std::uint32_t cowid) const
{
auto const branchCount = getBranchCount();
@@ -78,8 +78,10 @@ SHAMapInnerNode::clone(std::uint32_t cowid) const
p->hash_ = hash_;
p->isBranch_ = isBranch_;
p->fullBelowGen_ = fullBelowGen_;
SHAMapHash *cloneHashes = nullptr, *thisHashes = nullptr;
intr_ptr::SharedPtr<SHAMapTreeNode>*cloneChildren = nullptr, *thisChildren = nullptr;
SHAMapHash* cloneHashes = nullptr;
SHAMapHash* thisHashes = nullptr;
SHAMapTreeNodePtr* cloneChildren = nullptr;
SHAMapTreeNodePtr* thisChildren = nullptr;
// structured bindings can't be captured in c++ 17; use tie instead
std::tie(std::ignore, cloneHashes, cloneChildren) =
p->hashesAndChildren_.getHashesAndChildren();
@@ -118,7 +120,7 @@ SHAMapInnerNode::clone(std::uint32_t cowid) const
return p;
}
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
SHAMapInnerNode::makeFullInner(Slice data, SHAMapHash const& hash, bool hashValid)
{
// A full inner node is serialized as 16 256-bit hashes, back to back:
@@ -153,7 +155,7 @@ SHAMapInnerNode::makeFullInner(Slice data, SHAMapHash const& hash, bool hashVali
return ret;
}
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
SHAMapInnerNode::makeCompressedInner(Slice data)
{
// A compressed inner node is serialized as a series of 33 byte chunks,
@@ -207,7 +209,7 @@ void
SHAMapInnerNode::updateHashDeep()
{
SHAMapHash* hashes = nullptr;
intr_ptr::SharedPtr<SHAMapTreeNode>* children = nullptr;
SHAMapTreeNodePtr* children = nullptr;
// structured bindings can't be captured in c++ 17; use tie instead
std::tie(std::ignore, hashes, children) = hashesAndChildren_.getHashesAndChildren();
iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
@@ -265,7 +267,7 @@ SHAMapInnerNode::getString(SHAMapNodeID const& id) const
// We are modifying an inner node
void
SHAMapInnerNode::setChild(int m, intr_ptr::SharedPtr<SHAMapTreeNode> child)
SHAMapInnerNode::setChild(int m, SHAMapTreeNodePtr child)
{
XRPL_ASSERT(
(m >= 0) && (m < kBranchFactor), "xrpl::SHAMapInnerNode::setChild : valid branch input");
@@ -307,7 +309,7 @@ SHAMapInnerNode::setChild(int m, intr_ptr::SharedPtr<SHAMapTreeNode> child)
// finished modifying, now make shareable
void
SHAMapInnerNode::shareChild(int m, intr_ptr::SharedPtr<SHAMapTreeNode> const& child)
SHAMapInnerNode::shareChild(int m, SHAMapTreeNodePtr const& child)
{
XRPL_ASSERT(
(m >= 0) && (m < kBranchFactor), "xrpl::SHAMapInnerNode::shareChild : valid branch input");
@@ -337,7 +339,7 @@ SHAMapInnerNode::getChildPointer(int branch)
return hashesAndChildren_.getChildren()[index].get();
}
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
SHAMapInnerNode::getChild(int branch)
{
XRPL_ASSERT(
@@ -365,8 +367,8 @@ SHAMapInnerNode::getChildHash(int m) const
return kZeroShaMapHash;
}
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapInnerNode::canonicalizeChild(int branch, intr_ptr::SharedPtr<SHAMapTreeNode> node)
SHAMapTreeNodePtr
SHAMapInnerNode::canonicalizeChild(int branch, SHAMapTreeNodePtr node)
{
XRPL_ASSERT(
branch >= 0 && branch < kBranchFactor,

View File

@@ -66,7 +66,7 @@ SHAMap::visitNodes(std::function<bool(SHAMapTreeNode&)> const& function) const
{
if (!node->isEmptyBranch(pos))
{
intr_ptr::SharedPtr<SHAMapTreeNode> const child = descendNoStore(*node, pos);
SHAMapTreeNodePtr const child = descendNoStore(*node, pos);
if (!function(*child))
return;
@@ -204,8 +204,7 @@ SHAMap::gmnProcessNodes(MissingNodes& mn, MissingNodes::StackEntry& se)
branch,
mn.filter,
pending,
[node, nodeID, branch, &mn](
intr_ptr::SharedPtr<SHAMapTreeNode> found, SHAMapHash const&) {
[node, nodeID, branch, &mn](SHAMapTreeNodePtr found, SHAMapHash const&) {
// a read completed asynchronously
std::unique_lock<std::mutex> const lock{mn.deferLock};
mn.finishedReads.emplace_back(node, nodeID, branch, std::move(found));
@@ -266,8 +265,7 @@ SHAMap::gmnProcessDeferredReads(MissingNodes& mn)
int complete = 0;
while (complete != mn.deferred)
{
std::tuple<SHAMapInnerNode*, SHAMapNodeID, int, intr_ptr::SharedPtr<SHAMapTreeNode>>
deferredNode;
std::tuple<SHAMapInnerNode*, SHAMapNodeID, int, SHAMapTreeNodePtr> deferredNode;
{
std::unique_lock<std::mutex> lock{mn.deferLock};

View File

@@ -25,7 +25,7 @@
namespace xrpl {
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
SHAMapTreeNode::makeTransaction(Slice data, SHAMapHash const& hash, bool hashValid)
{
if (data.size() < kMinShaMapItemBytes)
@@ -43,7 +43,7 @@ SHAMapTreeNode::makeTransaction(Slice data, SHAMapHash const& hash, bool hashVal
return intr_ptr::makeShared<SHAMapTxLeafNode>(std::move(item), 0);
}
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
SHAMapTreeNode::makeTransactionWithMeta(Slice data, SHAMapHash const& hash, bool hashValid)
{
Serializer s(data.data(), data.size());
@@ -83,7 +83,7 @@ SHAMapTreeNode::makeTransactionWithMeta(Slice data, SHAMapHash const& hash, bool
return intr_ptr::makeShared<SHAMapTxPlusMetaLeafNode>(std::move(item), 0);
}
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
SHAMapTreeNode::makeAccountState(Slice data, SHAMapHash const& hash, bool hashValid)
{
Serializer s(data.data(), data.size());
@@ -124,7 +124,7 @@ SHAMapTreeNode::makeAccountState(Slice data, SHAMapHash const& hash, bool hashVa
return intr_ptr::makeShared<SHAMapAccountStateLeafNode>(std::move(item), 0);
}
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
SHAMapTreeNode::makeFromWire(Slice rawNode)
{
if (rawNode.empty())
@@ -155,7 +155,7 @@ SHAMapTreeNode::makeFromWire(Slice rawNode)
Throw<std::runtime_error>("wire: Unknown type (" + std::to_string(type) + ")");
}
intr_ptr::SharedPtr<SHAMapTreeNode>
SHAMapTreeNodePtr
SHAMapTreeNode::makeFromPrefix(Slice rawNode, SHAMapHash const& hash)
{
if (rawNode.size() < 4)

View File

@@ -4,6 +4,7 @@
#include <xrpl/basics/base_uint.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/NFTokenHelpers.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/Protocol.h>
@@ -21,8 +22,14 @@ namespace xrpl {
NotTEC
NFTokenCancelOffer::preflight(PreflightContext const& ctx)
{
if (auto const& ids = ctx.tx[sfNFTokenOffers];
ids.empty() || (ids.size() > kMaxTokenOfferCancelCount))
auto const& offerIds = ctx.tx[sfNFTokenOffers];
if (offerIds.empty() || (offerIds.size() > kMaxTokenOfferCancelCount))
return temMALFORMED;
// Zero offer IDs cannot be passed as ledger entry keys.
if (ctx.rules.enabled(fixCleanup3_2_0) &&
std::ranges::any_of(offerIds, [](uint256 const& id) { return id.isZero(); }))
return temMALFORMED;
// In order to prevent unnecessarily overlarge transactions, we

View File

@@ -1,5 +1,6 @@
#include <test/jtx/Account.h>
#include <test/jtx/CaptureLogs.h>
#include <test/jtx/Env.h>
#include <test/jtx/JTx.h>
#include <test/jtx/TestHelpers.h>
@@ -52,6 +53,7 @@
#include <array>
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <string_view>
#include <tuple>
@@ -1708,7 +1710,8 @@ class LoanBroker_test : public beast::unit_test::Suite
Account const alice("alice");
auto const withFix = features[fixCleanup3_2_0];
Env env(*this, features);
std::string logs;
Env env(*this, features, std::make_unique<CaptureLogs>(&logs));
env.fund(XRP(100'000), issuer, alice);
env.close();

View File

@@ -892,6 +892,25 @@ class NFTokenBaseUtil_test : public beast::unit_test::Suite
BEAST_EXPECT(ownerCount(env, buyer) == 1);
}
// Only test this with fixCleanup3_2_0 enabled. Without the fix,
// an assert-enabled build can crash when Ledger::read() receives
// a zero-key offer ID.
if (features[fixCleanup3_2_0])
{
// Zero is not a valid offer ID.
env(token::cancelOffer(buyer, {uint256{}}), Ter(temMALFORMED));
env.close();
BEAST_EXPECT(ownerCount(env, buyer) == 1);
// List of offer IDs containing zero is invalid.
// craftedIndex is not a valid offer index but it is not zero.
auto const craftedIndex = keylet::nftoffer(gw, env.seq(gw)).key;
env(token::cancelOffer(buyer, {buyerOfferIndex, uint256{}, craftedIndex}),
Ter(temMALFORMED));
env.close();
BEAST_EXPECT(ownerCount(env, buyer) == 1);
}
// List of tokens to delete is too long.
{
std::vector<uint256> const offers(kMaxTokenOfferCancelCount + 1, buyerOfferIndex);

View File

@@ -1,6 +1,7 @@
#include <test/jtx/AMM.h>
#include <test/jtx/AMMTest.h>
#include <test/jtx/Account.h>
#include <test/jtx/CaptureLogs.h>
#include <test/jtx/Env.h>
#include <test/jtx/TestHelpers.h>
#include <test/jtx/amount.h>
@@ -60,6 +61,7 @@
#include <cstdint>
#include <functional>
#include <limits>
#include <memory>
#include <optional>
#include <string>
#include <tuple>
@@ -6630,7 +6632,8 @@ class Vault_test : public beast::unit_test::Suite
"fixed-asset amount with impaired loan"} +
(withFix ? " (fixCleanup3_2_0)" : " (pre-fix)"));
Env env(*this, features);
std::string logs;
Env env(*this, features, std::make_unique<test::CaptureLogs>(&logs));
auto const f = setupStuckDepositor(env);
if (!f.vaultKeylet || !f.asset || f.sharesLender == 0)
{
@@ -6748,7 +6751,8 @@ class Vault_test : public beast::unit_test::Suite
"burn is rejected while loss outstanding"} +
(withFix ? " (fixCleanup3_2_0)" : " (pre-fix)"));
Env env(*this, features);
std::string logs;
Env env(*this, features, std::make_unique<test::CaptureLogs>(&logs));
auto const f = setupStuckDepositor(env);
if (!f.vaultKeylet || f.sharesLender == 0)
{
@@ -7074,7 +7078,8 @@ class Vault_test : public beast::unit_test::Suite
using namespace test::jtx;
auto runScenario = [this](FeatureBitset features, TER expected) {
Env env(*this, features);
std::string logs;
Env env(*this, features, std::make_unique<test::CaptureLogs>(&logs));
Account const issuer{"issuer"};
Account const alice{"alice"};
@@ -7150,7 +7155,8 @@ class Vault_test : public beast::unit_test::Suite
using namespace test::jtx;
auto runScenario = [this](FeatureBitset features, TER expected) {
Env env(*this, features);
std::string logs;
Env env(*this, features, std::make_unique<test::CaptureLogs>(&logs));
Account const issuer{"issuer"};
Account const alice{"alice"};
@@ -7226,7 +7232,8 @@ class Vault_test : public beast::unit_test::Suite
enum class DestKind : bool { ThirdParty = false, Self = true };
auto runScenario = [this](FeatureBitset features, DestKind destKind, TER expected) {
Env env(*this, features);
std::string logs;
Env env(*this, features, std::make_unique<test::CaptureLogs>(&logs));
Account const issuer{"issuer"};
Account const alice{"alice"};
@@ -7331,7 +7338,8 @@ class Vault_test : public beast::unit_test::Suite
using namespace test::jtx;
auto runScenario = [this](FeatureBitset features, TER expected) {
Env env(*this, features);
std::string logs;
Env env(*this, features, std::make_unique<test::CaptureLogs>(&logs));
Account const issuer{"issuer"};
Account const alice{"alice"};
@@ -7414,7 +7422,8 @@ class Vault_test : public beast::unit_test::Suite
{
using namespace test::jtx;
auto runScenario = [this](FeatureBitset features, TER expected) {
Env env(*this, features);
std::string logs;
Env env(*this, features, std::make_unique<test::CaptureLogs>(&logs));
Account const issuer{"issuer"};
Account const alice{"alice"};
@@ -7489,7 +7498,8 @@ class Vault_test : public beast::unit_test::Suite
using namespace test::jtx;
auto runScenario = [this](FeatureBitset features, TER expected) {
Env env(*this, features);
std::string logs;
Env env(*this, features, std::make_unique<test::CaptureLogs>(&logs));
Account const issuer{"issuer"};
Account const owner{"owner"};