aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml24
-rw-r--r--.github/workflows/ci.yml59
-rwxr-xr-xci/test/00_setup_env.sh14
-rwxr-xr-xci/test/00_setup_env_mac_native.sh (renamed from ci/test/00_setup_env_mac_native_arm64.sh)6
-rwxr-xr-xci/test/00_setup_env_native_fuzz_with_msan.sh2
-rwxr-xr-xci/test/00_setup_env_native_msan.sh2
-rwxr-xr-xci/test/01_base_install.sh56
-rwxr-xr-xci/test/04_install.sh13
-rw-r--r--contrib/devtools/bitcoin-tidy/CMakeLists.txt13
-rw-r--r--contrib/devtools/bitcoin-tidy/README7
-rw-r--r--src/bip324.cpp5
-rw-r--r--src/bip324.h2
-rw-r--r--src/compat/compat.h25
-rw-r--r--src/crypto/chacha20poly1305.cpp4
-rw-r--r--src/crypto/chacha20poly1305.h1
-rw-r--r--src/init.cpp6
-rw-r--r--src/kernel/mempool_persist.cpp31
-rw-r--r--src/kernel/mempool_persist.h12
-rw-r--r--src/rpc/client.cpp4
-rw-r--r--src/rpc/mempool.cpp63
-rw-r--r--src/test/bip324_tests.cpp8
-rw-r--r--src/test/crypto_tests.cpp3
-rw-r--r--src/test/fuzz/bip324.cpp5
-rw-r--r--src/test/fuzz/coins_view.cpp6
-rw-r--r--src/test/fuzz/rpc.cpp1
-rw-r--r--src/test/fuzz/validation_load_mempool.cpp7
-rw-r--r--src/torcontrol.cpp16
-rw-r--r--src/torcontrol.h11
-rw-r--r--src/txmempool.h4
-rw-r--r--src/validation.cpp8
-rw-r--r--src/validation.h3
-rwxr-xr-xtest/functional/mempool_persist.py53
-rwxr-xr-xtest/functional/wallet_fundrawtransaction.py10
33 files changed, 344 insertions, 140 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index eaaa2c471f..29116c9940 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -321,32 +321,10 @@ task:
task:
name: 'macOS 11.0 [gui, no tests] [jammy]'
- << : *CONTAINER_DEPENDS_TEMPLATE
+ << : *GLOBAL_TASK_TEMPLATE
container:
docker_arguments:
CI_IMAGE_NAME_TAG: ubuntu:jammy
FILE_ENV: "./ci/test/00_setup_env_mac.sh"
- macos_sdk_cache:
- folder: "depends/SDKs/$MACOS_SDK"
- fingerprint_key: "$MACOS_SDK"
- << : *MAIN_TEMPLATE
- env:
- MACOS_SDK: "Xcode-12.2-12B45b-extracted-SDK-with-libcxx-headers"
- << : *CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV
-
-task:
- name: 'macOS 13 native arm64 [gui, sqlite only] [no depends]'
- macos_instance:
- # Use latest image, but hardcode version to avoid silent upgrades (and breaks)
- image: ghcr.io/cirruslabs/macos-ventura-xcode:14.3.1 # https://cirrus-ci.org/guide/macOS
- << : *BASE_TEMPLATE
- check_clang_script:
- - clang --version
- brew_install_script:
- - brew install boost libevent qt@5 miniupnpc libnatpmp ccache zeromq qrencode libtool automake gnu-getopt
- << : *MAIN_TEMPLATE
env:
<< : *CIRRUS_EPHEMERAL_WORKER_TEMPLATE_ENV
- CI_USE_APT_INSTALL: "no"
- PACKAGE_MANAGER_INSTALL: "echo" # Nothing to do
- FILE_ENV: "./ci/test/00_setup_env_mac_native_arm64.sh"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000000..f6d22f033f
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,59 @@
+# Copyright (c) 2023 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+name: CI
+on:
+ # See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request.
+ pull_request:
+ # See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push.
+ push:
+ branches:
+ - '**'
+ tags-ignore:
+ - '**'
+
+env:
+ DANGER_RUN_CI_ON_HOST: 1
+ TEST_RUNNER_TIMEOUT_FACTOR: 40
+
+jobs:
+ macos-native-x86_64:
+ name: macOS 13 native, x86_64 [no depends, sqlite only, gui]
+ # Use latest image, but hardcode version to avoid silent upgrades (and breaks).
+ # See: https://github.com/actions/runner-images#available-images.
+ runs-on: macos-13
+
+ # No need to run on the read-only mirror, unless it is a PR.
+ if: github.repository != 'bitcoin-core/gui' || github.event_name == 'pull_request'
+
+ timeout-minutes: 120
+
+ env:
+ MAKEJOBS: '-j4'
+ CI_USE_APT_INSTALL: 'no'
+ PACKAGE_MANAGER_INSTALL: 'echo' # Nothing to do
+ FILE_ENV: './ci/test/00_setup_env_mac_native.sh'
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Clang version
+ run: clang --version
+
+ - name: Install Homebrew packages
+ run: brew install boost libevent qt@5 miniupnpc libnatpmp ccache zeromq qrencode libtool automake gnu-getopt
+
+ - name: Set Ccache directory
+ run: echo "CCACHE_DIR=${RUNNER_TEMP}/ccache_dir" >> "$GITHUB_ENV"
+
+ - name: Ccache cache
+ uses: actions/cache@v3
+ with:
+ path: ${{ env.CCACHE_DIR }}
+ key: ${{ github.job }}-ccache-cache-${{ github.run_id }}
+ restore-keys: ${{ github.job }}-ccache-cache
+
+ - name: CI script
+ run: ./ci/test_run_all.sh
diff --git a/ci/test/00_setup_env.sh b/ci/test/00_setup_env.sh
index 75b47af0ed..3014714a44 100755
--- a/ci/test/00_setup_env.sh
+++ b/ci/test/00_setup_env.sh
@@ -8,10 +8,18 @@ export LC_ALL=C.UTF-8
set -ex
-# The root dir.
+# The source root dir, usually from git, usually read-only.
# The ci system copies this folder.
-BASE_ROOT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../../ >/dev/null 2>&1 && pwd )
-export BASE_ROOT_DIR
+BASE_READ_ONLY_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../../ >/dev/null 2>&1 && pwd )
+export BASE_READ_ONLY_DIR
+# The destination root dir inside the container.
+if [ -z "${DANGER_RUN_CI_ON_HOST}" ] ; then
+ # This folder only exists on the ci guest and will be a copy of BASE_READ_ONLY_DIR
+ export BASE_ROOT_DIR="/ci_container_base"
+else
+ # This folder is equal to BASE_READ_ONLY_DIR and is read-write
+ export BASE_ROOT_DIR="${BASE_READ_ONLY_DIR}"
+fi
# The depends dir.
# This folder exists only on the ci guest, and on the ci host as a volume.
export DEPENDS_DIR=${DEPENDS_DIR:-$BASE_ROOT_DIR/depends}
diff --git a/ci/test/00_setup_env_mac_native_arm64.sh b/ci/test/00_setup_env_mac_native.sh
index eee72db435..c9f65bf397 100755
--- a/ci/test/00_setup_env_mac_native_arm64.sh
+++ b/ci/test/00_setup_env_mac_native.sh
@@ -1,18 +1,18 @@
#!/usr/bin/env bash
#
-# Copyright (c) 2019-2022 The Bitcoin Core developers
+# Copyright (c) 2019-present The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
export LC_ALL=C.UTF-8
-export HOST=arm64-apple-darwin
+export HOST=x86_64-apple-darwin
export PIP_PACKAGES="zmq"
export GOAL="install"
export BITCOIN_CONFIG="--with-gui --with-miniupnpc --with-natpmp --enable-reduce-exports"
export CI_OS_NAME="macos"
export NO_DEPENDS=1
export OSX_SDK=""
-export CCACHE_MAXSIZE=300M
+export CCACHE_MAXSIZE=400M
export RUN_FUZZ_TESTS=true
export FUZZ_TESTS_CONFIG="--exclude banman" # https://github.com/bitcoin/bitcoin/issues/27924
diff --git a/ci/test/00_setup_env_native_fuzz_with_msan.sh b/ci/test/00_setup_env_native_fuzz_with_msan.sh
index 89006bf95e..007bb4bedf 100755
--- a/ci/test/00_setup_env_native_fuzz_with_msan.sh
+++ b/ci/test/00_setup_env_native_fuzz_with_msan.sh
@@ -7,7 +7,7 @@
export LC_ALL=C.UTF-8
export CI_IMAGE_NAME_TAG="ubuntu:22.04"
-LIBCXX_DIR="${BASE_SCRATCH_DIR}/msan/cxx_build/"
+LIBCXX_DIR="/msan/cxx_build/"
export MSAN_FLAGS="-fsanitize=memory -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer -g -O1 -fno-optimize-sibling-calls"
LIBCXX_FLAGS="-nostdinc++ -nostdlib++ -isystem ${LIBCXX_DIR}include/c++/v1 -L${LIBCXX_DIR}lib -Wl,-rpath,${LIBCXX_DIR}lib -lc++ -lc++abi -lpthread -Wno-unused-command-line-argument"
export MSAN_AND_LIBCXX_FLAGS="${MSAN_FLAGS} ${LIBCXX_FLAGS}"
diff --git a/ci/test/00_setup_env_native_msan.sh b/ci/test/00_setup_env_native_msan.sh
index a96ed30dd3..1d39ef9c48 100755
--- a/ci/test/00_setup_env_native_msan.sh
+++ b/ci/test/00_setup_env_native_msan.sh
@@ -7,7 +7,7 @@
export LC_ALL=C.UTF-8
export CI_IMAGE_NAME_TAG="ubuntu:22.04"
-LIBCXX_DIR="${BASE_SCRATCH_DIR}/msan/cxx_build/"
+LIBCXX_DIR="/msan/cxx_build/"
export MSAN_FLAGS="-fsanitize=memory -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer -g -O1 -fno-optimize-sibling-calls"
LIBCXX_FLAGS="-nostdinc++ -nostdlib++ -isystem ${LIBCXX_DIR}include/c++/v1 -L${LIBCXX_DIR}lib -Wl,-rpath,${LIBCXX_DIR}lib -lc++ -lc++abi -lpthread -Wno-unused-command-line-argument"
export MSAN_AND_LIBCXX_FLAGS="${MSAN_FLAGS} ${LIBCXX_FLAGS}"
diff --git a/ci/test/01_base_install.sh b/ci/test/01_base_install.sh
index 83213b2856..c9a496e6ab 100755
--- a/ci/test/01_base_install.sh
+++ b/ci/test/01_base_install.sh
@@ -42,33 +42,35 @@ if [ -n "$PIP_PACKAGES" ]; then
fi
if [[ ${USE_MEMORY_SANITIZER} == "true" ]]; then
- git clone --depth=1 https://github.com/llvm/llvm-project -b llvmorg-16.0.6 "${BASE_SCRATCH_DIR}"/msan/llvm-project
-
- cmake -G Ninja -B "${BASE_SCRATCH_DIR}"/msan/clang_build/ -DLLVM_ENABLE_PROJECTS="clang" \
- -DCMAKE_BUILD_TYPE=Release \
- -DLLVM_TARGETS_TO_BUILD=Native \
- -DLLVM_ENABLE_RUNTIMES="compiler-rt;libcxx;libcxxabi;libunwind" \
- -S "${BASE_SCRATCH_DIR}"/msan/llvm-project/llvm
-
- ninja -C "${BASE_SCRATCH_DIR}"/msan/clang_build/ "$MAKEJOBS"
- ninja -C "${BASE_SCRATCH_DIR}"/msan/clang_build/ install-runtimes
-
- update-alternatives --install /usr/bin/clang++ clang++ "${BASE_SCRATCH_DIR}"/msan/clang_build/bin/clang++ 100
- update-alternatives --install /usr/bin/clang clang "${BASE_SCRATCH_DIR}"/msan/clang_build/bin/clang 100
- update-alternatives --install /usr/bin/llvm-symbolizer llvm-symbolizer "${BASE_SCRATCH_DIR}"/msan/clang_build/bin/llvm-symbolizer 100
-
- cmake -G Ninja -B "${BASE_SCRATCH_DIR}"/msan/cxx_build/ -DLLVM_ENABLE_RUNTIMES='libcxx;libcxxabi' \
- -DCMAKE_BUILD_TYPE=Release \
- -DLLVM_USE_SANITIZER=MemoryWithOrigins \
- -DCMAKE_C_COMPILER=clang \
- -DCMAKE_CXX_COMPILER=clang++ \
- -DLLVM_TARGETS_TO_BUILD=Native \
- -DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF \
- -DLIBCXX_ENABLE_DEBUG_MODE=ON \
- -DLIBCXX_ENABLE_ASSERTIONS=ON \
- -S "${BASE_SCRATCH_DIR}"/msan/llvm-project/runtimes
-
- ninja -C "${BASE_SCRATCH_DIR}"/msan/cxx_build/ "$MAKEJOBS"
+ git clone --depth=1 https://github.com/llvm/llvm-project -b llvmorg-16.0.6 /msan/llvm-project
+
+ cmake -G Ninja -B /msan/clang_build/ \
+ -DLLVM_ENABLE_PROJECTS="clang" \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DLLVM_TARGETS_TO_BUILD=Native \
+ -DLLVM_ENABLE_RUNTIMES="compiler-rt;libcxx;libcxxabi;libunwind" \
+ -S /msan/llvm-project/llvm
+
+ ninja -C /msan/clang_build/ "$MAKEJOBS"
+ ninja -C /msan/clang_build/ install-runtimes
+
+ update-alternatives --install /usr/bin/clang++ clang++ /msan/clang_build/bin/clang++ 100
+ update-alternatives --install /usr/bin/clang clang /msan/clang_build/bin/clang 100
+ update-alternatives --install /usr/bin/llvm-symbolizer llvm-symbolizer /msan/clang_build/bin/llvm-symbolizer 100
+
+ cmake -G Ninja -B /msan/cxx_build/ \
+ -DLLVM_ENABLE_RUNTIMES='libcxx;libcxxabi' \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DLLVM_USE_SANITIZER=MemoryWithOrigins \
+ -DCMAKE_C_COMPILER=clang \
+ -DCMAKE_CXX_COMPILER=clang++ \
+ -DLLVM_TARGETS_TO_BUILD=Native \
+ -DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF \
+ -DLIBCXX_ENABLE_DEBUG_MODE=ON \
+ -DLIBCXX_ENABLE_ASSERTIONS=ON \
+ -S /msan/llvm-project/runtimes
+
+ ninja -C /msan/cxx_build/ "$MAKEJOBS"
fi
if [[ "${RUN_TIDY}" == "true" ]]; then
diff --git a/ci/test/04_install.sh b/ci/test/04_install.sh
index 7ecf00097c..99e16bcb98 100755
--- a/ci/test/04_install.sh
+++ b/ci/test/04_install.sh
@@ -6,10 +6,6 @@
export LC_ALL=C.UTF-8
-# Create folders that are mounted into the docker
-mkdir -p "${CCACHE_DIR}"
-mkdir -p "${PREVIOUS_RELEASES_DIR}"
-
export ASAN_OPTIONS="detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1"
export LSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/lsan"
export TSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/tsan:halt_on_error=1:log_path=${BASE_SCRATCH_DIR}/sanitizer-output/tsan"
@@ -23,11 +19,11 @@ if [ -z "$DANGER_RUN_CI_ON_HOST" ]; then
docker run --rm "${CI_IMAGE_NAME_TAG}" bash -c "env | grep --extended-regexp '^(HOME|PATH|USER)='" | tee --append /tmp/env
echo "Creating $CI_IMAGE_NAME_TAG container to run in"
DOCKER_BUILDKIT=1 docker build \
- --file "${BASE_ROOT_DIR}/ci/test_imagefile" \
+ --file "${BASE_READ_ONLY_DIR}/ci/test_imagefile" \
--build-arg "CI_IMAGE_NAME_TAG=${CI_IMAGE_NAME_TAG}" \
--build-arg "FILE_ENV=${FILE_ENV}" \
--tag="${CONTAINER_NAME}" \
- "${BASE_ROOT_DIR}"
+ "${BASE_READ_ONLY_DIR}"
docker volume create "${CONTAINER_NAME}_ccache" || true
docker volume create "${CONTAINER_NAME}_depends" || true
docker volume create "${CONTAINER_NAME}_previous_releases" || true
@@ -41,7 +37,7 @@ if [ -z "$DANGER_RUN_CI_ON_HOST" ]; then
# shellcheck disable=SC2086
CI_CONTAINER_ID=$(docker run $CI_CONTAINER_CAP --rm --interactive --detach --tty \
- --mount type=bind,src=$BASE_ROOT_DIR,dst=/ro_base,readonly \
+ --mount type=bind,src=$BASE_READ_ONLY_DIR,dst=/ro_base,readonly \
--mount "type=volume,src=${CONTAINER_NAME}_ccache,dst=$CCACHE_DIR" \
--mount "type=volume,src=${CONTAINER_NAME}_depends,dst=$DEPENDS_DIR" \
--mount "type=volume,src=${CONTAINER_NAME}_previous_releases,dst=$PREVIOUS_RELEASES_DIR" \
@@ -52,6 +48,9 @@ if [ -z "$DANGER_RUN_CI_ON_HOST" ]; then
export CI_EXEC_CMD_PREFIX="docker exec ${CI_CONTAINER_ID}"
else
echo "Running on host system without docker wrapper"
+ echo "Create missing folders"
+ mkdir -p "${CCACHE_DIR}"
+ mkdir -p "${PREVIOUS_RELEASES_DIR}"
fi
CI_EXEC () {
diff --git a/contrib/devtools/bitcoin-tidy/CMakeLists.txt b/contrib/devtools/bitcoin-tidy/CMakeLists.txt
index 9ed18696d4..35e60d1d87 100644
--- a/contrib/devtools/bitcoin-tidy/CMakeLists.txt
+++ b/contrib/devtools/bitcoin-tidy/CMakeLists.txt
@@ -25,6 +25,12 @@ else()
target_compile_options(bitcoin-tidy PRIVATE -fno-exceptions)
endif()
+if(CMAKE_HOST_APPLE)
+ # ld64 expects no undefined symbols by default
+ target_link_options(bitcoin-tidy PRIVATE -Wl,-flat_namespace)
+ target_link_options(bitcoin-tidy PRIVATE -Wl,-undefined -Wl,suppress)
+endif()
+
# Add warnings
if (MSVC)
target_compile_options(bitcoin-tidy PRIVATE /W4)
@@ -33,7 +39,12 @@ else()
target_compile_options(bitcoin-tidy PRIVATE -Wextra)
endif()
-set(CLANG_TIDY_COMMAND "${CLANG_TIDY_EXE}" "--load=${CMAKE_BINARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}bitcoin-tidy${CMAKE_SHARED_LIBRARY_SUFFIX}" "-checks=-*,bitcoin-*")
+if(CMAKE_VERSION VERSION_LESS 3.27)
+ set(CLANG_TIDY_COMMAND "${CLANG_TIDY_EXE}" "--load=${CMAKE_BINARY_DIR}/${CMAKE_SHARED_MODULE_PREFIX}bitcoin-tidy${CMAKE_SHARED_MODULE_SUFFIX}" "-checks=-*,bitcoin-*")
+else()
+ # CLANG_TIDY_COMMAND supports generator expressions as of 3.27
+ set(CLANG_TIDY_COMMAND "${CLANG_TIDY_EXE}" "--load=$<TARGET_FILE:bitcoin-tidy>" "-checks=-*,bitcoin-*")
+endif()
# Create a dummy library that runs clang-tidy tests as a side-effect of building
add_library(bitcoin-tidy-tests OBJECT EXCLUDE_FROM_ALL example_logprintf.cpp)
diff --git a/contrib/devtools/bitcoin-tidy/README b/contrib/devtools/bitcoin-tidy/README
index 1669fe98f5..c15e07c4ed 100644
--- a/contrib/devtools/bitcoin-tidy/README
+++ b/contrib/devtools/bitcoin-tidy/README
@@ -3,6 +3,9 @@
Example Usage:
```bash
-cmake -S . -B build -DLLVM_DIR=/path/to/lib/cmake/llvm -DCMAKE_BUILD_TYPE=Release
-make -C build -j$(nproc)
+cmake -S . -B build -DLLVM_DIR=$(llvm-config --cmakedir) -DCMAKE_BUILD_TYPE=Release
+
+cmake --build build -j$(nproc)
+
+cmake --build build --target bitcoin-tidy-tests -j$(nproc)
```
diff --git a/src/bip324.cpp b/src/bip324.cpp
index 7ed99e5585..314e756829 100644
--- a/src/bip324.cpp
+++ b/src/bip324.cpp
@@ -8,14 +8,19 @@
#include <crypto/chacha20.h>
#include <crypto/chacha20poly1305.h>
#include <crypto/hkdf_sha256_32.h>
+#include <key.h>
+#include <pubkey.h>
#include <random.h>
#include <span.h>
#include <support/cleanse.h>
+#include <uint256.h>
#include <algorithm>
#include <assert.h>
#include <cstdint>
#include <cstddef>
+#include <iterator>
+#include <string>
BIP324Cipher::BIP324Cipher() noexcept
{
diff --git a/src/bip324.h b/src/bip324.h
index 8d025c2ee3..0238c479c0 100644
--- a/src/bip324.h
+++ b/src/bip324.h
@@ -5,6 +5,7 @@
#ifndef BITCOIN_BIP324_H
#define BITCOIN_BIP324_H
+#include <array>
#include <cstddef>
#include <optional>
@@ -54,6 +55,7 @@ public:
/** Initialize when the other side's public key is received. Can only be called once.
*
+ * initiator is set to true if we are the initiator establishing the v2 P2P connection.
* self_decrypt is only for testing, and swaps encryption/decryption keys, so that encryption
* and decryption can be tested without knowing the other side's private key.
*/
diff --git a/src/compat/compat.h b/src/compat/compat.h
index 8195bceaec..435a403552 100644
--- a/src/compat/compat.h
+++ b/src/compat/compat.h
@@ -22,19 +22,18 @@
#include <ws2tcpip.h>
#include <cstdint>
#else
-#include <fcntl.h>
-#include <sys/mman.h>
-#include <sys/select.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <net/if.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
-#include <ifaddrs.h>
-#include <limits.h>
-#include <netdb.h>
-#include <unistd.h>
+#include <arpa/inet.h> // IWYU pragma: export
+#include <fcntl.h> // IWYU pragma: export
+#include <ifaddrs.h> // IWYU pragma: export
+#include <net/if.h> // IWYU pragma: export
+#include <netdb.h> // IWYU pragma: export
+#include <netinet/in.h> // IWYU pragma: export
+#include <netinet/tcp.h> // IWYU pragma: export
+#include <sys/mman.h> // IWYU pragma: export
+#include <sys/select.h> // IWYU pragma: export
+#include <sys/socket.h> // IWYU pragma: export
+#include <sys/types.h> // IWYU pragma: export
+#include <unistd.h> // IWYU pragma: export
#endif
// We map Linux / BSD error functions and codes, to the equivalent
diff --git a/src/crypto/chacha20poly1305.cpp b/src/crypto/chacha20poly1305.cpp
index c936dd2265..26161641bb 100644
--- a/src/crypto/chacha20poly1305.cpp
+++ b/src/crypto/chacha20poly1305.cpp
@@ -11,9 +11,7 @@
#include <support/cleanse.h>
#include <assert.h>
-#include <cstdint>
#include <cstddef>
-#include <iterator>
AEADChaCha20Poly1305::AEADChaCha20Poly1305(Span<const std::byte> key) noexcept : m_chacha20(UCharCast(key.data()))
{
@@ -95,7 +93,7 @@ bool AEADChaCha20Poly1305::Decrypt(Span<const std::byte> cipher, Span<const std:
m_chacha20.Seek64(nonce, 0);
std::byte expected_tag[EXPANSION];
ComputeTag(m_chacha20, aad, cipher.first(cipher.size() - EXPANSION), expected_tag);
- if (timingsafe_bcmp(UCharCast(expected_tag), UCharCast(cipher.data() + cipher.size() - EXPANSION), EXPANSION)) return false;
+ if (timingsafe_bcmp(UCharCast(expected_tag), UCharCast(cipher.last(EXPANSION).data()), EXPANSION)) return false;
// Decrypt (starting at block 1).
m_chacha20.Crypt(UCharCast(cipher.data()), UCharCast(plain1.data()), plain1.size());
diff --git a/src/crypto/chacha20poly1305.h b/src/crypto/chacha20poly1305.h
index dc8fb1cc13..a847c258ef 100644
--- a/src/crypto/chacha20poly1305.h
+++ b/src/crypto/chacha20poly1305.h
@@ -6,7 +6,6 @@
#define BITCOIN_CRYPTO_CHACHA20POLY1305_H
#include <cstddef>
-#include <cstdlib>
#include <stdint.h>
#include <crypto/chacha20.h>
diff --git a/src/init.cpp b/src/init.cpp
index 33389e695c..c2c4dbe459 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -116,6 +116,7 @@
#endif
using kernel::DumpMempool;
+using kernel::LoadMempool;
using kernel::ValidationCacheSizes;
using node::ApplyArgsManOptions;
@@ -1676,7 +1677,10 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
return;
}
// Load mempool from disk
- chainman.ActiveChainstate().LoadMempool(ShouldPersistMempool(args) ? MempoolPath(args) : fs::path{});
+ if (auto* pool{chainman.ActiveChainstate().GetMempool()}) {
+ LoadMempool(*pool, ShouldPersistMempool(args) ? MempoolPath(args) : fs::path{}, chainman.ActiveChainstate(), {});
+ pool->SetLoadTried(!chainman.m_interrupt);
+ }
});
// Wait for genesis block to be processed
diff --git a/src/kernel/mempool_persist.cpp b/src/kernel/mempool_persist.cpp
index d060e45af3..6be07da222 100644
--- a/src/kernel/mempool_persist.cpp
+++ b/src/kernel/mempool_persist.cpp
@@ -19,7 +19,6 @@
#include <util/time.h>
#include <validation.h>
-#include <chrono>
#include <cstdint>
#include <cstdio>
#include <exception>
@@ -37,11 +36,11 @@ namespace kernel {
static const uint64_t MEMPOOL_DUMP_VERSION = 1;
-bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active_chainstate, FopenFn mockable_fopen_function)
+bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active_chainstate, ImportMempoolOptions&& opts)
{
if (load_path.empty()) return false;
- FILE* filestr{mockable_fopen_function(load_path, "rb")};
+ FILE* filestr{opts.mockable_fopen_function(load_path, "rb")};
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
if (file.IsNull()) {
LogPrintf("Failed to open mempool file from disk. Continuing anyway.\n");
@@ -53,7 +52,7 @@ bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active
int64_t failed = 0;
int64_t already_there = 0;
int64_t unbroadcast = 0;
- auto now = NodeClock::now();
+ const auto now{NodeClock::now()};
try {
uint64_t version;
@@ -72,8 +71,12 @@ bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active
file >> nTime;
file >> nFeeDelta;
+ if (opts.use_current_time) {
+ nTime = TicksSinceEpoch<std::chrono::seconds>(now);
+ }
+
CAmount amountdelta = nFeeDelta;
- if (amountdelta) {
+ if (amountdelta && opts.apply_fee_delta_priority) {
pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
}
if (nTime > TicksSinceEpoch<std::chrono::seconds>(now - pool.m_expiry)) {
@@ -101,17 +104,21 @@ bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active
std::map<uint256, CAmount> mapDeltas;
file >> mapDeltas;
- for (const auto& i : mapDeltas) {
- pool.PrioritiseTransaction(i.first, i.second);
+ if (opts.apply_fee_delta_priority) {
+ for (const auto& i : mapDeltas) {
+ pool.PrioritiseTransaction(i.first, i.second);
+ }
}
std::set<uint256> unbroadcast_txids;
file >> unbroadcast_txids;
- unbroadcast = unbroadcast_txids.size();
- for (const auto& txid : unbroadcast_txids) {
- // Ensure transactions were accepted to mempool then add to
- // unbroadcast set.
- if (pool.get(txid) != nullptr) pool.AddUnbroadcastTx(txid);
+ if (opts.apply_unbroadcast_set) {
+ unbroadcast = unbroadcast_txids.size();
+ for (const auto& txid : unbroadcast_txids) {
+ // Ensure transactions were accepted to mempool then add to
+ // unbroadcast set.
+ if (pool.get(txid) != nullptr) pool.AddUnbroadcastTx(txid);
+ }
}
} catch (const std::exception& e) {
LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing anyway.\n", e.what());
diff --git a/src/kernel/mempool_persist.h b/src/kernel/mempool_persist.h
index cb09119e4a..e124a8eadf 100644
--- a/src/kernel/mempool_persist.h
+++ b/src/kernel/mempool_persist.h
@@ -12,15 +12,21 @@ class CTxMemPool;
namespace kernel {
-/** Dump the mempool to disk. */
+/** Dump the mempool to a file. */
bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path,
fsbridge::FopenFn mockable_fopen_function = fsbridge::fopen,
bool skip_file_commit = false);
-/** Load the mempool from disk. */
+struct ImportMempoolOptions {
+ fsbridge::FopenFn mockable_fopen_function{fsbridge::fopen};
+ bool use_current_time{false};
+ bool apply_fee_delta_priority{true};
+ bool apply_unbroadcast_set{true};
+};
+/** Import the file and attempt to add its contents to the mempool. */
bool LoadMempool(CTxMemPool& pool, const fs::path& load_path,
Chainstate& active_chainstate,
- fsbridge::FopenFn mockable_fopen_function = fsbridge::fopen);
+ ImportMempoolOptions&& opts);
} // namespace kernel
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index d289a9240e..2908c37c1f 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -229,6 +229,10 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "importaddress", 2, "rescan" },
{ "importaddress", 3, "p2sh" },
{ "importpubkey", 2, "rescan" },
+ { "importmempool", 1, "options" },
+ { "importmempool", 1, "apply_fee_delta_priority" },
+ { "importmempool", 1, "use_current_time" },
+ { "importmempool", 1, "apply_unbroadcast_set" },
{ "importmulti", 0, "requests" },
{ "importmulti", 1, "options" },
{ "importmulti", 1, "rescan" },
diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp
index 11d2874961..90ee2a48af 100644
--- a/src/rpc/mempool.cpp
+++ b/src/rpc/mempool.cpp
@@ -696,7 +696,7 @@ static RPCHelpMan getmempoolinfo()
RPCResult{
RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::BOOL, "loaded", "True if the mempool is fully loaded"},
+ {RPCResult::Type::BOOL, "loaded", "True if the initial load attempt of the persisted mempool finished"},
{RPCResult::Type::NUM, "size", "Current tx count"},
{RPCResult::Type::NUM, "bytes", "Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted"},
{RPCResult::Type::NUM, "usage", "Total memory usage for the mempool"},
@@ -719,6 +719,66 @@ static RPCHelpMan getmempoolinfo()
};
}
+static RPCHelpMan importmempool()
+{
+ return RPCHelpMan{
+ "importmempool",
+ "Import a mempool.dat file and attempt to add its contents to the mempool.\n"
+ "Warning: Importing untrusted files is dangerous, especially if metadata from the file is taken over.",
+ {
+ {"filepath", RPCArg::Type::STR, RPCArg::Optional::NO, "The mempool file"},
+ {"options",
+ RPCArg::Type::OBJ_NAMED_PARAMS,
+ RPCArg::Optional::OMITTED,
+ "",
+ {
+ {"use_current_time", RPCArg::Type::BOOL, RPCArg::Default{true},
+ "Whether to use the current system time or use the entry time metadata from the mempool file.\n"
+ "Warning: Importing untrusted metadata may lead to unexpected issues and undesirable behavior."},
+ {"apply_fee_delta_priority", RPCArg::Type::BOOL, RPCArg::Default{false},
+ "Whether to apply the fee delta metadata from the mempool file.\n"
+ "It will be added to any existing fee deltas.\n"
+ "The fee delta can be set by the prioritisetransaction RPC.\n"
+ "Warning: Importing untrusted metadata may lead to unexpected issues and undesirable behavior.\n"
+ "Only set this bool if you understand what it does."},
+ {"apply_unbroadcast_set", RPCArg::Type::BOOL, RPCArg::Default{false},
+ "Whether to apply the unbroadcast set metadata from the mempool file.\n"
+ "Warning: Importing untrusted metadata may lead to unexpected issues and undesirable behavior."},
+ },
+ RPCArgOptions{.oneline_description = "\"options\""}},
+ },
+ RPCResult{RPCResult::Type::OBJ, "", "", std::vector<RPCResult>{}},
+ RPCExamples{HelpExampleCli("importmempool", "/path/to/mempool.dat") + HelpExampleRpc("importmempool", "/path/to/mempool.dat")},
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
+ const NodeContext& node{EnsureAnyNodeContext(request.context)};
+
+ CTxMemPool& mempool{EnsureMemPool(node)};
+ Chainstate& chainstate = EnsureChainman(node).ActiveChainstate();
+
+ if (chainstate.IsInitialBlockDownload()) {
+ throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Can only import the mempool after the block download and sync is done.");
+ }
+
+ const fs::path load_path{fs::u8path(request.params[0].get_str())};
+ const UniValue& use_current_time{request.params[1]["use_current_time"]};
+ const UniValue& apply_fee_delta{request.params[1]["apply_fee_delta_priority"]};
+ const UniValue& apply_unbroadcast{request.params[1]["apply_unbroadcast_set"]};
+ kernel::ImportMempoolOptions opts{
+ .use_current_time = use_current_time.isNull() ? true : use_current_time.get_bool(),
+ .apply_fee_delta_priority = apply_fee_delta.isNull() ? false : apply_fee_delta.get_bool(),
+ .apply_unbroadcast_set = apply_unbroadcast.isNull() ? false : apply_unbroadcast.get_bool(),
+ };
+
+ if (!kernel::LoadMempool(mempool, load_path, chainstate, std::move(opts))) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Unable to import mempool file, see debug.log for details.");
+ }
+
+ UniValue ret{UniValue::VOBJ};
+ return ret;
+ },
+ };
+}
+
static RPCHelpMan savemempool()
{
return RPCHelpMan{"savemempool",
@@ -921,6 +981,7 @@ void RegisterMempoolRPCCommands(CRPCTable& t)
{"blockchain", &gettxspendingprevout},
{"blockchain", &getmempoolinfo},
{"blockchain", &getrawmempool},
+ {"blockchain", &importmempool},
{"blockchain", &savemempool},
{"hidden", &submitpackage},
};
diff --git a/src/test/bip324_tests.cpp b/src/test/bip324_tests.cpp
index ccb9e59e58..04472611ec 100644
--- a/src/test/bip324_tests.cpp
+++ b/src/test/bip324_tests.cpp
@@ -6,13 +6,15 @@
#include <chainparams.h>
#include <key.h>
#include <pubkey.h>
+#include <span.h>
#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <util/strencodings.h>
#include <array>
-#include <vector>
#include <cstddef>
+#include <cstdint>
+#include <vector>
#include <boost/test/unit_test.hpp>
@@ -131,10 +133,10 @@ void TestBIP324PacketVector(
// Decrypt length
auto to_decrypt = ciphertext;
if (error >= 2 && error <= 9) {
- to_decrypt[InsecureRandRange(to_decrypt.size())] ^= std::byte(1U << InsecureRandRange(8));
+ to_decrypt[InsecureRandRange(to_decrypt.size())] ^= std::byte(1U << (error - 2));
}
- // Decrypt length and resize ciphertext to accomodate.
+ // Decrypt length and resize ciphertext to accommodate.
uint32_t dec_len = dec_cipher.DecryptLength(MakeByteSpan(to_decrypt).first(cipher.LENGTH_LEN));
to_decrypt.resize(dec_len + cipher.EXPANSION);
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 6663c914a9..6fbe74a680 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -300,11 +300,11 @@ static void TestFSChaCha20Poly1305(const std::string& plain_hex, const std::stri
for (int it = 0; it < 10; ++it) {
// During it==0 we use the single-plain Encrypt/Decrypt; others use a split at prefix.
size_t prefix = it ? InsecureRandRange(plain.size() + 1) : plain.size();
+ std::byte dummy_tag[FSChaCha20Poly1305::EXPANSION] = {{}};
// Do msg_idx dummy encryptions to seek to the correct packet.
FSChaCha20Poly1305 enc_aead{key, 224};
for (uint64_t i = 0; i < msg_idx; ++i) {
- std::byte dummy_tag[FSChaCha20Poly1305::EXPANSION] = {{}};
enc_aead.Encrypt(Span{dummy_tag}.first(0), Span{dummy_tag}.first(0), dummy_tag);
}
@@ -319,7 +319,6 @@ static void TestFSChaCha20Poly1305(const std::string& plain_hex, const std::stri
// Do msg_idx dummy decryptions to seek to the correct packet.
FSChaCha20Poly1305 dec_aead{key, 224};
for (uint64_t i = 0; i < msg_idx; ++i) {
- std::byte dummy_tag[FSChaCha20Poly1305::EXPANSION] = {{}};
dec_aead.Decrypt(dummy_tag, Span{dummy_tag}.first(0), Span{dummy_tag}.first(0));
}
diff --git a/src/test/fuzz/bip324.cpp b/src/test/fuzz/bip324.cpp
index 359de6c66a..98ac10e364 100644
--- a/src/test/fuzz/bip324.cpp
+++ b/src/test/fuzz/bip324.cpp
@@ -11,7 +11,6 @@
#include <test/util/xoroshiro128plusplus.h>
#include <cstdint>
-#include <tuple>
#include <vector>
namespace {
@@ -75,13 +74,13 @@ FUZZ_TARGET(bip324_cipher_roundtrip, .init=Initialize)
// - Bit 0: whether the ignore bit is set in message
// - Bit 1: whether the responder (0) or initiator (1) sends
// - Bit 2: whether this ciphertext will be corrupted (making it the last sent one)
- // - Bit 3-4: controls the maximum aad length (max 511 bytes)
+ // - Bit 3-4: controls the maximum aad length (max 4095 bytes)
// - Bit 5-7: controls the maximum content length (max 16383 bytes, for performance reasons)
unsigned mode = provider.ConsumeIntegral<uint8_t>();
bool ignore = mode & 1;
bool from_init = mode & 2;
bool damage = mode & 4;
- unsigned aad_length_bits = 3 * ((mode >> 3) & 3);
+ unsigned aad_length_bits = 4 * ((mode >> 3) & 3);
unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1);
unsigned length_bits = 2 * ((mode >> 5) & 7);
unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);
diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
index 723dc6420f..b088aa0bd7 100644
--- a/src/test/fuzz/coins_view.cpp
+++ b/src/test/fuzz/coins_view.cpp
@@ -155,14 +155,16 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view)
}
assert((exists_using_access_coin && exists_using_have_coin_in_cache && exists_using_have_coin && exists_using_get_coin) ||
(!exists_using_access_coin && !exists_using_have_coin_in_cache && !exists_using_have_coin && !exists_using_get_coin));
+ // If HaveCoin on the backend is true, it must also be on the cache if the coin wasn't spent.
const bool exists_using_have_coin_in_backend = backend_coins_view.HaveCoin(random_out_point);
- if (exists_using_have_coin_in_backend) {
+ if (!coin_using_access_coin.IsSpent() && exists_using_have_coin_in_backend) {
assert(exists_using_have_coin);
}
Coin coin_using_backend_get_coin;
if (backend_coins_view.GetCoin(random_out_point, coin_using_backend_get_coin)) {
assert(exists_using_have_coin_in_backend);
- assert(coin_using_get_coin == coin_using_backend_get_coin);
+ // Note we can't assert that `coin_using_get_coin == coin_using_backend_get_coin` because the coin in
+ // the cache may have been modified but not yet flushed.
} else {
assert(!exists_using_have_coin_in_backend);
}
diff --git a/src/test/fuzz/rpc.cpp b/src/test/fuzz/rpc.cpp
index 2782888dc3..9e76c7be3e 100644
--- a/src/test/fuzz/rpc.cpp
+++ b/src/test/fuzz/rpc.cpp
@@ -78,6 +78,7 @@ const std::vector<std::string> RPC_COMMANDS_NOT_SAFE_FOR_FUZZING{
"generatetoaddress", // avoid prohibitively slow execution (when `num_blocks` is large)
"generatetodescriptor", // avoid prohibitively slow execution (when `nblocks` is large)
"gettxoutproof", // avoid prohibitively slow execution
+ "importmempool", // avoid reading from disk
"importwallet", // avoid reading from disk
"loadwallet", // avoid reading from disk
"savemempool", // disabled as a precautionary measure: may take a file path argument in the future
diff --git a/src/test/fuzz/validation_load_mempool.cpp b/src/test/fuzz/validation_load_mempool.cpp
index c203dd4e39..5d020b4d59 100644
--- a/src/test/fuzz/validation_load_mempool.cpp
+++ b/src/test/fuzz/validation_load_mempool.cpp
@@ -20,6 +20,7 @@
#include <vector>
using kernel::DumpMempool;
+using kernel::LoadMempool;
using node::MempoolPath;
@@ -47,6 +48,10 @@ FUZZ_TARGET(validation_load_mempool, .init = initialize_validation_load_mempool)
auto fuzzed_fopen = [&](const fs::path&, const char*) {
return fuzzed_file_provider.open();
};
- (void)chainstate.LoadMempool(MempoolPath(g_setup->m_args), fuzzed_fopen);
+ (void)LoadMempool(pool, MempoolPath(g_setup->m_args), chainstate,
+ {
+ .mockable_fopen_function = fuzzed_fopen,
+ });
+ pool.SetLoadTried(true);
(void)DumpMempool(pool, MempoolPath(g_setup->m_args), fuzzed_fopen, true);
}
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index a8d6fb4b3f..9cf976a700 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -14,14 +14,26 @@
#include <net.h>
#include <netaddress.h>
#include <netbase.h>
+#include <random.h>
+#include <tinyformat.h>
+#include <util/check.h>
+#include <util/fs.h>
#include <util/readwritefile.h>
#include <util/strencodings.h>
+#include <util/string.h>
#include <util/thread.h>
#include <util/time.h>
+#include <algorithm>
+#include <cassert>
+#include <cstdlib>
#include <deque>
#include <functional>
+#include <map>
+#include <optional>
#include <set>
+#include <thread>
+#include <utility>
#include <vector>
#include <event2/buffer.h>
@@ -79,15 +91,15 @@ void TorControlConnection::readcb(struct bufferevent *bev, void *ctx)
if (s.size() < 4) // Short line
continue;
// <status>(-|+| )<data><CRLF>
- self->message.code = LocaleIndependentAtoi<int>(s.substr(0,3));
+ self->message.code = ToIntegral<int>(s.substr(0, 3)).value_or(0);
self->message.lines.push_back(s.substr(4));
char ch = s[3]; // '-','+' or ' '
if (ch == ' ') {
// Final line, dispatch reply and clean up
if (self->message.code >= 600) {
+ // (currently unused)
// Dispatch async notifications to async handler
// Synchronous and asynchronous messages are never interleaved
- self->async_handler(*self, self->message);
} else {
if (!self->reply_handlers.empty()) {
// Invoke reply handler with message
diff --git a/src/torcontrol.h b/src/torcontrol.h
index afc5413db0..1a9065b01a 100644
--- a/src/torcontrol.h
+++ b/src/torcontrol.h
@@ -11,19 +11,14 @@
#include <netaddress.h>
#include <util/fs.h>
-#include <boost/signals2/signal.hpp>
+#include <event2/util.h>
-#include <event2/bufferevent.h>
-#include <event2/event.h>
-
-#include <cstdlib>
+#include <cstdint>
#include <deque>
#include <functional>
#include <string>
#include <vector>
-class CService;
-
extern const std::string DEFAULT_TOR_CONTROL;
static const bool DEFAULT_LISTEN_ONION = true;
@@ -83,8 +78,6 @@ public:
*/
bool Command(const std::string &cmd, const ReplyHandlerCB& reply_handler);
- /** Response handlers for async replies */
- boost::signals2::signal<void(TorControlConnection &,const TorControlReply &)> async_handler;
private:
/** Callback when ready for use */
std::function<void(TorControlConnection&)> connected;
diff --git a/src/txmempool.h b/src/txmempool.h
index a1867eb895..fa1dbbf4b5 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -663,13 +663,13 @@ public:
void GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants, size_t* ancestorsize = nullptr, CAmount* ancestorfees = nullptr) const;
/**
- * @returns true if we've made an attempt to load the mempool regardless of
+ * @returns true if an initial attempt to load the persisted mempool was made, regardless of
* whether the attempt was successful or not
*/
bool GetLoadTried() const;
/**
- * Set whether or not we've made an attempt to load the mempool (regardless
+ * Set whether or not an initial attempt to load the persisted mempool was made (regardless
* of whether the attempt was successful or not)
*/
void SetLoadTried(bool load_tried);
diff --git a/src/validation.cpp b/src/validation.cpp
index c45c847471..cec6d13181 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -69,7 +69,6 @@
using kernel::CCoinsStats;
using kernel::CoinStatsHashType;
using kernel::ComputeUTXOStats;
-using kernel::LoadMempool;
using kernel::Notifications;
using fsbridge::FopenFn;
@@ -4126,13 +4125,6 @@ void PruneBlockFilesManual(Chainstate& active_chainstate, int nManualPruneHeight
}
}
-void Chainstate::LoadMempool(const fs::path& load_path, FopenFn mockable_fopen_function)
-{
- if (!m_mempool) return;
- ::LoadMempool(*m_mempool, load_path, *this, mockable_fopen_function);
- m_mempool->SetLoadTried(!m_chainman.m_interrupt);
-}
-
bool Chainstate::LoadChainTip()
{
AssertLockHeld(cs_main);
diff --git a/src/validation.h b/src/validation.h
index d7ad86a5e8..4c9f807f5d 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -712,9 +712,6 @@ public:
/** Find the last common block of this chain and a locator. */
const CBlockIndex* FindForkInGlobalIndex(const CBlockLocator& locator) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- /** Load the persisted mempool from disk */
- void LoadMempool(const fs::path& load_path, fsbridge::FopenFn mockable_fopen_function = fsbridge::fopen);
-
/** Update the chain tip based on database information, i.e. CoinsTip()'s best block. */
bool LoadChainTip() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py
index a1335ff069..32a927084a 100755
--- a/test/functional/mempool_persist.py
+++ b/test/functional/mempool_persist.py
@@ -46,7 +46,7 @@ from test_framework.util import (
assert_greater_than_or_equal,
assert_raises_rpc_error,
)
-from test_framework.wallet import MiniWallet
+from test_framework.wallet import MiniWallet, COIN
class MempoolPersistTest(BitcoinTestFramework):
@@ -159,6 +159,16 @@ class MempoolPersistTest(BitcoinTestFramework):
assert self.nodes[0].getmempoolinfo()["loaded"]
assert_equal(len(self.nodes[0].getrawmempool()), 0)
+ self.log.debug("Import mempool at runtime to node0.")
+ assert_equal({}, self.nodes[0].importmempool(mempooldat0))
+ assert_equal(len(self.nodes[0].getrawmempool()), 7)
+ fees = self.nodes[0].getmempoolentry(txid=last_txid)["fees"]
+ assert_equal(fees["base"], fees["modified"])
+ assert_equal({}, self.nodes[0].importmempool(mempooldat0, {"apply_fee_delta_priority": True, "apply_unbroadcast_set": True}))
+ assert_equal(2, self.nodes[0].getmempoolinfo()["unbroadcastcount"])
+ fees = self.nodes[0].getmempoolentry(txid=last_txid)["fees"]
+ assert_equal(fees["base"] + Decimal("0.00001000"), fees["modified"])
+
self.log.debug("Stop-start node0. Verify that it has the transactions in its mempool.")
self.stop_nodes()
self.start_node(0)
@@ -186,6 +196,7 @@ class MempoolPersistTest(BitcoinTestFramework):
assert_raises_rpc_error(-1, "Unable to dump mempool to disk", self.nodes[1].savemempool)
os.rmdir(mempooldotnew1)
+ self.test_importmempool_union()
self.test_persist_unbroadcast()
def test_persist_unbroadcast(self):
@@ -210,6 +221,46 @@ class MempoolPersistTest(BitcoinTestFramework):
node0.mockscheduler(16 * 60) # 15 min + 1 for buffer
self.wait_until(lambda: len(conn.get_invs()) == 1)
+ def test_importmempool_union(self):
+ self.log.debug("Submit different transactions to node0 and node1's mempools")
+ self.start_node(0)
+ self.start_node(2)
+ tx_node0 = self.mini_wallet.send_self_transfer(from_node=self.nodes[0])
+ tx_node1 = self.mini_wallet.send_self_transfer(from_node=self.nodes[1])
+ tx_node01 = self.mini_wallet.create_self_transfer()
+ tx_node01_secret = self.mini_wallet.create_self_transfer()
+ self.nodes[0].prioritisetransaction(tx_node01["txid"], 0, COIN)
+ self.nodes[0].prioritisetransaction(tx_node01_secret["txid"], 0, 2 * COIN)
+ self.nodes[1].prioritisetransaction(tx_node01_secret["txid"], 0, 3 * COIN)
+ self.nodes[0].sendrawtransaction(tx_node01["hex"])
+ self.nodes[1].sendrawtransaction(tx_node01["hex"])
+ assert tx_node0["txid"] in self.nodes[0].getrawmempool()
+ assert not tx_node0["txid"] in self.nodes[1].getrawmempool()
+ assert not tx_node1["txid"] in self.nodes[0].getrawmempool()
+ assert tx_node1["txid"] in self.nodes[1].getrawmempool()
+ assert tx_node01["txid"] in self.nodes[0].getrawmempool()
+ assert tx_node01["txid"] in self.nodes[1].getrawmempool()
+ assert not tx_node01_secret["txid"] in self.nodes[0].getrawmempool()
+ assert not tx_node01_secret["txid"] in self.nodes[1].getrawmempool()
+
+ self.log.debug("Check that importmempool can add txns without replacing the entire mempool")
+ mempooldat0 = str(self.nodes[0].chain_path / "mempool.dat")
+ result0 = self.nodes[0].savemempool()
+ assert_equal(mempooldat0, result0["filename"])
+ assert_equal({}, self.nodes[1].importmempool(mempooldat0, {"apply_fee_delta_priority": True}))
+ # All transactions should be in node1's mempool now.
+ assert tx_node0["txid"] in self.nodes[1].getrawmempool()
+ assert tx_node1["txid"] in self.nodes[1].getrawmempool()
+ assert not tx_node1["txid"] in self.nodes[0].getrawmempool()
+ # For transactions that already existed, priority should be changed
+ entry_node01 = self.nodes[1].getmempoolentry(tx_node01["txid"])
+ assert_equal(entry_node01["fees"]["base"] + 1, entry_node01["fees"]["modified"])
+ # Deltas for not-yet-submitted transactions should be applied as well (prioritisation is stackable).
+ self.nodes[1].sendrawtransaction(tx_node01_secret["hex"])
+ entry_node01_secret = self.nodes[1].getmempoolentry(tx_node01_secret["txid"])
+ assert_equal(entry_node01_secret["fees"]["base"] + 5, entry_node01_secret["fees"]["modified"])
+ self.stop_nodes()
+
if __name__ == "__main__":
MempoolPersistTest().main()
diff --git a/test/functional/wallet_fundrawtransaction.py b/test/functional/wallet_fundrawtransaction.py
index e367daae2c..fa4f009f34 100755
--- a/test/functional/wallet_fundrawtransaction.py
+++ b/test/functional/wallet_fundrawtransaction.py
@@ -23,6 +23,7 @@ from test_framework.util import (
assert_raises_rpc_error,
count_bytes,
find_vout_for_address,
+ get_fee,
)
from test_framework.wallet_util import generate_keypair
@@ -570,6 +571,8 @@ class RawTransactionsTest(BitcoinTestFramework):
df_wallet = self.nodes[1].get_wallet_rpc(self.default_wallet_name)
self.nodes[1].createwallet(wallet_name="locked_wallet", descriptors=self.options.descriptors)
wallet = self.nodes[1].get_wallet_rpc("locked_wallet")
+ # This test is not meant to exercise fee estimation. Making sure all txs are sent at a consistent fee rate.
+ wallet.settxfee(self.min_relay_tx_fee)
# Add some balance to the wallet (this will be reverted at the end of the test)
df_wallet.sendall(recipients=[wallet.getnewaddress()])
@@ -599,8 +602,11 @@ class RawTransactionsTest(BitcoinTestFramework):
# Choose input
inputs = wallet.listunspent()
- # Deduce fee to produce a changeless transaction
- value = inputs[0]["amount"] - Decimal("0.00002200")
+
+ # Deduce exact fee to produce a changeless transaction
+ tx_size = 110 # Total tx size: 110 vbytes, p2wpkh -> p2wpkh. Input 68 vbytes + rest of tx is 42 vbytes.
+ value = inputs[0]["amount"] - get_fee(tx_size, self.min_relay_tx_fee)
+
outputs = {self.nodes[0].getnewaddress():value}
rawtx = wallet.createrawtransaction(inputs, outputs)
# fund a transaction that does not require a new key for the change output