aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--.travis.yml3
-rw-r--r--ci/README.md2
-rwxr-xr-xci/test/00_setup_env.sh2
-rw-r--r--ci/test/00_setup_env_native_qt5.sh1
-rwxr-xr-xci/test/04_install.sh4
-rwxr-xr-xci/test/05_before_script.sh5
-rwxr-xr-xcontrib/devtools/previous_release.sh149
-rw-r--r--contrib/gitian-descriptors/gitian-linux.yml2
-rw-r--r--contrib/guix/README.md2
-rwxr-xr-xcontrib/guix/guix-build.sh2
-rw-r--r--depends/README.md2
-rw-r--r--depends/config.site.in2
-rw-r--r--depends/packages/native_libdmg-hfsplus.mk2
-rw-r--r--doc/release-process.md5
-rw-r--r--src/chain.h6
-rw-r--r--src/flatfile.h2
-rw-r--r--src/node/coinstats.cpp2
-rw-r--r--src/prevector.h14
-rw-r--r--src/qt/clientmodel.cpp5
-rw-r--r--src/qt/walletmodel.cpp4
-rw-r--r--src/serialize.h5
-rw-r--r--src/test/serialize_tests.cpp26
-rw-r--r--src/test/transaction_tests.cpp20
-rw-r--r--src/txdb.cpp2
-rw-r--r--src/wallet/rpcwallet.cpp21
-rwxr-xr-xtest/functional/feature_backwards_compatibility.py335
-rwxr-xr-xtest/functional/test_framework/test_framework.py11
-rwxr-xr-xtest/functional/test_framework/test_node.py13
-rwxr-xr-xtest/functional/test_runner.py1
-rw-r--r--test/sanitizer_suppressions/ubsan2
31 files changed, 588 insertions, 67 deletions
diff --git a/.gitignore b/.gitignore
index 86d3f98d84..1c487f43a7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -108,6 +108,9 @@ qrc_*.cpp
.DS_Store
build
+# Previous releases
+releases
+
#lcov
*.gcno
*.gcda
diff --git a/.travis.yml b/.travis.yml
index 364278ee6a..c5b8c476b7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -35,6 +35,7 @@ cache:
- $TRAVIS_BUILD_DIR/depends/built
- $TRAVIS_BUILD_DIR/depends/sdk-sources
- $TRAVIS_BUILD_DIR/ci/scratch/.ccache
+ - $TRAVIS_BUILD_DIR/releases/$HOST
# macOS
- $HOME/Library/Caches/Homebrew
- /usr/local/Homebrew
@@ -108,7 +109,7 @@ jobs:
FILE_ENV="./ci/test/00_setup_env_i686_centos.sh"
- stage: test
- name: 'x86_64 Linux [GOAL: install] [bionic] [uses qt5 dev package and some depends packages] [unsigned char]'
+ name: 'x86_64 Linux [GOAL: install] [bionic] [previous releases, uses qt5 dev package and some depends packages] [unsigned char]'
env: >-
FILE_ENV="./ci/test/00_setup_env_native_qt5.sh"
diff --git a/ci/README.md b/ci/README.md
index 880e49b459..d2ea255b4b 100644
--- a/ci/README.md
+++ b/ci/README.md
@@ -13,7 +13,7 @@ If the repository is not a fresh git clone, you might have to clean files from p
The ci needs to perform various sysadmin tasks such as installing packages or writing to the user's home directory.
While most of the actions are done inside a docker container, this is not possible for all. Thus, cache directories,
-such as the depends cache or ccache, are mounted as read-write into the docker container. While it should be fine to run
+such as the depends cache, previous release binaries, or ccache, are mounted as read-write into the docker container. While it should be fine to run
the ci system locally on you development box, the ci scripts can generally be assumed to have received less review and
testing compared to other parts of the codebase. If you want to keep the work tree clean, you might want to run the ci
system in a virtual machine with a Linux operating system of your choice.
diff --git a/ci/test/00_setup_env.sh b/ci/test/00_setup_env.sh
index eb00e9c705..4c22e4e6c5 100755
--- a/ci/test/00_setup_env.sh
+++ b/ci/test/00_setup_env.sh
@@ -33,6 +33,7 @@ export HOST=${HOST:-$("$BASE_ROOT_DIR/depends/config.guess")}
export USE_BUSY_BOX=${USE_BUSY_BOX:-false}
export RUN_UNIT_TESTS=${RUN_UNIT_TESTS:-true}
export RUN_FUNCTIONAL_TESTS=${RUN_FUNCTIONAL_TESTS:-true}
+export TEST_PREVIOUS_RELEASES=${TEST_PREVIOUS_RELEASES:-false}
export RUN_FUZZ_TESTS=${RUN_FUZZ_TESTS:-false}
export CONTAINER_NAME=${CONTAINER_NAME:-ci_unnamed}
export DOCKER_NAME_TAG=${DOCKER_NAME_TAG:-ubuntu:18.04}
@@ -50,6 +51,7 @@ export CCACHE_DIR=${CCACHE_DIR:-$BASE_SCRATCH_DIR/.ccache}
export DEPENDS_DIR=${DEPENDS_DIR:-$BASE_ROOT_DIR/depends}
# Folder where the build is done (bin and lib).
export BASE_OUTDIR=${BASE_OUTDIR:-$BASE_SCRATCH_DIR/out/$HOST}
+export PREVIOUS_RELEASES_DIR=${PREVIOUS_RELEASES_DIR:-$BASE_ROOT_DIR/releases/$HOST}
export SDK_URL=${SDK_URL:-https://bitcoincore.org/depends-sources/sdks}
export WINEDEBUG=${WINEDEBUG:-fixme-all}
export DOCKER_PACKAGES=${DOCKER_PACKAGES:-build-essential libtool autotools-dev automake pkg-config bsdmainutils curl ca-certificates ccache python3 rsync git procps}
diff --git a/ci/test/00_setup_env_native_qt5.sh b/ci/test/00_setup_env_native_qt5.sh
index 1c11936262..21c15236d2 100644
--- a/ci/test/00_setup_env_native_qt5.sh
+++ b/ci/test/00_setup_env_native_qt5.sh
@@ -11,4 +11,5 @@ export PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools libdbus-1-dev libhar
export DEP_OPTS="NO_QT=1 NO_UPNP=1 DEBUG=1 ALLOW_HOST_PACKAGES=1"
export TEST_RUNNER_EXTRA="--coverage --extended --exclude feature_dbcrash" # Run extended tests so that coverage does not fail, but exclude the very slow dbcrash
export GOAL="install"
+export TEST_PREVIOUS_RELEASES=true
export BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-glibc-back-compat --enable-reduce-exports --enable-debug CFLAGS=\"-g0 -O2 -funsigned-char\" CXXFLAGS=\"-g0 -O2 -funsigned-char\""
diff --git a/ci/test/04_install.sh b/ci/test/04_install.sh
index 477fd1e329..c6e8331a71 100755
--- a/ci/test/04_install.sh
+++ b/ci/test/04_install.sh
@@ -40,12 +40,13 @@ fi
mkdir -p "${BASE_SCRATCH_DIR}"
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:log_path=${BASE_SCRATCH_DIR}/sanitizer-output/tsan"
export UBSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/ubsan:print_stacktrace=1:halt_on_error=1:report_error_type=1"
-env | grep -E '^(BITCOIN_CONFIG|BASE_|QEMU_|CCACHE_|WINEDEBUG|LC_ALL|BOOST_TEST_RANDOM|CONFIG_SHELL|(ASAN|LSAN|TSAN|UBSAN)_OPTIONS)' | tee /tmp/env
+env | grep -E '^(BITCOIN_CONFIG|BASE_|QEMU_|CCACHE_|WINEDEBUG|LC_ALL|BOOST_TEST_RANDOM|CONFIG_SHELL|(ASAN|LSAN|TSAN|UBSAN)_OPTIONS|TEST_PREVIOUS_RELEASES|PREVIOUS_RELEASES_DIR)' | tee /tmp/env
if [[ $HOST = *-mingw32 ]]; then
DOCKER_ADMIN="--cap-add SYS_ADMIN"
elif [[ $BITCOIN_CONFIG = *--with-sanitizers=*address* ]]; then # If ran with (ASan + LSan), Docker needs access to ptrace (https://github.com/google/sanitizers/issues/764)
@@ -62,6 +63,7 @@ if [ -z "$RUN_CI_ON_HOST" ]; then
--mount type=bind,src=$BASE_ROOT_DIR,dst=/ro_base,readonly \
--mount type=bind,src=$CCACHE_DIR,dst=$CCACHE_DIR \
--mount type=bind,src=$DEPENDS_DIR,dst=$DEPENDS_DIR \
+ --mount type=bind,src=$PREVIOUS_RELEASES_DIR,dst=$PREVIOUS_RELEASES_DIR \
-w $BASE_ROOT_DIR \
--env-file /tmp/env \
--name $CONTAINER_NAME \
diff --git a/ci/test/05_before_script.sh b/ci/test/05_before_script.sh
index d8aa5c87a2..1cb5c30901 100755
--- a/ci/test/05_before_script.sh
+++ b/ci/test/05_before_script.sh
@@ -35,3 +35,8 @@ if [ -z "$NO_DEPENDS" ]; then
fi
DOCKER_EXEC $SHELL_OPTS make $MAKEJOBS -C depends HOST=$HOST $DEP_OPTS
fi
+if [ "$TEST_PREVIOUS_RELEASES" = "true" ]; then
+ BEGIN_FOLD previous-versions
+ DOCKER_EXEC contrib/devtools/previous_release.sh -b -t "$PREVIOUS_RELEASES_DIR" v0.17.1 v0.18.1 v0.19.0.1
+ END_FOLD
+fi
diff --git a/contrib/devtools/previous_release.sh b/contrib/devtools/previous_release.sh
new file mode 100755
index 0000000000..efd035f778
--- /dev/null
+++ b/contrib/devtools/previous_release.sh
@@ -0,0 +1,149 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2018-2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#
+# Build previous releases.
+
+export LC_ALL=C
+
+CONFIG_FLAGS=""
+FUNCTIONAL_TESTS=0
+DELETE_EXISTING=0
+USE_DEPENDS=0
+DOWNLOAD_BINARY=0
+CONFIG_FLAGS=""
+TARGET="releases"
+
+while getopts ":hfrdbt:" opt; do
+ case $opt in
+ h)
+ echo "Usage: .previous_release.sh [options] tag1 tag2"
+ echo " options:"
+ echo " -h Print this message"
+ echo " -f Configure for functional tests"
+ echo " -r Remove existing directory"
+ echo " -d Use depends"
+ echo " -b Download release binary"
+ echo " -t Target directory (default: releases)"
+ exit 0
+ ;;
+ f)
+ FUNCTIONAL_TESTS=1
+ CONFIG_FLAGS="$CONFIG_FLAGS --without-gui --disable-tests --disable-bench"
+ ;;
+ r)
+ DELETE_EXISTING=1
+ ;;
+ d)
+ USE_DEPENDS=1
+ ;;
+ b)
+ DOWNLOAD_BINARY=1
+ ;;
+ t)
+ TARGET=$OPTARG
+ ;;
+ \?)
+ echo "Invalid option: -$OPTARG" >&2
+ exit 1
+ ;;
+ esac
+done
+
+shift $((OPTIND-1))
+
+if [ -z "$1" ]; then
+ echo "Specify release tag(s), e.g.: .previous_release v0.15.1"
+ exit 1
+fi
+
+if [ ! -d "$TARGET" ]; then
+ mkdir -p $TARGET
+fi
+
+if [ "$DOWNLOAD_BINARY" -eq "1" ]; then
+ HOST="${HOST:-$(./depends/config.guess)}"
+ case "$HOST" in
+ x86_64-*-linux*)
+ PLATFORM=x86_64-linux-gnu
+ ;;
+ x86_64-apple-darwin*)
+ PLATFORM=osx64
+ ;;
+ *)
+ echo "Not sure which binary to download for $HOST."
+ exit 1
+ ;;
+ esac
+fi
+
+echo "Releases directory: $TARGET"
+pushd "$TARGET" || exit 1
+{
+ for tag in "$@"
+ do
+ if [ "$DELETE_EXISTING" -eq "1" ]; then
+ if [ -d "$tag" ]; then
+ rm -r "$tag"
+ fi
+ fi
+
+ if [ "$DOWNLOAD_BINARY" -eq "0" ]; then
+
+ if [ ! -d "$tag" ]; then
+ if [ -z $(git tag -l "$tag") ]; then
+ echo "Tag $tag not found"
+ exit 1
+ fi
+
+ git clone https://github.com/bitcoin/bitcoin "$tag"
+ pushd "$tag" || exit 1
+ {
+ git checkout "$tag"
+ if [ "$USE_DEPENDS" -eq "1" ]; then
+ pushd depends || exit 1
+ {
+ if [ "$FUNCTIONAL_TESTS" -eq "1" ]; then
+ make NO_QT=1
+ else
+ make
+ fi
+ HOST="${HOST:-$(./config.guess)}"
+ }
+ popd || exit 1
+ CONFIG_FLAGS="--prefix=$PWD/depends/$HOST $CONFIG_FLAGS"
+ fi
+ ./autogen.sh
+ ./configure $CONFIG_FLAGS
+ make
+ # Move binaries, so they're in the same place as in the release download:
+ mkdir bin
+ mv src/bitcoind src/bitcoin-cli src/bitcoin-tx bin
+ if [ "$FUNCTIONAL_TESTS" -eq "0" ]; then
+ mv src/qt/bitcoin-qt bin
+ fi
+ }
+ popd || exit 1
+ fi
+ else
+ if [ -d "$tag" ]; then
+ echo "Using cached $tag"
+ else
+ mkdir "$tag"
+ if [[ "$tag" =~ v(.*)(rc[0-9]+)$ ]]; then
+ BIN_PATH="bin/bitcoin-core-${BASH_REMATCH[1]}/test.${BASH_REMATCH[2]}"
+ else
+ BIN_PATH="bin/bitcoin-core-${tag:1}"
+ fi
+ URL="https://bitcoin.org/$BIN_PATH/bitcoin-${tag:1}-$PLATFORM.tar.gz"
+ echo "Fetching: $URL"
+ curl -O $URL
+ tar -zxf "bitcoin-${tag:1}-$PLATFORM.tar.gz" -C "$tag" --strip-components=1 "bitcoin-${tag:1}"
+ rm "bitcoin-${tag:1}-$PLATFORM.tar.gz"
+ fi
+ fi
+ done
+}
+popd || exit 1
diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml
index 257dd8ba30..4a8b125ae7 100644
--- a/contrib/gitian-descriptors/gitian-linux.yml
+++ b/contrib/gitian-descriptors/gitian-linux.yml
@@ -40,7 +40,7 @@ script: |
set -e -o pipefail
WRAP_DIR=$HOME/wrapped
- HOSTS="i686-pc-linux-gnu x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu riscv64-linux-gnu"
+ HOSTS="x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu riscv64-linux-gnu"
CONFIGFLAGS="--enable-glibc-back-compat --enable-reduce-exports --disable-bench --disable-gui-tests"
FAKETIME_HOST_PROGS="gcc g++"
FAKETIME_PROGS="date ar ranlib nm"
diff --git a/contrib/guix/README.md b/contrib/guix/README.md
index 46d755886c..8500379025 100644
--- a/contrib/guix/README.md
+++ b/contrib/guix/README.md
@@ -114,7 +114,7 @@ find output/ -type f -print0 | sort -z | xargs -r0 sha256sum
* _**HOSTS**_
Override the space-separated list of platform triples for which to perform a
- bootstrappable build. _(defaults to "i686-linux-gnu x86\_64-linux-gnu
+ bootstrappable build. _(defaults to "x86\_64-linux-gnu
arm-linux-gnueabihf aarch64-linux-gnu riscv64-linux-gnu")_
> Windows and OS X platform triplet support are WIP.
diff --git a/contrib/guix/guix-build.sh b/contrib/guix/guix-build.sh
index 5e0c681f29..2daa8aba5e 100755
--- a/contrib/guix/guix-build.sh
+++ b/contrib/guix/guix-build.sh
@@ -20,7 +20,7 @@ time-machine() {
}
# Deterministically build Bitcoin Core for HOSTs (overriable by environment)
-for host in ${HOSTS=i686-linux-gnu x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu riscv64-linux-gnu}; do
+for host in ${HOSTS=x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu riscv64-linux-gnu}; do
# Display proper warning when the user interrupts the build
trap 'echo "** INT received while building ${host}, you may want to clean up the relevant output and distsrc-* directories before rebuilding"' INT
diff --git a/depends/README.md b/depends/README.md
index 93f619983f..9461887a33 100644
--- a/depends/README.md
+++ b/depends/README.md
@@ -46,7 +46,7 @@ The paths are automatically configured and no other options are needed unless ta
sudo apt-get install curl librsvg2-bin libtiff-tools bsdmainutils cmake imagemagick libcap-dev libz-dev libbz2-dev python3-setuptools
-#### For Win32/Win64 cross compilation
+#### For Win64 cross compilation
- see [build-windows.md](../doc/build-windows.md#cross-compilation-for-ubuntu-and-windows-subsystem-for-linux)
diff --git a/depends/config.site.in b/depends/config.site.in
index c5731e5269..fb9bf713cc 100644
--- a/depends/config.site.in
+++ b/depends/config.site.in
@@ -59,7 +59,7 @@ PKG_CONFIG="`which pkg-config` --static"
# avoid ruining the cache. Sigh.
export PKG_CONFIG_PATH=$depends_prefix/share/pkgconfig:$depends_prefix/lib/pkgconfig
if test -z "@allow_host_packages@"; then
- export PKGCONFIG_LIBDIR=
+ export PKG_CONFIG_LIBDIR=$depends_prefix/lib/pkgconfig
fi
CPPFLAGS="-I$depends_prefix/include/ $CPPFLAGS"
diff --git a/depends/packages/native_libdmg-hfsplus.mk b/depends/packages/native_libdmg-hfsplus.mk
index 8493f1d979..c0f0ce74de 100644
--- a/depends/packages/native_libdmg-hfsplus.mk
+++ b/depends/packages/native_libdmg-hfsplus.mk
@@ -12,7 +12,7 @@ define $(package)_preprocess_cmds
endef
define $(package)_config_cmds
- cmake -DCMAKE_INSTALL_PREFIX:PATH=$(build_prefix) ..
+ cmake -DCMAKE_INSTALL_PREFIX:PATH=$(build_prefix) -DCMAKE_C_FLAGS="-Wl,--build-id=none" ..
endef
define $(package)_build_cmds
diff --git a/doc/release-process.md b/doc/release-process.md
index 1ffef3e106..e0f29f6ad7 100644
--- a/doc/release-process.md
+++ b/doc/release-process.md
@@ -121,7 +121,7 @@ Ensure gitian-builder is up-to-date:
echo '5a60e0a4b3e0b4d655317b2f12a810211c50242138322b16e7e01c6fbb89d92f inputs/osslsigncode-2.0.tar.gz' | sha256sum -c
popd
-Create the macOS SDK tarball, see the [macOS build instructions](build-osx.md#deterministic-macos-dmg-notes) for details, and copy it into the inputs directory.
+Create the macOS SDK tarball, see the [macdeploy instructions](/contrib/macdeploy/README.md#deterministic-macos-dmg-notes) for details, and copy it into the inputs directory.
### Optional: Seed the Gitian sources cache and offline git repositories
@@ -268,7 +268,6 @@ The list of files should be:
```
bitcoin-${VERSION}-aarch64-linux-gnu.tar.gz
bitcoin-${VERSION}-arm-linux-gnueabihf.tar.gz
-bitcoin-${VERSION}-i686-pc-linux-gnu.tar.gz
bitcoin-${VERSION}-riscv64-linux-gnu.tar.gz
bitcoin-${VERSION}-x86_64-linux-gnu.tar.gz
bitcoin-${VERSION}-osx64.tar.gz
@@ -329,8 +328,6 @@ bitcoin.org (see below for bitcoin.org update instructions).
- Update packaging repo
- - Notify BlueMatt so that he can start building [the PPAs](https://launchpad.net/~bitcoin/+archive/ubuntu/bitcoin)
-
- Push the flatpak to flathub, e.g. https://github.com/flathub/org.bitcoincore.bitcoin-qt/pull/2
- Push the latest version to master (if applicable), e.g. https://github.com/bitcoin-core/packaging/pull/32
diff --git a/src/chain.h b/src/chain.h
index 48bcb8bfdd..64c016a1d6 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -333,12 +333,12 @@ public:
SERIALIZE_METHODS(CDiskBlockIndex, obj)
{
int _nVersion = s.GetVersion();
- if (!(s.GetType() & SER_GETHASH)) READWRITE(VARINT(_nVersion, VarIntMode::NONNEGATIVE_SIGNED));
+ if (!(s.GetType() & SER_GETHASH)) READWRITE(VARINT_MODE(_nVersion, VarIntMode::NONNEGATIVE_SIGNED));
- READWRITE(VARINT(obj.nHeight, VarIntMode::NONNEGATIVE_SIGNED));
+ READWRITE(VARINT_MODE(obj.nHeight, VarIntMode::NONNEGATIVE_SIGNED));
READWRITE(VARINT(obj.nStatus));
READWRITE(VARINT(obj.nTx));
- if (obj.nStatus & (BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO)) READWRITE(VARINT(obj.nFile, VarIntMode::NONNEGATIVE_SIGNED));
+ if (obj.nStatus & (BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO)) READWRITE(VARINT_MODE(obj.nFile, VarIntMode::NONNEGATIVE_SIGNED));
if (obj.nStatus & BLOCK_HAVE_DATA) READWRITE(VARINT(obj.nDataPos));
if (obj.nStatus & BLOCK_HAVE_UNDO) READWRITE(VARINT(obj.nUndoPos));
diff --git a/src/flatfile.h b/src/flatfile.h
index 374ceff411..d80682d383 100644
--- a/src/flatfile.h
+++ b/src/flatfile.h
@@ -20,7 +20,7 @@ struct FlatFilePos
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(VARINT(nFile, VarIntMode::NONNEGATIVE_SIGNED));
+ READWRITE(VARINT_MODE(nFile, VarIntMode::NONNEGATIVE_SIGNED));
READWRITE(VARINT(nPos));
}
diff --git a/src/node/coinstats.cpp b/src/node/coinstats.cpp
index a818f06d51..641b2a5d9c 100644
--- a/src/node/coinstats.cpp
+++ b/src/node/coinstats.cpp
@@ -23,7 +23,7 @@ static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash,
for (const auto& output : outputs) {
ss << VARINT(output.first + 1);
ss << output.second.out.scriptPubKey;
- ss << VARINT(output.second.out.nValue, VarIntMode::NONNEGATIVE_SIGNED);
+ ss << VARINT_MODE(output.second.out.nValue, VarIntMode::NONNEGATIVE_SIGNED);
stats.nTransactionOutputs++;
stats.nTotalAmount += output.second.out.nValue;
stats.nBogoSize += 32 /* txid */ + 4 /* vout index */ + 4 /* height + coinbase */ + 8 /* amount */ +
diff --git a/src/prevector.h b/src/prevector.h
index f4ece738a8..09debedc4f 100644
--- a/src/prevector.h
+++ b/src/prevector.h
@@ -15,7 +15,6 @@
#include <type_traits>
#include <utility>
-#pragma pack(push, 1)
/** Implements a drop-in replacement for std::vector<T> which stores up to N
* elements directly (without heap allocation). The types Size and Diff are
* used to store element counts, and can be any unsigned + signed type.
@@ -147,14 +146,20 @@ public:
};
private:
- size_type _size = 0;
+#pragma pack(push, 1)
union direct_or_indirect {
char direct[sizeof(T) * N];
struct {
- size_type capacity;
char* indirect;
+ size_type capacity;
};
- } _union = {};
+ };
+#pragma pack(pop)
+ alignas(char*) direct_or_indirect _union = {};
+ size_type _size = 0;
+
+ static_assert(alignof(char*) % alignof(size_type) == 0 && sizeof(char*) % alignof(size_type) == 0, "size_type cannot have more restrictive alignment requirement than pointer");
+ static_assert(alignof(char*) % alignof(T) == 0, "value_type T cannot have more restrictive alignment requirement than pointer");
T* direct_ptr(difference_type pos) { return reinterpret_cast<T*>(_union.direct) + pos; }
const T* direct_ptr(difference_type pos) const { return reinterpret_cast<const T*>(_union.direct) + pos; }
@@ -523,6 +528,5 @@ public:
return item_ptr(0);
}
};
-#pragma pack(pop)
#endif // BITCOIN_PREVECTOR_H
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index e8146982f9..a1ec3eaab1 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -242,8 +242,9 @@ static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, int heig
clientmodel->cachedBestHeaderHeight = height;
clientmodel->cachedBestHeaderTime = blockTime;
}
- // if we are in-sync or if we notify a header update, update the UI regardless of last update time
- if (fHeader || !initialSync || now - nLastUpdateNotification > MODEL_UPDATE_DELAY) {
+
+ // During initial sync, block notifications, and header notifications from reindexing are both throttled.
+ if (!initialSync || (fHeader && !clientmodel->node().getReindex()) || now - nLastUpdateNotification > MODEL_UPDATE_DELAY) {
//pass an async signal to the UI thread
bool invoked = QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection,
Q_ARG(int, height),
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 6c3a06f3a2..8a84a8c168 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -82,12 +82,12 @@ void WalletModel::pollBalanceChanged()
return;
}
- if(fForceCheckBalanceChanged || m_node.getNumBlocks() != cachedNumBlocks)
+ if(fForceCheckBalanceChanged || numBlocks != cachedNumBlocks)
{
fForceCheckBalanceChanged = false;
// Balance and number of transactions might have changed
- cachedNumBlocks = m_node.getNumBlocks();
+ cachedNumBlocks = numBlocks;
checkBalanceChanged(new_balances);
if(transactionTableModel)
diff --git a/src/serialize.h b/src/serialize.h
index 75d6b52154..cee7225bcb 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -493,12 +493,13 @@ public:
template<typename Formatter, typename T>
static inline Wrapper<Formatter, T&> Using(T&& t) { return Wrapper<Formatter, T&>(t); }
-#define VARINT(obj, ...) Using<VarIntFormatter<__VA_ARGS__>>(obj)
+#define VARINT_MODE(obj, mode) Using<VarIntFormatter<mode>>(obj)
+#define VARINT(obj) Using<VarIntFormatter<VarIntMode::DEFAULT>>(obj)
#define COMPACTSIZE(obj) CCompactSize(REF(obj))
#define LIMITED_STRING(obj,n) LimitedString< n >(REF(obj))
/** Serialization wrapper class for integers in VarInt format. */
-template<VarIntMode Mode=VarIntMode::DEFAULT>
+template<VarIntMode Mode>
struct VarIntFormatter
{
template<typename Stream, typename I> void Ser(Stream &s, I v)
diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp
index 303bb9b88c..ea600499ca 100644
--- a/src/test/serialize_tests.cpp
+++ b/src/test/serialize_tests.cpp
@@ -182,8 +182,8 @@ BOOST_AUTO_TEST_CASE(varints)
CDataStream ss(SER_DISK, 0);
CDataStream::size_type size = 0;
for (int i = 0; i < 100000; i++) {
- ss << VARINT(i, VarIntMode::NONNEGATIVE_SIGNED);
- size += ::GetSerializeSize(VARINT(i, VarIntMode::NONNEGATIVE_SIGNED), 0);
+ ss << VARINT_MODE(i, VarIntMode::NONNEGATIVE_SIGNED);
+ size += ::GetSerializeSize(VARINT_MODE(i, VarIntMode::NONNEGATIVE_SIGNED), 0);
BOOST_CHECK(size == ss.size());
}
@@ -196,7 +196,7 @@ BOOST_AUTO_TEST_CASE(varints)
// decode
for (int i = 0; i < 100000; i++) {
int j = -1;
- ss >> VARINT(j, VarIntMode::NONNEGATIVE_SIGNED);
+ ss >> VARINT_MODE(j, VarIntMode::NONNEGATIVE_SIGNED);
BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i);
}
@@ -210,21 +210,21 @@ BOOST_AUTO_TEST_CASE(varints)
BOOST_AUTO_TEST_CASE(varints_bitpatterns)
{
CDataStream ss(SER_DISK, 0);
- ss << VARINT(0, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "00"); ss.clear();
- ss << VARINT(0x7f, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "7f"); ss.clear();
- ss << VARINT((int8_t)0x7f, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "7f"); ss.clear();
- ss << VARINT(0x80, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "8000"); ss.clear();
+ ss << VARINT_MODE(0, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "00"); ss.clear();
+ ss << VARINT_MODE(0x7f, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "7f"); ss.clear();
+ ss << VARINT_MODE((int8_t)0x7f, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "7f"); ss.clear();
+ ss << VARINT_MODE(0x80, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "8000"); ss.clear();
ss << VARINT((uint8_t)0x80); BOOST_CHECK_EQUAL(HexStr(ss), "8000"); ss.clear();
- ss << VARINT(0x1234, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "a334"); ss.clear();
- ss << VARINT((int16_t)0x1234, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "a334"); ss.clear();
- ss << VARINT(0xffff, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "82fe7f"); ss.clear();
+ ss << VARINT_MODE(0x1234, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "a334"); ss.clear();
+ ss << VARINT_MODE((int16_t)0x1234, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "a334"); ss.clear();
+ ss << VARINT_MODE(0xffff, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "82fe7f"); ss.clear();
ss << VARINT((uint16_t)0xffff); BOOST_CHECK_EQUAL(HexStr(ss), "82fe7f"); ss.clear();
- ss << VARINT(0x123456, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "c7e756"); ss.clear();
- ss << VARINT((int32_t)0x123456, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "c7e756"); ss.clear();
+ ss << VARINT_MODE(0x123456, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "c7e756"); ss.clear();
+ ss << VARINT_MODE((int32_t)0x123456, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "c7e756"); ss.clear();
ss << VARINT(0x80123456U); BOOST_CHECK_EQUAL(HexStr(ss), "86ffc7e756"); ss.clear();
ss << VARINT((uint32_t)0x80123456U); BOOST_CHECK_EQUAL(HexStr(ss), "86ffc7e756"); ss.clear();
ss << VARINT(0xffffffff); BOOST_CHECK_EQUAL(HexStr(ss), "8efefefe7f"); ss.clear();
- ss << VARINT(0x7fffffffffffffffLL, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "fefefefefefefefe7f"); ss.clear();
+ ss << VARINT_MODE(0x7fffffffffffffffLL, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "fefefefefefefefe7f"); ss.clear();
ss << VARINT(0xffffffffffffffffULL); BOOST_CHECK_EQUAL(HexStr(ss), "80fefefefefefefefe7f"); ss.clear();
}
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index fb45ce0ee6..0352d2d95a 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -821,9 +821,29 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
BOOST_CHECK_EQUAL(reason, "scriptsig-size");
+ // Check tx-size (non-standard if transaction weight is > MAX_STANDARD_TX_WEIGHT)
+ t.vin.clear();
+ t.vin.resize(2438); // size per input (empty scriptSig): 41 bytes
+ t.vout[0].scriptPubKey = CScript() << OP_RETURN << std::vector<unsigned char>(19, 0); // output size: 30 bytes
+ // tx header: 12 bytes => 48 vbytes
+ // 2438 inputs: 2438*41 = 99958 bytes => 399832 vbytes
+ // 1 output: 30 bytes => 120 vbytes
+ // ===============================
+ // total: 400000 vbytes
+ BOOST_CHECK_EQUAL(GetTransactionWeight(CTransaction(t)), 400000);
+ BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
+
+ // increase output size by one byte, so we end up with 400004 vbytes
+ t.vout[0].scriptPubKey = CScript() << OP_RETURN << std::vector<unsigned char>(20, 0); // output size: 31 bytes
+ BOOST_CHECK_EQUAL(GetTransactionWeight(CTransaction(t)), 400004);
+ reason.clear();
+ BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
+ BOOST_CHECK_EQUAL(reason, "tx-size");
+
// Check bare multisig (standard if policy flag fIsBareMultisigStd is set)
fIsBareMultisigStd = true;
t.vout[0].scriptPubKey = GetScriptForMultisig(1, {key.GetPubKey()}); // simple 1-of-1
+ t.vin.resize(1);
t.vin[0].scriptSig = CScript() << std::vector<unsigned char>(65, 0);
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 35bbdab00d..acc47ab45e 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -339,7 +339,7 @@ public:
::Unserialize(s, Using<TxOutCompression>(vout[i]));
}
// coinbase height
- ::Unserialize(s, VARINT(nHeight, VarIntMode::NONNEGATIVE_SIGNED));
+ ::Unserialize(s, VARINT_MODE(nHeight, VarIntMode::NONNEGATIVE_SIGNED));
}
};
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index dc9124e1a3..e56125cdca 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -1500,23 +1500,10 @@ UniValue listtransactions(const JSONRPCRequest& request)
if ((nFrom + nCount) > (int)ret.size())
nCount = ret.size() - nFrom;
- std::vector<UniValue> arrTmp = ret.getValues();
-
- std::vector<UniValue>::iterator first = arrTmp.begin();
- std::advance(first, nFrom);
- std::vector<UniValue>::iterator last = arrTmp.begin();
- std::advance(last, nFrom+nCount);
-
- if (last != arrTmp.end()) arrTmp.erase(last, arrTmp.end());
- if (first != arrTmp.begin()) arrTmp.erase(arrTmp.begin(), first);
-
- std::reverse(arrTmp.begin(), arrTmp.end()); // Return oldest to newest
-
- ret.clear();
- ret.setArray();
- ret.push_backV(arrTmp);
-
- return ret;
+ const std::vector<UniValue>& txs = ret.getValues();
+ UniValue result{UniValue::VARR};
+ result.push_backV({ txs.rend() - nFrom - nCount, txs.rend() - nFrom }); // Return oldest to newest
+ return result;
}
static UniValue listsinceblock(const JSONRPCRequest& request)
diff --git a/test/functional/feature_backwards_compatibility.py b/test/functional/feature_backwards_compatibility.py
new file mode 100755
index 0000000000..1ac10f971a
--- /dev/null
+++ b/test/functional/feature_backwards_compatibility.py
@@ -0,0 +1,335 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018-2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Backwards compatibility functional test
+
+Test various backwards compatibility scenarios. Download the previous node binaries:
+
+contrib/devtools/previous_release.sh -b v0.19.0.1 v0.18.1 v0.17.1
+
+Due to RPC changes introduced in various versions the below tests
+won't work for older versions without some patches or workarounds.
+
+Use only the latest patch version of each release, unless a test specifically
+needs an older patch version.
+"""
+
+import os
+import shutil
+
+from test_framework.test_framework import BitcoinTestFramework, SkipTest
+from test_framework.descriptors import descsum_create
+
+from test_framework.util import (
+ assert_equal,
+ sync_blocks,
+ sync_mempools
+)
+
+class BackwardsCompatibilityTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 5
+ # Add new version after each release:
+ self.extra_args = [
+ ["-addresstype=bech32"], # Pre-release: use to mine blocks
+ ["-nowallet", "-walletrbf=1", "-addresstype=bech32"], # Pre-release: use to receive coins, swap wallets, etc
+ ["-nowallet", "-walletrbf=1", "-addresstype=bech32"], # v0.19.0.1
+ ["-nowallet", "-walletrbf=1", "-addresstype=bech32"], # v0.18.1
+ ["-nowallet", "-walletrbf=1", "-addresstype=bech32"] # v0.17.1
+ ]
+
+ def setup_nodes(self):
+ if os.getenv("TEST_PREVIOUS_RELEASES") == "false":
+ raise SkipTest("backwards compatibility tests")
+
+ releases_path = os.getenv("PREVIOUS_RELEASES_DIR") or os.getcwd() + "/releases"
+ if not os.path.isdir(releases_path):
+ if os.getenv("TEST_PREVIOUS_RELEASES") == "true":
+ raise AssertionError("TEST_PREVIOUS_RELEASES=1 but releases missing: " + releases_path)
+ raise SkipTest("This test requires binaries for previous releases")
+
+ self.add_nodes(self.num_nodes, extra_args=self.extra_args, versions=[
+ None,
+ None,
+ 190000,
+ 180100,
+ 170100
+ ], binary=[
+ self.options.bitcoind,
+ self.options.bitcoind,
+ releases_path + "/v0.19.0.1/bin/bitcoind",
+ releases_path + "/v0.18.1/bin/bitcoind",
+ releases_path + "/v0.17.1/bin/bitcoind"
+ ], binary_cli=[
+ self.options.bitcoincli,
+ self.options.bitcoincli,
+ releases_path + "/v0.19.0.1/bin/bitcoin-cli",
+ releases_path + "/v0.18.1/bin/bitcoin-cli",
+ releases_path + "/v0.17.1/bin/bitcoin-cli"
+ ])
+
+ self.start_nodes()
+
+ def run_test(self):
+ self.nodes[0].generatetoaddress(101, self.nodes[0].getnewaddress())
+
+ sync_blocks(self.nodes)
+
+ # Sanity check the test framework:
+ res = self.nodes[self.num_nodes - 1].getblockchaininfo()
+ assert_equal(res['blocks'], 101)
+
+ node_master = self.nodes[self.num_nodes - 4]
+ node_v19 = self.nodes[self.num_nodes - 3]
+ node_v18 = self.nodes[self.num_nodes - 2]
+ node_v17 = self.nodes[self.num_nodes - 1]
+
+ self.log.info("Test wallet backwards compatibility...")
+ # Create a number of wallets and open them in older versions:
+
+ # w1: regular wallet, created on master: update this test when default
+ # wallets can no longer be opened by older versions.
+ node_master.createwallet(wallet_name="w1")
+ wallet = node_master.get_wallet_rpc("w1")
+ info = wallet.getwalletinfo()
+ assert info['private_keys_enabled']
+ assert info['keypoolsize'] > 0
+ # Create a confirmed transaction, receiving coins
+ address = wallet.getnewaddress()
+ self.nodes[0].sendtoaddress(address, 10)
+ sync_mempools(self.nodes)
+ self.nodes[0].generate(1)
+ sync_blocks(self.nodes)
+ # Create a conflicting transaction using RBF
+ return_address = self.nodes[0].getnewaddress()
+ tx1_id = self.nodes[1].sendtoaddress(return_address, 1)
+ tx2_id = self.nodes[1].bumpfee(tx1_id)["txid"]
+ # Confirm the transaction
+ sync_mempools(self.nodes)
+ self.nodes[0].generate(1)
+ sync_blocks(self.nodes)
+ # Create another conflicting transaction using RBF
+ tx3_id = self.nodes[1].sendtoaddress(return_address, 1)
+ tx4_id = self.nodes[1].bumpfee(tx3_id)["txid"]
+ # Abondon transaction, but don't confirm
+ self.nodes[1].abandontransaction(tx3_id)
+
+ # w1_v19: regular wallet, created with v0.19
+ node_v19.createwallet(wallet_name="w1_v19")
+ wallet = node_v19.get_wallet_rpc("w1_v19")
+ info = wallet.getwalletinfo()
+ assert info['private_keys_enabled']
+ assert info['keypoolsize'] > 0
+
+ # w1_v18: regular wallet, created with v0.18
+ node_v18.createwallet(wallet_name="w1_v18")
+ wallet = node_v18.get_wallet_rpc("w1_v18")
+ info = wallet.getwalletinfo()
+ assert info['private_keys_enabled']
+ assert info['keypoolsize'] > 0
+
+ # w2: wallet with private keys disabled, created on master: update this
+ # test when default wallets private keys disabled can no longer be
+ # opened by older versions.
+ node_master.createwallet(wallet_name="w2", disable_private_keys=True)
+ wallet = node_master.get_wallet_rpc("w2")
+ info = wallet.getwalletinfo()
+ assert info['private_keys_enabled'] == False
+ assert info['keypoolsize'] == 0
+
+ # w2_v19: wallet with private keys disabled, created with v0.19
+ node_v19.createwallet(wallet_name="w2_v19", disable_private_keys=True)
+ wallet = node_v19.get_wallet_rpc("w2_v19")
+ info = wallet.getwalletinfo()
+ assert info['private_keys_enabled'] == False
+ assert info['keypoolsize'] == 0
+
+ # w2_v18: wallet with private keys disabled, created with v0.18
+ node_v18.createwallet(wallet_name="w2_v18", disable_private_keys=True)
+ wallet = node_v18.get_wallet_rpc("w2_v18")
+ info = wallet.getwalletinfo()
+ assert info['private_keys_enabled'] == False
+ assert info['keypoolsize'] == 0
+
+ # w3: blank wallet, created on master: update this
+ # test when default blank wallets can no longer be opened by older versions.
+ node_master.createwallet(wallet_name="w3", blank=True)
+ wallet = node_master.get_wallet_rpc("w3")
+ info = wallet.getwalletinfo()
+ assert info['private_keys_enabled']
+ assert info['keypoolsize'] == 0
+
+ # w3_v19: blank wallet, created with v0.19
+ node_v19.createwallet(wallet_name="w3_v19", blank=True)
+ wallet = node_v19.get_wallet_rpc("w3_v19")
+ info = wallet.getwalletinfo()
+ assert info['private_keys_enabled']
+ assert info['keypoolsize'] == 0
+
+ # w3_v18: blank wallet, created with v0.18
+ node_v18.createwallet(wallet_name="w3_v18", blank=True)
+ wallet = node_v18.get_wallet_rpc("w3_v18")
+ info = wallet.getwalletinfo()
+ assert info['private_keys_enabled']
+ assert info['keypoolsize'] == 0
+
+ # Copy the wallets to older nodes:
+ node_master_wallets_dir = os.path.join(node_master.datadir, "regtest/wallets")
+ node_v19_wallets_dir = os.path.join(node_v19.datadir, "regtest/wallets")
+ node_v18_wallets_dir = os.path.join(node_v18.datadir, "regtest/wallets")
+ node_v17_wallets_dir = os.path.join(node_v17.datadir, "regtest/wallets")
+ node_master.unloadwallet("w1")
+ node_master.unloadwallet("w2")
+ node_v19.unloadwallet("w1_v19")
+ node_v19.unloadwallet("w2_v19")
+ node_v18.unloadwallet("w1_v18")
+ node_v18.unloadwallet("w2_v18")
+
+ # Copy wallets to v0.17
+ for wallet in os.listdir(node_master_wallets_dir):
+ shutil.copytree(
+ os.path.join(node_master_wallets_dir, wallet),
+ os.path.join(node_v17_wallets_dir, wallet)
+ )
+ for wallet in os.listdir(node_v18_wallets_dir):
+ shutil.copytree(
+ os.path.join(node_v18_wallets_dir, wallet),
+ os.path.join(node_v17_wallets_dir, wallet)
+ )
+
+ # Copy wallets to v0.18
+ for wallet in os.listdir(node_master_wallets_dir):
+ shutil.copytree(
+ os.path.join(node_master_wallets_dir, wallet),
+ os.path.join(node_v18_wallets_dir, wallet)
+ )
+
+ # Copy wallets to v0.19
+ for wallet in os.listdir(node_master_wallets_dir):
+ shutil.copytree(
+ os.path.join(node_master_wallets_dir, wallet),
+ os.path.join(node_v19_wallets_dir, wallet)
+ )
+
+ # Open the wallets in v0.19
+ node_v19.loadwallet("w1")
+ wallet = node_v19.get_wallet_rpc("w1")
+ info = wallet.getwalletinfo()
+ assert info['private_keys_enabled']
+ assert info['keypoolsize'] > 0
+ txs = wallet.listtransactions()
+ assert_equal(len(txs), 5)
+ assert_equal(txs[1]["txid"], tx1_id)
+ assert_equal(txs[2]["walletconflicts"], [tx1_id])
+ assert_equal(txs[1]["replaced_by_txid"], tx2_id)
+ assert not(txs[1]["abandoned"])
+ assert_equal(txs[1]["confirmations"], -1)
+ assert_equal(txs[2]["blockindex"], 1)
+ assert txs[3]["abandoned"]
+ assert_equal(txs[4]["walletconflicts"], [tx3_id])
+ assert_equal(txs[3]["replaced_by_txid"], tx4_id)
+ assert not(hasattr(txs[3], "blockindex"))
+
+ node_v19.loadwallet("w2")
+ wallet = node_v19.get_wallet_rpc("w2")
+ info = wallet.getwalletinfo()
+ assert info['private_keys_enabled'] == False
+ assert info['keypoolsize'] == 0
+
+ node_v19.loadwallet("w3")
+ wallet = node_v19.get_wallet_rpc("w3")
+ info = wallet.getwalletinfo()
+ assert info['private_keys_enabled']
+ assert info['keypoolsize'] == 0
+
+ # Open the wallets in v0.18
+ node_v18.loadwallet("w1")
+ wallet = node_v18.get_wallet_rpc("w1")
+ info = wallet.getwalletinfo()
+ assert info['private_keys_enabled']
+ assert info['keypoolsize'] > 0
+ txs = wallet.listtransactions()
+ assert_equal(len(txs), 5)
+ assert_equal(txs[1]["txid"], tx1_id)
+ assert_equal(txs[2]["walletconflicts"], [tx1_id])
+ assert_equal(txs[1]["replaced_by_txid"], tx2_id)
+ assert not(txs[1]["abandoned"])
+ assert_equal(txs[1]["confirmations"], -1)
+ assert_equal(txs[2]["blockindex"], 1)
+ assert txs[3]["abandoned"]
+ assert_equal(txs[4]["walletconflicts"], [tx3_id])
+ assert_equal(txs[3]["replaced_by_txid"], tx4_id)
+ assert not(hasattr(txs[3], "blockindex"))
+
+ node_v18.loadwallet("w2")
+ wallet = node_v18.get_wallet_rpc("w2")
+ info = wallet.getwalletinfo()
+ assert info['private_keys_enabled'] == False
+ assert info['keypoolsize'] == 0
+
+ node_v18.loadwallet("w3")
+ wallet = node_v18.get_wallet_rpc("w3")
+ info = wallet.getwalletinfo()
+ assert info['private_keys_enabled']
+ assert info['keypoolsize'] == 0
+
+ # Open the wallets in v0.17
+ node_v17.loadwallet("w1_v18")
+ wallet = node_v17.get_wallet_rpc("w1_v18")
+ info = wallet.getwalletinfo()
+ assert info['private_keys_enabled']
+ assert info['keypoolsize'] > 0
+
+ node_v17.loadwallet("w1")
+ wallet = node_v17.get_wallet_rpc("w1")
+ info = wallet.getwalletinfo()
+ assert info['private_keys_enabled']
+ assert info['keypoolsize'] > 0
+
+ node_v17.loadwallet("w2_v18")
+ wallet = node_v17.get_wallet_rpc("w2_v18")
+ info = wallet.getwalletinfo()
+ assert info['private_keys_enabled'] == False
+ assert info['keypoolsize'] == 0
+
+ node_v17.loadwallet("w2")
+ wallet = node_v17.get_wallet_rpc("w2")
+ info = wallet.getwalletinfo()
+ assert info['private_keys_enabled'] == False
+ assert info['keypoolsize'] == 0
+
+ # RPC loadwallet failure causes bitcoind to exit, in addition to the RPC
+ # call failure, so the following test won't work:
+ # assert_raises_rpc_error(-4, "Wallet loading failed.", node_v17.loadwallet, 'w3_v18')
+
+ # Instead, we stop node and try to launch it with the wallet:
+ self.stop_node(self.num_nodes - 1)
+ node_v17.assert_start_raises_init_error(["-wallet=w3_v18"], "Error: Error loading w3_v18: Wallet requires newer version of Bitcoin Core")
+ node_v17.assert_start_raises_init_error(["-wallet=w3"], "Error: Error loading w3: Wallet requires newer version of Bitcoin Core")
+ self.start_node(self.num_nodes - 1)
+
+ self.log.info("Test wallet upgrade path...")
+ # u1: regular wallet, created with v0.17
+ node_v17.createwallet(wallet_name="u1_v17")
+ wallet = node_v17.get_wallet_rpc("u1_v17")
+ address = wallet.getnewaddress("bech32")
+ info = wallet.getaddressinfo(address)
+ hdkeypath = info["hdkeypath"]
+ pubkey = info["pubkey"]
+
+ # Copy the wallet to the last Bitcoin Core version and open it:
+ node_v17.unloadwallet("u1_v17")
+ shutil.copytree(
+ os.path.join(node_v17_wallets_dir, "u1_v17"),
+ os.path.join(node_master_wallets_dir, "u1_v17")
+ )
+ node_master.loadwallet("u1_v17")
+ wallet = node_master.get_wallet_rpc("u1_v17")
+ info = wallet.getaddressinfo(address)
+ descriptor = "wpkh([" + info["hdmasterfingerprint"] + hdkeypath[1:] + "]" + pubkey + ")"
+ assert_equal(info["desc"], descsum_create(descriptor))
+
+if __name__ == '__main__':
+ BackwardsCompatibilityTest().main()
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index da92c6325a..e36fb350c6 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -369,7 +369,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
# Public helper methods. These can be accessed by the subclass test scripts.
- def add_nodes(self, num_nodes, extra_args=None, *, rpchost=None, binary=None):
+ def add_nodes(self, num_nodes, extra_args=None, *, rpchost=None, binary=None, binary_cli=None, versions=None):
"""Instantiate TestNode objects.
Should only be called once after the nodes have been specified in
@@ -380,11 +380,17 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
extra_confs = [[]] * num_nodes
if extra_args is None:
extra_args = [[]] * num_nodes
+ if versions is None:
+ versions = [None] * num_nodes
if binary is None:
binary = [self.options.bitcoind] * num_nodes
+ if binary_cli is None:
+ binary_cli = [self.options.bitcoincli] * num_nodes
assert_equal(len(extra_confs), num_nodes)
assert_equal(len(extra_args), num_nodes)
+ assert_equal(len(versions), num_nodes)
assert_equal(len(binary), num_nodes)
+ assert_equal(len(binary_cli), num_nodes)
for i in range(num_nodes):
self.nodes.append(TestNode(
i,
@@ -393,7 +399,8 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
rpchost=rpchost,
timewait=self.rpc_timeout,
bitcoind=binary[i],
- bitcoin_cli=self.options.bitcoincli,
+ bitcoin_cli=binary_cli[i],
+ version=versions[i],
coverage_dir=self.options.coveragedir,
cwd=self.options.tmpdir,
extra_conf=extra_confs[i],
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 0742dbe617..c7559ac7c8 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -60,7 +60,7 @@ class TestNode():
To make things easier for the test writer, any unrecognised messages will
be dispatched to the RPC connection."""
- def __init__(self, i, datadir, *, chain, rpchost, timewait, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False, use_valgrind=False):
+ def __init__(self, i, datadir, *, chain, rpchost, timewait, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False, use_valgrind=False, version=None):
"""
Kwargs:
start_perf (bool): If True, begin profiling the node with `perf` as soon as
@@ -84,6 +84,7 @@ class TestNode():
# For those callers that need more flexibility, they can just set the args property directly.
# Note that common args are set in the config file (see initialize_datadir)
self.extra_args = extra_args
+ self.version = version
# Configuration for logging is set as command-line args rather than in the bitcoin.conf file.
# This means that starting a bitcoind using the temp dir to debug a failed test won't
# spam debug.log.
@@ -91,7 +92,6 @@ class TestNode():
self.binary,
"-datadir=" + self.datadir,
"-logtimemicros",
- "-logthreadnames",
"-debug",
"-debugexclude=libevent",
"-debugexclude=leveldb",
@@ -107,6 +107,9 @@ class TestNode():
"--gen-suppressions=all", "--exit-on-first-error=yes",
"--error-exitcode=1", "--quiet"] + self.args
+ if self.version is None or self.version >= 190000:
+ self.args.append("-logthreadnames")
+
self.cli = TestNodeCLI(bitcoin_cli, self.datadir)
self.use_cli = use_cli
self.start_perf = start_perf
@@ -254,7 +257,11 @@ class TestNode():
return
self.log.debug("Stopping node")
try:
- self.stop(wait=wait)
+ # Do not use wait argument when testing older nodes, e.g. in feature_backwards_compatibility.py
+ if self.version is None or self.version >= 180000:
+ self.stop(wait=wait)
+ else:
+ self.stop()
except http.client.CannotSendRequest:
self.log.exception("Unable to stop node.")
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 8b527bcff0..ea8a340081 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -158,6 +158,7 @@ BASE_SCRIPTS = [
'feature_assumevalid.py',
'example_test.py',
'wallet_txn_doublespend.py',
+ 'feature_backwards_compatibility.py',
'wallet_txn_clone.py --mineblock',
'feature_notifications.py',
'rpc_getblockfilter.py',
diff --git a/test/sanitizer_suppressions/ubsan b/test/sanitizer_suppressions/ubsan
index 098dd29260..b3d9b9e6ec 100644
--- a/test/sanitizer_suppressions/ubsan
+++ b/test/sanitizer_suppressions/ubsan
@@ -1,7 +1,5 @@
# -fsanitize=undefined suppressions
# =================================
-alignment:move.h
-alignment:prevector.h
float-divide-by-zero:policy/fees.cpp
float-divide-by-zero:validation.cpp
float-divide-by-zero:wallet/wallet.cpp