mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-09 11:46:49 +00:00
Compare commits
9 Commits
pratik/Ret
...
tapanito/c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11e33560fd | ||
|
|
67037782af | ||
|
|
6c543426c3 | ||
|
|
e5cf1a0985 | ||
|
|
023bdaeeed | ||
|
|
96b2c0964f | ||
|
|
1441d4690d | ||
|
|
225ed204ad | ||
|
|
ad111bcc22 |
@@ -134,6 +134,7 @@ words:
|
||||
- iou
|
||||
- ious
|
||||
- isrdc
|
||||
- isystem
|
||||
- itype
|
||||
- jemalloc
|
||||
- jlog
|
||||
|
||||
34
docker/check-tools.sh
Executable file
34
docker/check-tools.sh
Executable file
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
# Verify that every tool expected in the Nix CI env is present and runnable.
|
||||
set -euo pipefail
|
||||
|
||||
ccache --version
|
||||
clang --version
|
||||
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
|
||||
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}"
|
||||
89
docker/install-sanitizer-libs.sh
Executable file
89
docker/install-sanitizer-libs.sh
Executable file
@@ -0,0 +1,89 @@
|
||||
#!/bin/bash
|
||||
# Install sanitizer runtime libraries required to run binaries compiled with:
|
||||
# -fsanitize=address → libasan.so.8
|
||||
# -fsanitize=thread → libtsan.so.2
|
||||
# -fsanitize=undefined → libubsan.so.1
|
||||
#
|
||||
# The exact SONAMEs required depend on the compiler toolchain used to build the
|
||||
# test binaries (see nix/ci-env.nix). If the toolchain is bumped and SONAMEs
|
||||
# change, update the list below (or detect them from the binaries).
|
||||
#
|
||||
# Supported base images:
|
||||
# debian:bookworm
|
||||
# ubuntu:20.04
|
||||
# rhel:9
|
||||
# nixos/nix — tests are skipped; this script is not called
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [ ! -f /etc/os-release ]; then
|
||||
echo "ERROR: /etc/os-release not found; cannot detect OS" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
. /etc/os-release
|
||||
|
||||
echo "Detected OS: ${ID} ${VERSION_ID:-}"
|
||||
|
||||
case "${ID}" in
|
||||
debian)
|
||||
apt-get update -y
|
||||
apt-get install -y --no-install-recommends \
|
||||
libasan8 \
|
||||
libtsan2 \
|
||||
libubsan1
|
||||
|
||||
apt-get clean
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
;;
|
||||
|
||||
ubuntu)
|
||||
apt-get update -y
|
||||
apt-get install -y --no-install-recommends \
|
||||
gnupg \
|
||||
software-properties-common
|
||||
add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
apt-get update -y
|
||||
apt-get install -y --no-install-recommends \
|
||||
libasan8 \
|
||||
libtsan2 \
|
||||
libubsan1
|
||||
|
||||
apt-get clean
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
;;
|
||||
|
||||
rhel | centos | rocky | almalinux)
|
||||
dnf install -y \
|
||||
libasan8 \
|
||||
libtsan2 \
|
||||
libubsan
|
||||
|
||||
dnf clean -y all
|
||||
rm -rf /var/cache/dnf/*
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "ERROR: unsupported OS '${ID}'. Supported: debian, ubuntu, rhel-family" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Verify that every expected library is now resolvable by the dynamic linker.
|
||||
missing=0
|
||||
for lib in libasan.so.8 libtsan.so.2 libubsan.so.1; do
|
||||
if ldconfig -p | grep -q "${lib}"; then
|
||||
echo "OK: ${lib} found"
|
||||
else
|
||||
echo "ERROR: ${lib} not found after installation" >&2
|
||||
missing=$((missing + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "${missing}" -ne 0 ]; then
|
||||
echo "ERROR: ${missing} library/libraries missing" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "All sanitizer runtime libraries installed successfully."
|
||||
@@ -32,7 +32,7 @@ FROM ${BASE_IMAGE} AS final
|
||||
ARG BASE_IMAGE
|
||||
|
||||
# bash is not located at /bin/bash in nixos/nix, so we need to create a symlink to it.
|
||||
RUN if [ -d /nix ]; then \
|
||||
RUN if echo "${BASE_IMAGE}" | grep -qiE 'nixos'; then \
|
||||
ln -s /root/.nix-profile/bin/bash /bin/bash; \
|
||||
fi
|
||||
|
||||
@@ -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,38 +71,44 @@ if [ ! -e "${target}" ]; then
|
||||
fi
|
||||
EOF
|
||||
|
||||
RUN <<EOF
|
||||
ccache --version
|
||||
clang --version
|
||||
clang++ --version
|
||||
clang-format --version
|
||||
cmake --version
|
||||
conan --version
|
||||
g++ --version
|
||||
gcc --version
|
||||
gcovr --version
|
||||
git --version
|
||||
make --version
|
||||
mold --version
|
||||
ninja --version
|
||||
perl --version
|
||||
pkg-config --version
|
||||
pre-commit --version
|
||||
python3 --version
|
||||
run-clang-tidy --help
|
||||
vim --version
|
||||
EOF
|
||||
COPY docker/check-tools.sh /tmp/check-tools.sh
|
||||
RUN /tmp/check-tools.sh
|
||||
|
||||
# Sanity-check that the sanitizer runtimes shipped with g++/clang++ are able to build binaries
|
||||
# Sanity-check that the g++/clang++ are able to build binaries, including sanitizer-instrumented ones.
|
||||
COPY docker/test_files/cpp_sources/ /tmp/cpp_sources/
|
||||
COPY docker/test_files/compile-cpp-sources.sh /tmp/compile-cpp-sources.sh
|
||||
RUN /tmp/compile-cpp-sources.sh /tmp/cpp_sources /tmp/bins
|
||||
|
||||
# Sanity-check that the built binaries are able to run.
|
||||
# We only support running the test binaries on Ubuntu and NixOS right now (will be fixed in the future)
|
||||
#
|
||||
# When build and test images will be separate, we will be to run on vanilla images.
|
||||
COPY docker/test_files/run-test-binaries.sh /tmp/run-test-binaries.sh
|
||||
RUN if echo "${BASE_IMAGE}" | grep -qiE '(ubuntu|nixos)'; then \
|
||||
/tmp/run-test-binaries.sh /tmp/bins; \
|
||||
# Tester: start from a clean BASE_IMAGE, install sanitizer runtime libraries,
|
||||
# and run the compiled test binaries to verify they execute correctly.
|
||||
FROM ${BASE_IMAGE} AS tester
|
||||
|
||||
ARG BASE_IMAGE
|
||||
|
||||
# bash is not located at /bin/bash in nixos/nix, so we need to create a symlink to it.
|
||||
RUN if echo "${BASE_IMAGE}" | grep -qiE 'nixos'; then \
|
||||
ln -s /root/.nix-profile/bin/bash /bin/bash; \
|
||||
fi
|
||||
|
||||
SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"]
|
||||
|
||||
# Sanity-check that the built binaries run correctly in the vanilla base image, with the necessary sanitizer runtime libraries installed.
|
||||
COPY docker/install-sanitizer-libs.sh /tmp/install-sanitizer-libs.sh
|
||||
COPY docker/test_files/run-test-binaries.sh /tmp/run-test-binaries.sh
|
||||
COPY --from=final /tmp/bins /tmp/bins
|
||||
|
||||
RUN <<EOF
|
||||
if echo "${BASE_IMAGE}" | grep -qiE 'nixos'; then
|
||||
echo "Skipping runnning binaries on NixOS."
|
||||
else
|
||||
/tmp/install-sanitizer-libs.sh
|
||||
/tmp/run-test-binaries.sh /tmp/bins
|
||||
fi
|
||||
touch /tmp/tests-passed
|
||||
EOF
|
||||
|
||||
# Output: the final image, gated on a successful test run in the tester stage.
|
||||
# Copying the sentinel from tester creates a hard build dependency: if the test
|
||||
# run above failed, this stage — and the overall build — fails too.
|
||||
FROM final
|
||||
COPY --from=tester /tmp/tests-passed /tmp/tests-passed
|
||||
|
||||
@@ -20,14 +20,21 @@ function compile() {
|
||||
local src="${src_dir}/${name}.cpp"
|
||||
local binary="${dst_dir}/${name}-${compiler}"
|
||||
|
||||
echo "=== Compile ${name} with ${compiler} ==="
|
||||
cmd="${compiler} -std=c++23 -O1 -g \
|
||||
echo "=== Compiling ${name} with ${compiler} ==="
|
||||
# Always statically link libstdc++ so the test binary does not depend on
|
||||
# the host's libstdc++.so.6 version.
|
||||
local compile_cmd="${compiler} -std=c++23 -O1 -g \
|
||||
-pthread \
|
||||
-Wl,--dynamic-linker=${loader} \
|
||||
-static-libstdc++ \
|
||||
${san_flag} \
|
||||
${src} -o ${binary}"
|
||||
echo "Command: ${cmd}"
|
||||
eval "${cmd}"
|
||||
echo "Compile cmd: ${compile_cmd}"
|
||||
eval "${compile_cmd}"
|
||||
|
||||
echo "=== Patching ${binary} to use ${loader} as PT_INTERP ==="
|
||||
local patch_cmd="patchelf --set-interpreter ${loader} --remove-rpath ${binary}"
|
||||
echo "Patch cmd: ${patch_cmd}"
|
||||
eval "${patch_cmd}"
|
||||
}
|
||||
|
||||
declare -A sanitize=(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -7,6 +7,8 @@ set -eo pipefail
|
||||
|
||||
bins_dir="${1:?usage: $0 <bins_dir>}"
|
||||
|
||||
failed_binaries=()
|
||||
|
||||
# Run a binary and verify its exit code and output.
|
||||
# Usage: run <binary> <expected_output> <expected_rc>
|
||||
function run() {
|
||||
@@ -18,27 +20,34 @@ function run() {
|
||||
out_file="$(mktemp)"
|
||||
|
||||
echo "=== Run ${binary} ==="
|
||||
local rc=0
|
||||
"${binary}" >"${out_file}" 2>&1 || rc=$?
|
||||
set +e
|
||||
"${binary}" >"${out_file}" 2>&1
|
||||
local rc=$?
|
||||
set -e
|
||||
|
||||
cat "${out_file}"
|
||||
|
||||
local failed=0
|
||||
if [ "${expected_rc}" = "nonzero" ]; then
|
||||
if [ "${rc}" -eq 0 ]; then
|
||||
echo "ERROR: expected non-zero exit code from ${binary}, got ${rc}" >&2
|
||||
exit 1
|
||||
failed=1
|
||||
fi
|
||||
elif [ "${rc}" -ne "${expected_rc}" ]; then
|
||||
echo "ERROR: expected exit code ${expected_rc} from ${binary}, got ${rc}" >&2
|
||||
exit 1
|
||||
failed=1
|
||||
fi
|
||||
|
||||
grep -q "${expected_output}" "${out_file}" ||
|
||||
{
|
||||
echo "ERROR: expected '${expected_output}' from ${binary}" >&2
|
||||
exit 1
|
||||
}
|
||||
echo "OK: '${expected_output}' detected"
|
||||
if ! grep -q "${expected_output}" "${out_file}"; then
|
||||
echo "ERROR: expected '${expected_output}' from ${binary}" >&2
|
||||
failed=1
|
||||
fi
|
||||
|
||||
if [ "${failed}" -eq 0 ]; then
|
||||
echo "OK: '${expected_output}' detected"
|
||||
else
|
||||
failed_binaries+=("${binary}")
|
||||
fi
|
||||
}
|
||||
|
||||
declare -A expect=(
|
||||
@@ -52,6 +61,15 @@ declare -A expect=(
|
||||
for compiler in g++ clang++; do
|
||||
for name in regular asan tsan ubsan; do
|
||||
binary="${bins_dir}/${name}-${compiler}"
|
||||
|
||||
if [ "${name}" = "tsan" ] && [ "${compiler}" = "g++" ] &&
|
||||
grep -qi 'debian' /etc/os-release 2>/dev/null &&
|
||||
[ "$(uname -m)" = "aarch64" ]; then
|
||||
echo "=== Skipping ${binary} (tsan-g++ unsupported on Debian ARM64) ==="
|
||||
echo " NOTE: to enable it, add --security-opt seccomp=unconfined to your docker run command"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "${name}" = "regular" ]; then
|
||||
expected_rc=0
|
||||
else
|
||||
@@ -60,3 +78,9 @@ for compiler in g++ clang++; do
|
||||
run "${binary}" "${expect[$name]}" "${expected_rc}"
|
||||
done
|
||||
done
|
||||
|
||||
if [ "${#failed_binaries[@]}" -gt 0 ]; then
|
||||
echo "ERROR: the following binaries failed:" >&2
|
||||
printf ' %s\n' "${failed_binaries[@]}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
6
flake.lock
generated
6
flake.lock
generated
@@ -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": {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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_);
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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_);
|
||||
|
||||
@@ -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_);
|
||||
|
||||
@@ -11,5 +11,5 @@ using TreeNodeCache = TaggedCache<
|
||||
SHAMapTreeNode,
|
||||
/*IsKeyCache*/ false,
|
||||
intr_ptr::SharedWeakUnionPtr<SHAMapTreeNode>,
|
||||
intr_ptr::SharedPtr<SHAMapTreeNode>>;
|
||||
SHAMapTreeNodePtr>;
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -103,23 +103,7 @@ public:
|
||||
view_->rawDestroyXRP(fee);
|
||||
}
|
||||
|
||||
/** Applies all invariant checkers one by one.
|
||||
|
||||
@param result the result generated by processing this transaction.
|
||||
@param fee the fee charged for this transaction
|
||||
@return the result code that should be returned for this transaction.
|
||||
*/
|
||||
TER
|
||||
checkInvariants(TER const result, XRPAmount const fee);
|
||||
|
||||
private:
|
||||
static TER
|
||||
failInvariantCheck(TER const result);
|
||||
|
||||
template <std::size_t... Is>
|
||||
TER
|
||||
checkInvariantsHelper(TER const result, XRPAmount const fee, std::index_sequence<Is...>);
|
||||
|
||||
OpenView& base_;
|
||||
ApplyFlags flags_;
|
||||
std::optional<ApplyViewImpl> view_;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/ApplyContext.h>
|
||||
#include <xrpl/tx/applySteps.h>
|
||||
#include <xrpl/tx/invariants/CheckInvariants.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
@@ -142,19 +143,30 @@ public:
|
||||
return ctx_.view();
|
||||
}
|
||||
|
||||
/** Whether to run the transaction-specific invariant check.
|
||||
*
|
||||
* After a fee-claim reset the transaction's effects have been rolled back,
|
||||
* so only the protocol invariants are meaningful; pass @c Yes to skip the
|
||||
* transaction-specific check in that case.
|
||||
*/
|
||||
enum class SkipTxInvariants : bool { No = false, Yes = true };
|
||||
|
||||
/** Check all invariants for the current transaction.
|
||||
*
|
||||
* Runs transaction-specific invariants first (visitInvariantEntry +
|
||||
* finalizeInvariants), then protocol-level invariants. Both layers
|
||||
* always run; the worst failure code is returned.
|
||||
* Delegates to the free @c xrpl::checkInvariants runner. Unless @p skip is
|
||||
* @c SkipTxInvariants::Yes, the transaction-specific adapter is passed so
|
||||
* both layers share a single walk of the modified ledger entries.
|
||||
* Protocol faults (tefINVARIANT_FAILED) take priority over transaction
|
||||
* faults (tecINVARIANT_FAILED).
|
||||
*
|
||||
* @param result the tentative TER from transaction processing.
|
||||
* @param fee the fee consumed by the transaction.
|
||||
* @param skip whether to skip the transaction-specific invariant check.
|
||||
*
|
||||
* @return the final TER after all invariant checks.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
checkInvariants(TER result, XRPAmount fee);
|
||||
checkInvariants(TER result, XRPAmount fee, SkipTxInvariants skip);
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/*
|
||||
@@ -287,7 +299,7 @@ protected:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) = 0;
|
||||
beast::Journal const& j) const = 0;
|
||||
|
||||
/** Compute the minimum fee required to process a transaction
|
||||
with a given baseFee based on the current server load.
|
||||
@@ -404,20 +416,34 @@ private:
|
||||
static NotTEC
|
||||
preflightUniversal(PreflightContext const& ctx);
|
||||
|
||||
/** Check transaction-specific invariants only.
|
||||
*
|
||||
* Walks every modified ledger entry via visitInvariantEntry, then
|
||||
* calls finalizeInvariants on the derived transactor. Returns
|
||||
* tecINVARIANT_FAILED if any transaction invariant is violated.
|
||||
*
|
||||
* @param result the tentative TER from transaction processing.
|
||||
* @param fee the fee consumed by the transaction.
|
||||
*
|
||||
* @return the original result if all invariants pass, or
|
||||
* tecINVARIANT_FAILED otherwise.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
checkTransactionInvariants(TER result, XRPAmount fee);
|
||||
/** Bridges the transaction-specific two-phase invariant hooks
|
||||
* (visitInvariantEntry + finalizeInvariants) into the InvariantCheck
|
||||
* interface consumed by the free xrpl::checkInvariants runner. */
|
||||
class InvariantCheckAdapter : public InvariantCheck
|
||||
{
|
||||
Transactor& self_;
|
||||
|
||||
public:
|
||||
explicit InvariantCheckAdapter(Transactor& self) : self_(self)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
|
||||
{
|
||||
self_.visitInvariantEntry(isDelete, before, after);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
finalize(
|
||||
STTx const& tx,
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
InvariantCheckAdapter invariantCheck_{*this};
|
||||
};
|
||||
|
||||
inline bool
|
||||
|
||||
129
include/xrpl/tx/invariants/CheckInvariants.h
Normal file
129
include/xrpl/tx/invariants/CheckInvariants.h
Normal file
@@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/ApplyContext.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* @brief Runtime interface for a transaction-specific invariant check.
|
||||
*
|
||||
* The free @c checkInvariants runner drives two layers of checks over a single
|
||||
* walk of the modified ledger entries:
|
||||
*
|
||||
* - **Protocol checks** are the concrete types in @c InvariantChecks, held in
|
||||
* a @c std::tuple and dispatched statically by a compile-time fold (no
|
||||
* virtual calls). They are duck-typed against the two-phase contract
|
||||
* described below; see @c InvariantChecker_PROTOTYPE in InvariantCheck.h.
|
||||
* - **The transaction-specific check** is injected at runtime through this
|
||||
* interface, so the runner can call it without depending on the concrete
|
||||
* transactor type.
|
||||
*
|
||||
* Both layers honour the same two-phase protocol:
|
||||
*
|
||||
* **Phase 1 — state collection** (`visitEntry`)
|
||||
* Called once for each ledger entry created, modified, or deleted by the
|
||||
* transaction. Implementations accumulate whatever state they need to
|
||||
* evaluate their post-conditions. Must not throw.
|
||||
*
|
||||
* **Phase 2 — condition evaluation** (`finalize`)
|
||||
* Called once after every modified entry has been visited. Returns true if
|
||||
* all post-conditions hold, false to fail the transaction.
|
||||
*
|
||||
* ## Rules for implementing `finalize`
|
||||
*
|
||||
* ### Invariants must run regardless of transaction result
|
||||
*
|
||||
* `finalize` MUST perform meaningful checks even when the transaction has
|
||||
* failed (`!isTesSuccess(result)`). A bug or exploit could cause a failed
|
||||
* transaction to mutate ledger state in unexpected ways; invariants are the
|
||||
* last line of defense.
|
||||
*
|
||||
* The typical pattern: an invariant that expects a domain-specific state
|
||||
* change (e.g. a Vault being created) should expect that change only when
|
||||
* the transaction succeeded. A failed VaultCreate must not have created a
|
||||
* Vault.
|
||||
*
|
||||
* ### Privilege-gated checks apply to failed transactions too
|
||||
*
|
||||
* Failed transactions carry no privileges. Any privilege-gated assertion
|
||||
* must therefore also be enforced for failed transactions.
|
||||
*/
|
||||
class InvariantCheck
|
||||
{
|
||||
public:
|
||||
virtual ~InvariantCheck() = default;
|
||||
|
||||
/**
|
||||
* @brief Called for each ledger entry modified by the transaction.
|
||||
*
|
||||
* @param isDelete true if the SLE is being deleted.
|
||||
* @param before the entry's state before the transaction (nullptr for
|
||||
* newly created entries).
|
||||
* @param after the entry's state after the transaction. For deletions
|
||||
* this is the SLE being erased; use @p isDelete rather than
|
||||
* `after == nullptr` to detect deletions.
|
||||
*/
|
||||
virtual void
|
||||
visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) = 0;
|
||||
|
||||
/**
|
||||
* @brief Called after all entries have been visited.
|
||||
*
|
||||
* @param tx the transaction being applied.
|
||||
* @param result the tentative TER result of the transaction.
|
||||
* @param fee the fee consumed by the transaction.
|
||||
* @param view read-only view of the ledger after the transaction.
|
||||
* @param j journal for logging invariant failures.
|
||||
* @return true if all invariants hold; false to fail with
|
||||
* tecINVARIANT_FAILED / tefINVARIANT_FAILED.
|
||||
*/
|
||||
[[nodiscard]] virtual bool
|
||||
finalize(
|
||||
STTx const& tx,
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Run all protocol invariant checks plus the transaction-specific check
|
||||
* in a single pass over the modified entries.
|
||||
*
|
||||
* Both layers share one walk of the modified-entry set: @p txCheck's
|
||||
* `visitEntry` accumulates state on the same traversal that drives the
|
||||
* protocol checkers, then both layers' `finalize` run on the complete state.
|
||||
* Protocol faults (tefINVARIANT_FAILED) take precedence over transaction
|
||||
* faults (tecINVARIANT_FAILED).
|
||||
*
|
||||
* Every transaction is guaranteed to supply a @p txCheck (either a real check
|
||||
* or a no-op stub). The invariant contract requires @c finalize to handle
|
||||
* failed results gracefully, so passing the same @p txCheck after a fee-claim
|
||||
* reset is safe.
|
||||
*
|
||||
* @param ctx the apply context for the current transaction.
|
||||
* @param result the tentative TER from transaction processing.
|
||||
* @param fee the fee consumed by the transaction.
|
||||
* @param txCheck the transaction-specific invariant check.
|
||||
* @return the final TER after all invariant checks.
|
||||
*/
|
||||
[[nodiscard]] TER
|
||||
checkInvariants(
|
||||
ApplyContext& ctx,
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
std::optional<std::reference_wrapper<InvariantCheck>> txCheck);
|
||||
|
||||
[[nodiscard]] inline TER
|
||||
checkInvariants(ApplyContext& ctx, TER result, XRPAmount fee)
|
||||
{
|
||||
return checkInvariants(ctx, result, fee, std::nullopt);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
// Interface used by AccountDelete
|
||||
static TER
|
||||
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
class BridgeModify : public Transactor
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
using XChainModifyBridge = BridgeModify;
|
||||
@@ -113,7 +113,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -151,7 +151,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -191,7 +191,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -231,7 +231,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
class XChainAddAccountCreateAttestation : public Transactor
|
||||
@@ -262,7 +262,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -317,7 +317,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
using XChainAccountCreateCommit = XChainCreateAccountCommit;
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
// Interface used by AccountDelete
|
||||
static TER
|
||||
|
||||
@@ -72,7 +72,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
private:
|
||||
TER
|
||||
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -72,7 +72,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
private:
|
||||
std::pair<TER, bool>
|
||||
|
||||
@@ -57,7 +57,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
/** Equal-asset withdrawal (LPTokens) of some AMM instance pools
|
||||
* shares represented by the number of LPTokens .
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
private:
|
||||
std::pair<TER, bool>
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
public:
|
||||
static constexpr std::uint32_t kMinPaymentTotal = 1;
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
// Public to support unit tests.
|
||||
static uint256
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
static TER
|
||||
deleteOracle(ApplyView& view, SLE::ref sle, AccountID const& account, beast::Journal j);
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
// Interface used by AccountDelete
|
||||
static TER
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
static constexpr auto kDisabledTxTypes = std::to_array<TxType>({
|
||||
ttVAULT_CREATE,
|
||||
|
||||
@@ -27,7 +27,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
static Expected<MPTID, TER>
|
||||
create(ApplyView& view, beast::Journal journal, MPTCreateArgs const& args);
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
|
||||
private:
|
||||
Expected<std::pair<STAmount, STAmount>, TER>
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
TER result,
|
||||
XRPAmount fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) override;
|
||||
beast::Journal const& j) const override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -11,11 +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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -12,15 +12,10 @@
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/TxMeta.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/invariants/InvariantCheck.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -74,75 +69,4 @@ ApplyContext::visit(
|
||||
view_->visit(base_, func); // NOLINT(bugprone-unchecked-optional-access)
|
||||
}
|
||||
|
||||
TER
|
||||
ApplyContext::failInvariantCheck(TER const result)
|
||||
{
|
||||
// If we already failed invariant checks before and we are now attempting to
|
||||
// only charge a fee, and even that fails the invariant checks something is
|
||||
// very wrong. We switch to tefINVARIANT_FAILED, which does NOT get included
|
||||
// in a ledger.
|
||||
|
||||
return (result == tecINVARIANT_FAILED || result == tefINVARIANT_FAILED)
|
||||
? TER{tefINVARIANT_FAILED}
|
||||
: TER{tecINVARIANT_FAILED};
|
||||
}
|
||||
|
||||
template <std::size_t... Is>
|
||||
TER
|
||||
ApplyContext::checkInvariantsHelper(
|
||||
TER const result,
|
||||
XRPAmount const fee,
|
||||
std::index_sequence<Is...>)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto checkers = getInvariantChecks();
|
||||
|
||||
// call each check's per-entry method
|
||||
visit(
|
||||
[&checkers](
|
||||
uint256 const& index, bool isDelete, SLE::const_ref before, SLE::const_ref after) {
|
||||
(..., std::get<Is>(checkers).visitEntry(isDelete, before, after));
|
||||
});
|
||||
|
||||
// Note: do not replace this logic with a `...&&` fold expression.
|
||||
// The fold expression will only run until the first check fails (it
|
||||
// short-circuits). While the logic is still correct, the log
|
||||
// message won't be. Every failed invariant should write to the log,
|
||||
// not just the first one.
|
||||
std::array<bool, sizeof...(Is)> const finalizers{{std::get<Is>(checkers).finalize(
|
||||
tx, result, fee, *view_, journal)...}}; // NOLINT(bugprone-unchecked-optional-access)
|
||||
|
||||
// call each check's finalizer to see that it passes
|
||||
if (!std::all_of(finalizers.cbegin(), finalizers.cend(), [](auto const& b) { return b; }))
|
||||
{
|
||||
JLOG(journal.fatal()) << "Transaction has failed one or more global invariants: "
|
||||
<< to_string(tx.getJson(JsonOptions::Values::None));
|
||||
|
||||
return failInvariantCheck(result);
|
||||
}
|
||||
}
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
JLOG(journal.fatal()) << "Transaction caused an exception in a global invariant"
|
||||
<< ", ex: " << ex.what()
|
||||
<< ", tx: " << to_string(tx.getJson(JsonOptions::Values::None));
|
||||
|
||||
return failInvariantCheck(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TER
|
||||
ApplyContext::checkInvariants(TER const result, XRPAmount const fee)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
isTesSuccess(result) || isTecClaim(result),
|
||||
"xrpl::ApplyContext::checkInvariants : is tesSUCCESS or tecCLAIM");
|
||||
|
||||
return checkInvariantsHelper(
|
||||
result, fee, std::make_index_sequence<std::tuple_size_v<InvariantChecks>>{});
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
@@ -1135,58 +1134,24 @@ Transactor::trapTransaction(uint256 txHash) const
|
||||
JLOG(j_.debug()) << "Transaction trapped: " << txHash;
|
||||
}
|
||||
|
||||
[[nodiscard]] TER
|
||||
Transactor::checkTransactionInvariants(TER result, XRPAmount fee)
|
||||
[[nodiscard]] bool
|
||||
Transactor::InvariantCheckAdapter::finalize(
|
||||
STTx const& tx,
|
||||
TER const result,
|
||||
XRPAmount const fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j) const
|
||||
{
|
||||
try
|
||||
{
|
||||
// Phase 1: visit modified entries
|
||||
ctx_.visit(
|
||||
[this](uint256 const&, bool isDelete, SLE::const_ref before, SLE::const_ref after) {
|
||||
this->visitInvariantEntry(isDelete, before, after);
|
||||
});
|
||||
|
||||
// Phase 2: finalize
|
||||
if (!this->finalizeInvariants(ctx_.tx, result, fee, ctx_.view(), ctx_.journal))
|
||||
{
|
||||
JLOG(ctx_.journal.fatal()) << //
|
||||
"Transaction has failed one or more transaction invariants, tx: " << //
|
||||
to_string(ctx_.tx.getJson(JsonOptions::Values::None));
|
||||
return tecINVARIANT_FAILED;
|
||||
}
|
||||
}
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
JLOG(ctx_.journal.fatal()) << //
|
||||
"Exception while checking transaction invariants: " << //
|
||||
ex.what() << //
|
||||
", tx: " << //
|
||||
to_string(ctx_.tx.getJson(JsonOptions::Values::None));
|
||||
|
||||
return tecINVARIANT_FAILED;
|
||||
}
|
||||
|
||||
return result;
|
||||
return self_.finalizeInvariants(tx, result, fee, view, j);
|
||||
}
|
||||
|
||||
[[nodiscard]] TER
|
||||
Transactor::checkInvariants(TER result, XRPAmount fee)
|
||||
Transactor::checkInvariants(TER result, XRPAmount fee, SkipTxInvariants skip)
|
||||
{
|
||||
// Transaction invariants first (more specific). These check post-conditions of the specific
|
||||
// transaction. If these fail, the transaction's core logic is wrong.
|
||||
auto const txResult = checkTransactionInvariants(result, fee);
|
||||
if (skip == SkipTxInvariants::Yes)
|
||||
return xrpl::checkInvariants(ctx_, result, fee);
|
||||
|
||||
// Protocol invariants second (broader). These check properties that must hold regardless of
|
||||
// transaction type.
|
||||
auto const protoResult = ctx_.checkInvariants(result, fee);
|
||||
|
||||
// Fail if either check failed. tef (fatal) takes priority over tec.
|
||||
if (protoResult == tefINVARIANT_FAILED)
|
||||
return tefINVARIANT_FAILED;
|
||||
if (txResult == tecINVARIANT_FAILED || protoResult == tecINVARIANT_FAILED)
|
||||
return tecINVARIANT_FAILED;
|
||||
|
||||
return result;
|
||||
return xrpl::checkInvariants(ctx_, result, fee, std::ref(invariantCheck_));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
ApplyResult
|
||||
@@ -1364,7 +1329,7 @@ Transactor::operator()()
|
||||
{
|
||||
// Check invariants: if `tecINVARIANT_FAILED` is not returned, we can
|
||||
// proceed to apply the tx
|
||||
result = checkInvariants(result, fee);
|
||||
result = checkInvariants(result, fee, SkipTxInvariants::No);
|
||||
if (result == tecINVARIANT_FAILED)
|
||||
{
|
||||
// Reset to fee-claim only
|
||||
@@ -1375,11 +1340,11 @@ Transactor::operator()()
|
||||
fee = resetResult.second;
|
||||
|
||||
// Check invariants again to ensure the fee claiming doesn't violate
|
||||
// invariants. After reset, only protocol invariants are re-checked.
|
||||
// Transaction invariants are not meaningful here — the transaction's
|
||||
// effects have been rolled back.
|
||||
// invariants. After reset, only protocol invariants are re-checked;
|
||||
// the transaction's effects have been rolled back, so the
|
||||
// transaction-specific invariants are no longer meaningful.
|
||||
if (isTesSuccess(result) || isTecClaim(result))
|
||||
result = ctx_.checkInvariants(result, fee);
|
||||
result = checkInvariants(result, fee, SkipTxInvariants::Yes);
|
||||
}
|
||||
|
||||
// We ran through the invariant checker, which can, in some cases,
|
||||
|
||||
184
src/libxrpl/tx/invariants/CheckInvariants.cpp
Normal file
184
src/libxrpl/tx/invariants/CheckInvariants.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
#include <xrpl/tx/invariants/CheckInvariants.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/json/to_string.h> // IWYU pragma: keep
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/tx/ApplyContext.h>
|
||||
#include <xrpl/tx/invariants/InvariantCheck.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
namespace {
|
||||
|
||||
TER
|
||||
failInvariantCheck(TER const result)
|
||||
{
|
||||
return (result == tecINVARIANT_FAILED || result == tefINVARIANT_FAILED)
|
||||
? TER{tefINVARIANT_FAILED}
|
||||
: TER{tecINVARIANT_FAILED};
|
||||
}
|
||||
|
||||
template <std::size_t... Is>
|
||||
TER
|
||||
checkInvariantsHelper(
|
||||
ApplyContext& ctx,
|
||||
TER const result,
|
||||
XRPAmount const fee,
|
||||
std::optional<std::reference_wrapper<InvariantCheck>> txCheck,
|
||||
std::index_sequence<Is...>)
|
||||
{
|
||||
auto checkers = getInvariantChecks();
|
||||
|
||||
// Phase 1 — state collection.
|
||||
// One walk feeds both layers. Per-layer try-catch isolates faults: a throw
|
||||
// in txCheck stops only txCheck from visiting further entries; the protocol
|
||||
// fold keeps going (and vice-versa). A layer that threw skips finalize.
|
||||
bool txCollectionOk = true;
|
||||
bool protoCollectionOk = true;
|
||||
std::string txCollectionEx;
|
||||
std::string protoCollectionEx;
|
||||
|
||||
ctx.visit([&](uint256 const&, bool isDelete, SLE::const_ref before, SLE::const_ref after) {
|
||||
if (txCheck && txCollectionOk)
|
||||
{
|
||||
try
|
||||
{
|
||||
txCheck->get().visitEntry(isDelete, before, after);
|
||||
}
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
txCollectionOk = false;
|
||||
txCollectionEx = ex.what();
|
||||
}
|
||||
}
|
||||
|
||||
if (protoCollectionOk)
|
||||
{
|
||||
try
|
||||
{
|
||||
(..., std::get<Is>(checkers).visitEntry(isDelete, before, after));
|
||||
}
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
protoCollectionOk = false;
|
||||
protoCollectionEx = ex.what();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Phase 2 — evaluate invariant conditions.
|
||||
auto const txResult = [&]() -> TER {
|
||||
if (!txCheck)
|
||||
return result;
|
||||
|
||||
if (!txCollectionOk)
|
||||
{
|
||||
JLOG(ctx.journal.fatal())
|
||||
<< "Transaction caused an exception while collecting transaction invariant state"
|
||||
<< ", ex: " << txCollectionEx
|
||||
<< ", tx: " << to_string(ctx.tx.getJson(JsonOptions::Values::None));
|
||||
return tecINVARIANT_FAILED;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!txCheck->get().finalize(ctx.tx, result, fee, ctx.view(), ctx.journal))
|
||||
{
|
||||
JLOG(ctx.journal.fatal())
|
||||
<< "Transaction has failed one or more transaction invariants: "
|
||||
<< to_string(ctx.tx.getJson(JsonOptions::Values::None));
|
||||
return tecINVARIANT_FAILED;
|
||||
}
|
||||
}
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
JLOG(ctx.journal.fatal())
|
||||
<< "Transaction caused an exception in a transaction invariant"
|
||||
<< ", ex: " << ex.what()
|
||||
<< ", tx: " << to_string(ctx.tx.getJson(JsonOptions::Values::None));
|
||||
return tecINVARIANT_FAILED;
|
||||
}
|
||||
|
||||
return result;
|
||||
}();
|
||||
|
||||
auto const protoResult = [&]() -> TER {
|
||||
if (!protoCollectionOk)
|
||||
{
|
||||
JLOG(ctx.journal.fatal())
|
||||
<< "Transaction caused an exception while collecting global invariant state"
|
||||
<< ", ex: " << protoCollectionEx
|
||||
<< ", tx: " << to_string(ctx.tx.getJson(JsonOptions::Values::None));
|
||||
return failInvariantCheck(result);
|
||||
}
|
||||
|
||||
bool protoOk = true;
|
||||
try
|
||||
{
|
||||
// Note: do not replace this logic with a `...&&` fold expression.
|
||||
// The fold expression will only run until the first check fails (it
|
||||
// short-circuits). While the logic is still correct, the log
|
||||
// message won't be. Every failed invariant should write to the log,
|
||||
// not just the first one.
|
||||
std::array<bool, sizeof...(Is)> const finalizers{{std::get<Is>(checkers).finalize(
|
||||
ctx.tx,
|
||||
result,
|
||||
fee,
|
||||
ctx.view(),
|
||||
ctx.journal)...}}; // NOLINT(bugprone-unchecked-optional-access)
|
||||
|
||||
protoOk = std::all_of(
|
||||
finalizers.cbegin(), finalizers.cend(), [](auto const& b) { return b; });
|
||||
if (!protoOk)
|
||||
{
|
||||
JLOG(ctx.journal.fatal())
|
||||
<< "Transaction has failed one or more global invariants: "
|
||||
<< to_string(ctx.tx.getJson(JsonOptions::Values::None));
|
||||
}
|
||||
}
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
JLOG(ctx.journal.fatal())
|
||||
<< "Transaction caused an exception in a global invariant"
|
||||
<< ", ex: " << ex.what()
|
||||
<< ", tx: " << to_string(ctx.tx.getJson(JsonOptions::Values::None));
|
||||
protoOk = false;
|
||||
}
|
||||
|
||||
return protoOk ? result : failInvariantCheck(result);
|
||||
}();
|
||||
|
||||
// Fail if either check failed. tef (fatal) takes priority over tec.
|
||||
if (protoResult == tefINVARIANT_FAILED)
|
||||
return tefINVARIANT_FAILED;
|
||||
if (txResult == tecINVARIANT_FAILED || protoResult == tecINVARIANT_FAILED)
|
||||
return tecINVARIANT_FAILED;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TER
|
||||
checkInvariants(
|
||||
ApplyContext& ctx,
|
||||
TER const result,
|
||||
XRPAmount const fee,
|
||||
std::optional<std::reference_wrapper<InvariantCheck>> txCheck)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
isTesSuccess(result) || isTecClaim(result),
|
||||
"xrpl::checkInvariants : is tesSUCCESS or tecCLAIM");
|
||||
|
||||
return checkInvariantsHelper(
|
||||
ctx, result, fee, txCheck, std::make_index_sequence<std::tuple_size_v<InvariantChecks>>{});
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -427,7 +427,7 @@ AccountDelete::finalizeInvariants(
|
||||
TER,
|
||||
XRPAmount,
|
||||
ReadView const&,
|
||||
beast::Journal const&)
|
||||
beast::Journal const&) const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -649,6 +649,7 @@ AccountSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref)
|
||||
|
||||
bool
|
||||
AccountSet::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&)
|
||||
const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -90,7 +90,7 @@ SetRegularKey::finalizeInvariants(
|
||||
TER,
|
||||
XRPAmount,
|
||||
ReadView const&,
|
||||
beast::Journal const&)
|
||||
beast::Journal const&) const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
@@ -417,7 +417,7 @@ SignerListSet::finalizeInvariants(
|
||||
TER,
|
||||
XRPAmount,
|
||||
ReadView const&,
|
||||
beast::Journal const&)
|
||||
beast::Journal const&) const
|
||||
{
|
||||
// No transaction-specific invariants yet (future work).
|
||||
return true;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user