diff options
255 files changed, 5620 insertions, 3301 deletions
diff --git a/.travis.yml b/.travis.yml index 0332a0e204..6c3a13d73d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,13 @@ dist: trusty os: linux language: minimal cache: + ccache: true directories: - depends/built - depends/sdk-sources - $HOME/.ccache +git: + depth: 1 env: global: - MAKEJOBS=-j3 @@ -45,6 +48,7 @@ install: - if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get install --no-install-recommends --no-upgrade -qq $PACKAGES; fi - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then travis_retry pip3 install flake8 --user; fi before_script: + - if [ "$CHECK_DOC" = 1 ]; then git fetch --unshallow; fi - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then contrib/devtools/commit-script-check.sh $TRAVIS_COMMIT_RANGE; fi - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/git-subtree-check.sh src/crypto/ctaes; fi - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/git-subtree-check.sh src/secp256k1; fi @@ -62,13 +66,12 @@ before_script: - if [ "$NEED_XVFB" = 1 ]; then export DISPLAY=:99.0; /sbin/start-stop-daemon --start --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac; fi script: - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then while read LINE; do travis_retry gpg --keyserver hkp://subset.pool.sks-keyservers.net --recv-keys $LINE; done < contrib/verify-commits/trusted-keys; fi - - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then git fetch --unshallow; fi - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then contrib/verify-commits/verify-commits.sh; fi - export TRAVIS_COMMIT_LOG=`git log --format=fuller -1` - if [ -n "$USE_SHELL" ]; then export CONFIG_SHELL="$USE_SHELL"; fi - OUTDIR=$BASE_OUTDIR/$TRAVIS_PULL_REQUEST/$TRAVIS_JOB_NUMBER-$HOST - BITCOIN_CONFIG_ALL="--disable-dependency-tracking --prefix=$TRAVIS_BUILD_DIR/depends/$HOST --bindir=$OUTDIR/bin --libdir=$OUTDIR/lib" - - if [ -z "$NO_DEPENDS" ]; then depends/$HOST/native/bin/ccache --max-size=$CCACHE_SIZE; fi + - if [ -z "$NO_DEPENDS" ]; then ccache --max-size=$CCACHE_SIZE; fi - test -n "$USE_SHELL" && eval '"$USE_SHELL" -c "./autogen.sh"' || ./autogen.sh - mkdir build && cd build - ../configure --cache-file=config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false) diff --git a/Makefile.am b/Makefile.am index b24daf9905..f345760f2d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -277,6 +277,22 @@ CLEANFILES = $(OSX_DMG) $(BITCOIN_WIN_INSTALLER) DISTCHECK_CONFIGURE_FLAGS = --enable-man -clean-local: +doc/doxygen/.stamp: doc/Doxyfile FORCE + $(MKDIR_P) $(@D) + $(DOXYGEN) $^ + $(AM_V_at) touch $@ + +if HAVE_DOXYGEN +docs: doc/doxygen/.stamp +else +docs: + @echo "error: doxygen not found" +endif + +clean-docs: + rm -rf doc/doxygen + +clean-local: clean-docs rm -rf coverage_percent.txt test_bitcoin.coverage/ total.coverage/ test/tmp/ cache/ $(OSX_APP) rm -rf test/functional/__pycache__ test/functional/test_framework/__pycache__ test/cache + diff --git a/configure.ac b/configure.ac index d4cd895088..18f707f0ba 100644 --- a/configure.ac +++ b/configure.ac @@ -93,6 +93,11 @@ AC_PATH_PROG(HEXDUMP,hexdump) AC_PATH_TOOL(READELF, readelf) AC_PATH_TOOL(CPPFILT, c++filt) AC_PATH_TOOL(OBJCOPY, objcopy) +AC_PATH_PROG(DOXYGEN, doxygen) +if test -z "$DOXYGEN"; then + AC_MSG_WARN([Doxygen not found]) +fi +AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"]) AC_ARG_VAR(PYTHONPATH, Augments the default search path for python module files) @@ -143,9 +148,9 @@ AC_ARG_WITH([qrencode], AC_ARG_ENABLE([hardening], [AS_HELP_STRING([--disable-hardening], - [do not attempt to harden the resulting executables (default is to harden)])], + [do not attempt to harden the resulting executables (default is to harden when possible)])], [use_hardening=$enableval], - [use_hardening=yes]) + [use_hardening=auto]) AC_ARG_ENABLE([reduce-exports], [AS_HELP_STRING([--enable-reduce-exports], @@ -214,6 +219,13 @@ AC_ARG_ENABLE([debug], [enable_debug=$enableval], [enable_debug=no]) +# Enable gprof profiling +AC_ARG_ENABLE([gprof], + [AS_HELP_STRING([--enable-gprof], + [use gprof profiling compiler flags (default is no)])], + [enable_gprof=$enableval], + [enable_gprof=no]) + # Turn warnings into errors AC_ARG_ENABLE([werror], [AS_HELP_STRING([--enable-werror], @@ -553,12 +565,30 @@ else AC_SEARCH_LIBS([clock_gettime],[rt]) fi +if test "x$enable_gprof" = xyes; then + dnl -pg is incompatible with -pie. Since hardening and profiling together doesn't make sense, + dnl we simply make them mutually exclusive here. Additionally, hardened toolchains may force + dnl -pie by default, in which case it needs to be turned off with -no-pie. + + if test x$use_hardening = xyes; then + AC_MSG_ERROR(gprof profiling is not compatible with hardening. Reconfigure with --disable-hardening or --disable-gprof) + fi + use_hardening=no + AX_CHECK_COMPILE_FLAG([-pg],[GPROF_CXXFLAGS="-pg"], + [AC_MSG_ERROR(gprof profiling requested but not available)], [[$CXXFLAG_WERROR]]) + + AX_CHECK_LINK_FLAG([[-no-pie]], [GPROF_LDFLAGS="-no-pie"]) + AX_CHECK_LINK_FLAG([[-pg]],[GPROF_LDFLAGS="$GPROF_LDFLAGS -pg"], + [AC_MSG_ERROR(gprof profiling requested but not available)], [[$GPROF_LDFLAGS]]) +fi + if test x$TARGET_OS != xwindows; then # All windows code is PIC, forcing it on just adds useless compile warnings AX_CHECK_COMPILE_FLAG([-fPIC],[PIC_FLAGS="-fPIC"]) fi if test x$use_hardening != xno; then + use_hardening=yes AX_CHECK_COMPILE_FLAG([-Wstack-protector],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -Wstack-protector"]) AX_CHECK_COMPILE_FLAG([-fstack-protector-all],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-protector-all"]) @@ -1237,6 +1267,8 @@ AC_SUBST(BITCOIN_TX_NAME) AC_SUBST(RELDFLAGS) AC_SUBST(ERROR_CXXFLAGS) +AC_SUBST(GPROF_CXXFLAGS) +AC_SUBST(GPROF_LDFLAGS) AC_SUBST(HARDENED_CXXFLAGS) AC_SUBST(HARDENED_CPPFLAGS) AC_SUBST(HARDENED_LDFLAGS) @@ -1260,7 +1292,7 @@ AC_SUBST(PROTOBUF_LIBS) AC_SUBST(QR_LIBS) AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi share/qt/Info.plist test/config.ini]) AC_CONFIG_FILES([contrib/devtools/split-debug.sh],[chmod +x contrib/devtools/split-debug.sh]) -AC_CONFIG_FILES([doc/Doxyfile]) +AM_COND_IF([HAVE_DOXYGEN], [AC_CONFIG_FILES([doc/Doxyfile])]) AC_CONFIG_LINKS([contrib/filter-lcov.py:contrib/filter-lcov.py]) AC_CONFIG_LINKS([test/functional/test_runner.py:test/functional/test_runner.py]) AC_CONFIG_LINKS([test/util/bitcoin-util-test.py:test/util/bitcoin-util-test.py]) @@ -1330,6 +1362,7 @@ echo " with bench = $use_bench" echo " with upnp = $use_upnp" echo " use asm = $use_asm" echo " debug enabled = $enable_debug" +echo " gprof enabled = $enable_gprof" echo " werror = $enable_werror" echo echo " target os = $TARGET_OS" diff --git a/contrib/devtools/README.md b/contrib/devtools/README.md index 67c5e15a15..8ca8fa9066 100644 --- a/contrib/devtools/README.md +++ b/contrib/devtools/README.md @@ -85,6 +85,14 @@ gen-manpages.sh A small script to automatically create manpages in ../../doc/man by running the release binaries with the -help option. This requires help2man which can be found at: https://www.gnu.org/software/help2man/ +With in-tree builds this tool can be run from any directory within the +repostitory. To use this tool with out-of-tree builds set `BUILDDIR`. For +example: + +```bash +BUILDDIR=$PWD/build contrib/devtools/gen-manpages.sh +``` + git-subtree-check.sh ==================== diff --git a/contrib/devtools/gen-manpages.sh b/contrib/devtools/gen-manpages.sh index 925d6a6252..27c80548c1 100755 --- a/contrib/devtools/gen-manpages.sh +++ b/contrib/devtools/gen-manpages.sh @@ -1,13 +1,15 @@ #!/bin/bash TOPDIR=${TOPDIR:-$(git rev-parse --show-toplevel)} -SRCDIR=${SRCDIR:-$TOPDIR/src} +BUILDDIR=${BUILDDIR:-$TOPDIR} + +BINDIR=${BINDIR:-$BUILDDIR/src} MANDIR=${MANDIR:-$TOPDIR/doc/man} -BITCOIND=${BITCOIND:-$SRCDIR/bitcoind} -BITCOINCLI=${BITCOINCLI:-$SRCDIR/bitcoin-cli} -BITCOINTX=${BITCOINTX:-$SRCDIR/bitcoin-tx} -BITCOINQT=${BITCOINQT:-$SRCDIR/qt/bitcoin-qt} +BITCOIND=${BITCOIND:-$BINDIR/bitcoind} +BITCOINCLI=${BITCOINCLI:-$BINDIR/bitcoin-cli} +BITCOINTX=${BITCOINTX:-$BINDIR/bitcoin-tx} +BITCOINQT=${BITCOINQT:-$BINDIR/qt/bitcoin-qt} [ ! -x $BITCOIND ] && echo "$BITCOIND not found or not executable." && exit 1 diff --git a/contrib/devtools/lint-whitespace.sh b/contrib/devtools/lint-whitespace.sh index 989923f31a..c5d43043d5 100755 --- a/contrib/devtools/lint-whitespace.sh +++ b/contrib/devtools/lint-whitespace.sh @@ -7,16 +7,30 @@ # Check for new lines in diff that introduce trailing whitespace. # We can't run this check unless we know the commit range for the PR. + +while getopts "?" opt; do + case $opt in + ?) + echo "Usage: .lint-whitespace.sh [N]" + echo " TRAVIS_COMMIT_RANGE='<commit range>' .lint-whitespace.sh" + echo " .lint-whitespace.sh -?" + echo "Checks unstaged changes, the previous N commits, or a commit range." + echo "TRAVIS_COMMIT_RANGE='47ba2c3...ee50c9e' .lint-whitespace.sh" + exit 0 + ;; + esac +done + if [ -z "${TRAVIS_COMMIT_RANGE}" ]; then - echo "Cannot run lint-whitespace.sh without commit range. To run locally, use:" - echo "TRAVIS_COMMIT_RANGE='<commit range>' .lint-whitespace.sh" - echo "For example:" - echo "TRAVIS_COMMIT_RANGE='47ba2c3...ee50c9e' .lint-whitespace.sh" - exit 1 + if [ "$1" ]; then + TRAVIS_COMMIT_RANGE="HEAD~$1...HEAD" + else + TRAVIS_COMMIT_RANGE="HEAD" + fi fi showdiff() { - if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- "." ":(exclude)src/leveldb/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/"; then + if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- "." ":(exclude)depends/patches/" ":(exclude)src/leveldb/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/"; then echo "Failed to get a diff" exit 1 fi @@ -37,21 +51,26 @@ if showdiff | grep -E -q '^\+.*\s+$'; then echo "The following changes were suspected:" FILENAME="" SEEN=0 + SEENLN=0 while read -r line; do if [[ "$line" =~ ^diff ]]; then FILENAME="$line" SEEN=0 elif [[ "$line" =~ ^@@ ]]; then LINENUMBER="$line" + SEENLN=0 else if [ "$SEEN" -eq 0 ]; then # The first time a file is seen with trailing whitespace, we print the # filename (preceded by a newline). echo echo "$FILENAME" - echo "$LINENUMBER" SEEN=1 fi + if [ "$SEENLN" -eq 0 ]; then + echo "$LINENUMBER" + SEENLN=1 + fi echo "$line" fi done < <(showdiff | grep -E '^(diff --git |@@|\+.*\s+$)') @@ -59,29 +78,34 @@ if showdiff | grep -E -q '^\+.*\s+$'; then fi # Check if tab characters were found in the diff. -if showcodediff | grep -P -q '^\+.*\t'; then +if showcodediff | perl -nle '$MATCH++ if m{^\+.*\t}; END{exit 1 unless $MATCH>0}' > /dev/null; then echo "This diff appears to have added new lines with tab characters instead of spaces." echo "The following changes were suspected:" FILENAME="" SEEN=0 + SEENLN=0 while read -r line; do if [[ "$line" =~ ^diff ]]; then FILENAME="$line" SEEN=0 elif [[ "$line" =~ ^@@ ]]; then LINENUMBER="$line" + SEENLN=0 else if [ "$SEEN" -eq 0 ]; then # The first time a file is seen with a tab character, we print the # filename (preceded by a newline). echo echo "$FILENAME" - echo "$LINENUMBER" SEEN=1 fi + if [ "$SEENLN" -eq 0 ]; then + echo "$LINENUMBER" + SEENLN=1 + fi echo "$line" fi - done < <(showcodediff | grep -P '^(diff --git |@@|\+.*\t)') + done < <(showcodediff | perl -nle 'print if m{^(diff --git |@@|\+.*\t)}') RET=1 fi diff --git a/contrib/gitian-build.sh b/contrib/gitian-build.sh index 631fba9089..94d6a89c7b 100755 --- a/contrib/gitian-build.sh +++ b/contrib/gitian-build.sh @@ -47,7 +47,7 @@ Options: -j Number of processes to use. Default 2 -m Memory to allocate in MiB. Default 2000 --kvm Use KVM instead of LXC ---setup Set up the Gitian building environment. Uses KVM. If you want to use lxc, use the --lxc option. Only works on Debian-based systems (Ubuntu, Debian) +--setup Set up the Gitian building environment. Uses LXC. If you want to use KVM, use the --kvm option. Only works on Debian-based systems (Ubuntu, Debian) --detach-sign Create the assert file for detached signing. Will not commit anything. --no-commit Do not commit anything to git -h|--help Print this help message @@ -77,7 +77,7 @@ while :; do -S|--signer) if [ -n "$2" ] then - SIGNER=$2 + SIGNER="$2" shift else echo 'Error: "--signer" requires a non-empty argument.' @@ -190,7 +190,7 @@ fi # Get signer if [[ -n "$1" ]] then - SIGNER=$1 + SIGNER="$1" shift fi @@ -203,7 +203,7 @@ then fi # Check that a signer is specified -if [[ $SIGNER == "" ]] +if [[ "$SIGNER" == "" ]] then echo "$scriptName: Missing signer." echo "Try $scriptName --help for more information" @@ -272,7 +272,7 @@ then echo "Compiling ${VERSION} Linux" echo "" ./bin/gbuild -j ${proc} -m ${mem} --commit bitcoin=${COMMIT} --url bitcoin=${url} ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml - ./bin/gsign -p $signProg --signer $SIGNER --release ${VERSION}-linux --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml + ./bin/gsign -p "$signProg" --signer "$SIGNER" --release ${VERSION}-linux --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml mv build/out/bitcoin-*.tar.gz build/out/src/bitcoin-*.tar.gz ../bitcoin-binaries/${VERSION} fi # Windows @@ -282,7 +282,7 @@ then echo "Compiling ${VERSION} Windows" echo "" ./bin/gbuild -j ${proc} -m ${mem} --commit bitcoin=${COMMIT} --url bitcoin=${url} ../bitcoin/contrib/gitian-descriptors/gitian-win.yml - ./bin/gsign -p $signProg --signer $SIGNER --release ${VERSION}-win-unsigned --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win.yml + ./bin/gsign -p "$signProg" --signer "$SIGNER" --release ${VERSION}-win-unsigned --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win.yml mv build/out/bitcoin-*-win-unsigned.tar.gz inputs/bitcoin-win-unsigned.tar.gz mv build/out/bitcoin-*.zip build/out/bitcoin-*.exe ../bitcoin-binaries/${VERSION} fi @@ -293,7 +293,7 @@ then echo "Compiling ${VERSION} Mac OSX" echo "" ./bin/gbuild -j ${proc} -m ${mem} --commit bitcoin=${COMMIT} --url bitcoin=${url} ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml - ./bin/gsign -p $signProg --signer $SIGNER --release ${VERSION}-osx-unsigned --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml + ./bin/gsign -p "$signProg" --signer "$SIGNER" --release ${VERSION}-osx-unsigned --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml mv build/out/bitcoin-*-osx-unsigned.tar.gz inputs/bitcoin-osx-unsigned.tar.gz mv build/out/bitcoin-*.tar.gz build/out/bitcoin-*.dmg ../bitcoin-binaries/${VERSION} fi @@ -306,9 +306,9 @@ then echo "Committing ${VERSION} Unsigned Sigs" echo "" pushd gitian.sigs - git add ${VERSION}-linux/${SIGNER} - git add ${VERSION}-win-unsigned/${SIGNER} - git add ${VERSION}-osx-unsigned/${SIGNER} + git add ${VERSION}-linux/"${SIGNER}" + git add ${VERSION}-win-unsigned/"${SIGNER}" + git add ${VERSION}-osx-unsigned/"${SIGNER}" git commit -a -m "Add ${VERSION} unsigned sigs for ${SIGNER}" popd fi @@ -358,7 +358,7 @@ then echo "Signing ${VERSION} Windows" echo "" ./bin/gbuild -i --commit signature=${COMMIT} ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml - ./bin/gsign -p $signProg --signer $SIGNER --release ${VERSION}-win-signed --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml + ./bin/gsign -p "$signProg" --signer "$SIGNER" --release ${VERSION}-win-signed --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml mv build/out/bitcoin-*win64-setup.exe ../bitcoin-binaries/${VERSION} mv build/out/bitcoin-*win32-setup.exe ../bitcoin-binaries/${VERSION} fi @@ -369,7 +369,7 @@ then echo "Signing ${VERSION} Mac OSX" echo "" ./bin/gbuild -i --commit signature=${COMMIT} ../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml - ./bin/gsign -p $signProg --signer $SIGNER --release ${VERSION}-osx-signed --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml + ./bin/gsign -p "$signProg" --signer "$SIGNER" --release ${VERSION}-osx-signed --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml mv build/out/bitcoin-osx-signed.dmg ../bitcoin-binaries/${VERSION}/bitcoin-${VERSION}-osx.dmg fi popd @@ -381,8 +381,8 @@ then echo "" echo "Committing ${VERSION} Signed Sigs" echo "" - git add ${VERSION}-win-signed/${SIGNER} - git add ${VERSION}-osx-signed/${SIGNER} + git add ${VERSION}-win-signed/"${SIGNER}" + git add ${VERSION}-osx-signed/"${SIGNER}" git commit -a -m "Add ${VERSION} signed binary sigs for ${SIGNER}" popd fi diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index c80e19edbb..3e9ee0495a 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -1,5 +1,5 @@ --- -name: "bitcoin-linux-0.16" +name: "bitcoin-linux-0.17" enable_cache: true suites: - "trusty" diff --git a/contrib/gitian-descriptors/gitian-osx.yml b/contrib/gitian-descriptors/gitian-osx.yml index cbf286d2cd..a84dce3e3a 100644 --- a/contrib/gitian-descriptors/gitian-osx.yml +++ b/contrib/gitian-descriptors/gitian-osx.yml @@ -1,5 +1,5 @@ --- -name: "bitcoin-osx-0.16" +name: "bitcoin-osx-0.17" enable_cache: true suites: - "trusty" diff --git a/contrib/gitian-descriptors/gitian-win.yml b/contrib/gitian-descriptors/gitian-win.yml index 95ff9759c7..8a87d91754 100644 --- a/contrib/gitian-descriptors/gitian-win.yml +++ b/contrib/gitian-descriptors/gitian-win.yml @@ -1,5 +1,5 @@ --- -name: "bitcoin-win-0.16" +name: "bitcoin-win-0.17" enable_cache: true suites: - "trusty" diff --git a/contrib/gitian-keys/keys.txt b/contrib/gitian-keys/keys.txt index 47da725b74..593fba1d09 100644 --- a/contrib/gitian-keys/keys.txt +++ b/contrib/gitian-keys/keys.txt @@ -1,7 +1,6 @@ 617C90010B3BD370B0AC7D424BB42E31C79111B8 Akira Takizawa -152812300785C96444D3334D17565732E08E5E41 Andrew Chow E944AE667CF960B1004BC32FCA662BE18B877A60 Andreas Schildbach -07DF3E57A548CCFB7530709189BBB8663E2E65CE Matt Corallo (BlueMatt) +152812300785C96444D3334D17565732E08E5E41 Andrew Chow 912FD3228387123DC97E0E57D5566241A0295FA9 BtcDrak C519EBCF3B926298946783EFF6430754120EC2F4 Christian Decker (cdecker) F20F56EF6A067F70E8A5C99FFF95FAA971697405 centaur @@ -9,21 +8,23 @@ C060A6635913D98A3587D7DB1C2491FFEB0EF770 Cory Fields BF6273FAEF7CC0BA1F562E50989F6B3048A116B5 Dev Random 9A1689B60D1B3CCE9262307A2F40A9BF167FBA47 Erik Mossberg (erkmos) D35176BE9264832E4ACA8986BF0792FBE95DC863 fivepiece -E777299FC265DD04793070EB944D35F9AC3DB76A Michael Ford 01CDF4627A3B88AAE4A571C87588242FBE38D3A8 Gavin Andresen D3CC177286005BB8FF673294C5242A1AB3936517 jl2012 32EE5C4C3FA15CCADB46ABE529D4BCB6416F53EC Jonas Schnelli 4B4E840451149DD7FB0D633477DFAB5C3108B9A8 Jorge Timon -71A3B16735405025D447E8F274810B012346C9A6 Wladimir J. van der Laan +C42AFF7C61B3E44A1454CD3557AF762DB3353322 Karl-Johan Alm (kallewoof) E463A93F5F3117EEDE6C7316BD02942421F4889F Luke Dashjr B8B3F1C0E58C15DB6A81D30C3648A882F4316B9B Marco Falke +07DF3E57A548CCFB7530709189BBB8663E2E65CE Matt Corallo (BlueMatt) CA03882CB1FC067B5D3ACFE4D300116E1C875A3D MeshCollider +E777299FC265DD04793070EB944D35F9AC3DB76A Michael Ford 9692B91BBF0E8D34DFD33B1882C5C009628ECF0C Michagogo -37EC7D7B0A217CDB4B4E007E7FAB114267E4FA04 Peter Todd +77E72E69DA7EE0A148C06B21B34821D4944DE5F7 Nils Schneider D62A803E27E7F43486035ADBBCD04D8E9CCCAC2A Paul Rabahy +37EC7D7B0A217CDB4B4E007E7FAB114267E4FA04 Peter Todd D762373D24904A3E42F33B08B9A408E71DAAC974 Pieter Wuille (Location: Leuven, Belgium) 133EAC179436F14A5CF1B794860FEB804E669320 Pieter Wuille ED9BDF7AD6A55E232E84524257FF9BDBCC301009 Sjors Provoost -77E72E69DA7EE0A148C06B21B34821D4944DE5F7 Nils Schneider -79D00BAC68B56D422F945A8F8E3A8F3247DBCBBF Willy Ko AEC1884398647C47413C1C3FB1179EB7347DC10D Warren Togami +79D00BAC68B56D422F945A8F8E3A8F3247DBCBBF Willy Ko +71A3B16735405025D447E8F274810B012346C9A6 Wladimir J. van der Laan diff --git a/contrib/init/bitcoind.service b/contrib/init/bitcoind.service index ee113d7615..877abafd19 100644 --- a/contrib/init/bitcoind.service +++ b/contrib/init/bitcoind.service @@ -19,7 +19,26 @@ User=bitcoin Type=forking PIDFile=/run/bitcoind/bitcoind.pid Restart=on-failure + +# Hardening measures +#################### + +# Provide a private /tmp and /var/tmp. PrivateTmp=true +# Mount /usr, /boot/ and /etc read-only for the process. +ProtectSystem=full + +# Disallow the process and all of its children to gain +# new privileges through execve(). +NoNewPrivileges=true + +# Use a new /dev namespace only populated with API pseudo devices +# such as /dev/null, /dev/zero and /dev/random. +PrivateDevices=true + +# Deny the creation of writable and executable memory mappings. +MemoryDenyWriteExecute=true + [Install] WantedBy=multi-user.target diff --git a/contrib/seeds/generate-seeds.py b/contrib/seeds/generate-seeds.py index 28068a7523..2790ef4acd 100755 --- a/contrib/seeds/generate-seeds.py +++ b/contrib/seeds/generate-seeds.py @@ -124,7 +124,7 @@ def main(): g.write(' * AUTOGENERATED by contrib/seeds/generate-seeds.py\n') g.write(' *\n') g.write(' * Each line contains a 16-byte IPv6 address and a port.\n') - g.write(' * IPv4 as well as onion addresses are wrapped inside a IPv6 address accordingly.\n') + g.write(' * IPv4 as well as onion addresses are wrapped inside an IPv6 address accordingly.\n') g.write(' */\n') with open(os.path.join(indir,'nodes_main.txt'),'r') as f: process_nodes(g, f, 'pnSeed6_main', 8333) diff --git a/contrib/verify-commits/trusted-git-root b/contrib/verify-commits/trusted-git-root index c60f8ab695..e560b98d02 100644 --- a/contrib/verify-commits/trusted-git-root +++ b/contrib/verify-commits/trusted-git-root @@ -1 +1 @@ -82bcf405f6db1d55b684a1f63a4aabad376cdad7 +11049f4fe62606d1b0380a9ef800ac130f0fbadf diff --git a/contrib/zmq/zmq_sub.py b/contrib/zmq/zmq_sub.py index 6e44c56f52..60768dc59a 100755..100644 --- a/contrib/zmq/zmq_sub.py +++ b/contrib/zmq/zmq_sub.py @@ -38,7 +38,7 @@ port = 28332 class ZMQHandler(): def __init__(self): - self.loop = zmq.asyncio.install() + self.loop = asyncio.get_event_loop() self.zmqContext = zmq.asyncio.Context() self.zmqSubSocket = self.zmqContext.socket(zmq.SUB) diff --git a/contrib/zmq/zmq_sub3.4.py b/contrib/zmq/zmq_sub3.4.py index 536352d5ff..0df843c9a3 100755..100644 --- a/contrib/zmq/zmq_sub3.4.py +++ b/contrib/zmq/zmq_sub3.4.py @@ -42,7 +42,7 @@ port = 28332 class ZMQHandler(): def __init__(self): - self.loop = zmq.asyncio.install() + self.loop = asyncio.get_event_loop() self.zmqContext = zmq.asyncio.Context() self.zmqSubSocket = self.zmqContext.socket(zmq.SUB) diff --git a/depends/Makefile b/depends/Makefile index 0ddd348e53..14e94ba453 100644 --- a/depends/Makefile +++ b/depends/Makefile @@ -21,7 +21,6 @@ BUILD_ID_SALT ?= salt host:=$(BUILD) ifneq ($(HOST),) host:=$(HOST) -host_toolchain:=$(HOST)- endif ifneq ($(DEBUG),) diff --git a/depends/config.guess b/depends/config.guess index 69ed3e573b..9baaa270bf 100755 --- a/depends/config.guess +++ b/depends/config.guess @@ -1,8 +1,8 @@ #! /bin/sh # Attempt to guess a canonical system name. -# Copyright 1992-2017 Free Software Foundation, Inc. +# Copyright 1992-2018 Free Software Foundation, Inc. -timestamp='2017-03-05' +timestamp='2018-01-26' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ timestamp='2017-03-05' # General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, see <http://www.gnu.org/licenses/>. +# along with this program; if not, see <https://www.gnu.org/licenses/>. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -27,7 +27,7 @@ timestamp='2017-03-05' # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess +# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess # # Please send patches to <config-patches@gnu.org>. @@ -39,7 +39,7 @@ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. -Operation modes: +Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit @@ -50,7 +50,7 @@ version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright 1992-2017 Free Software Foundation, Inc. +Copyright 1992-2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -107,9 +107,9 @@ trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in - ,,) echo "int x;" > $dummy.c ; + ,,) echo "int x;" > "$dummy.c" ; for c in cc gcc c89 c99 ; do - if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + if ($c -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; @@ -132,14 +132,14 @@ UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown -case "${UNAME_SYSTEM}" in +case "$UNAME_SYSTEM" in Linux|GNU|GNU/*) # If the system lacks a compiler, then just pick glibc. # We could probably try harder. LIBC=gnu - eval $set_cc_for_build - cat <<-EOF > $dummy.c + eval "$set_cc_for_build" + cat <<-EOF > "$dummy.c" #include <features.h> #if defined(__UCLIBC__) LIBC=uclibc @@ -149,13 +149,20 @@ Linux|GNU|GNU/*) LIBC=gnu #endif EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`" + + # If ldd exists, use it to detect musl libc. + if command -v ldd >/dev/null && \ + ldd --version 2>&1 | grep -q ^musl + then + LIBC=musl + fi ;; esac # Note: order is significant - the case branches are not exclusive. -case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in +case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, @@ -169,30 +176,30 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ - /sbin/$sysctl 2>/dev/null || \ - /usr/sbin/$sysctl 2>/dev/null || \ + "/sbin/$sysctl" 2>/dev/null || \ + "/usr/sbin/$sysctl" 2>/dev/null || \ echo unknown)` - case "${UNAME_MACHINE_ARCH}" in + case "$UNAME_MACHINE_ARCH" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) - arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'` - endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'` - machine=${arch}${endian}-unknown + arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` + endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` + machine="${arch}${endian}"-unknown ;; - *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + *) machine="$UNAME_MACHINE_ARCH"-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently (or will in the future) and ABI. - case "${UNAME_MACHINE_ARCH}" in + case "$UNAME_MACHINE_ARCH" in earm*) os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) - eval $set_cc_for_build + eval "$set_cc_for_build" if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then @@ -208,10 +215,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in ;; esac # Determine ABI tags. - case "${UNAME_MACHINE_ARCH}" in + case "$UNAME_MACHINE_ARCH" in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' - abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"` + abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` ;; esac # The OS release @@ -219,46 +226,55 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. - case "${UNAME_VERSION}" in + case "$UNAME_VERSION" in Debian*) release='-gnu' ;; *) - release=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2` + release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "${machine}-${os}${release}${abi}" + echo "$machine-${os}${release}${abi}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} + echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE" exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE" exit ;; *:LibertyBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-libertybsd${UNAME_RELEASE} + echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE" + exit ;; + *:MidnightBSD:*:*) + echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE" exit ;; *:ekkoBSD:*:*) - echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE" exit ;; *:SolidBSD:*:*) - echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE" exit ;; macppc:MirBSD:*:*) - echo powerpc-unknown-mirbsd${UNAME_RELEASE} + echo powerpc-unknown-mirbsd"$UNAME_RELEASE" exit ;; *:MirBSD:*:*) - echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE" exit ;; *:Sortix:*:*) - echo ${UNAME_MACHINE}-unknown-sortix + echo "$UNAME_MACHINE"-unknown-sortix + exit ;; + *:Redox:*:*) + echo "$UNAME_MACHINE"-unknown-redox exit ;; + mips:OSF1:*.*) + echo mips-dec-osf1 + exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) @@ -310,28 +326,19 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. - echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`" # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; - Alpha\ *:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # Should we change UNAME_MACHINE based on the output of uname instead - # of the specific Alpha model? - echo alpha-pc-interix - exit ;; - 21064:Windows_NT:50:3) - echo alpha-dec-winnt3.5 - exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-amigaos + echo "$UNAME_MACHINE"-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-morphos + echo "$UNAME_MACHINE"-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition @@ -343,7 +350,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) - echo arm-acorn-riscix${UNAME_RELEASE} + echo arm-acorn-riscix"$UNAME_RELEASE" exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos @@ -370,19 +377,19 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) - echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" exit ;; sun4H:SunOS:5.*:*) - echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) - echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) - echo i386-pc-auroraux${UNAME_RELEASE} + echo i386-pc-auroraux"$UNAME_RELEASE" exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) - eval $set_cc_for_build + eval "$set_cc_for_build" SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. @@ -395,13 +402,13 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in SUN_ARCH=x86_64 fi fi - echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. - echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in @@ -410,25 +417,25 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. - echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`" exit ;; sun3*:SunOS:*:*) - echo m68k-sun-sunos${UNAME_RELEASE} + echo m68k-sun-sunos"$UNAME_RELEASE" exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` - test "x${UNAME_RELEASE}" = x && UNAME_RELEASE=3 + test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) - echo m68k-sun-sunos${UNAME_RELEASE} + echo m68k-sun-sunos"$UNAME_RELEASE" ;; sun4) - echo sparc-sun-sunos${UNAME_RELEASE} + echo sparc-sun-sunos"$UNAME_RELEASE" ;; esac exit ;; aushp:SunOS:*:*) - echo sparc-auspex-sunos${UNAME_RELEASE} + echo sparc-auspex-sunos"$UNAME_RELEASE" exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not @@ -439,44 +446,44 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} + echo m68k-atari-mint"$UNAME_RELEASE" exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} + echo m68k-atari-mint"$UNAME_RELEASE" exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} + echo m68k-atari-mint"$UNAME_RELEASE" exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) - echo m68k-milan-mint${UNAME_RELEASE} + echo m68k-milan-mint"$UNAME_RELEASE" exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) - echo m68k-hades-mint${UNAME_RELEASE} + echo m68k-hades-mint"$UNAME_RELEASE" exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) - echo m68k-unknown-mint${UNAME_RELEASE} + echo m68k-unknown-mint"$UNAME_RELEASE" exit ;; m68k:machten:*:*) - echo m68k-apple-machten${UNAME_RELEASE} + echo m68k-apple-machten"$UNAME_RELEASE" exit ;; powerpc:machten:*:*) - echo powerpc-apple-machten${UNAME_RELEASE} + echo powerpc-apple-machten"$UNAME_RELEASE" exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) - echo mips-dec-ultrix${UNAME_RELEASE} + echo mips-dec-ultrix"$UNAME_RELEASE" exit ;; VAX*:ULTRIX*:*:*) - echo vax-dec-ultrix${UNAME_RELEASE} + echo vax-dec-ultrix"$UNAME_RELEASE" exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) - echo clipper-intergraph-clix${UNAME_RELEASE} + echo clipper-intergraph-clix"$UNAME_RELEASE" exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + eval "$set_cc_for_build" + sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include <stdio.h> /* for printf() prototype */ int main (int argc, char *argv[]) { @@ -485,23 +492,23 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) - printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) - printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) - printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF - $CC_FOR_BUILD -o $dummy $dummy.c && - dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && - SYSTEM_NAME=`$dummy $dummyarg` && + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && + dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`"$dummy" "$dummyarg"` && { echo "$SYSTEM_NAME"; exit; } - echo mips-mips-riscos${UNAME_RELEASE} + echo mips-mips-riscos"$UNAME_RELEASE" exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax @@ -527,17 +534,17 @@ EOF AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` - if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ] then - if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ - [ ${TARGET_BINARY_INTERFACE}x = x ] + if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \ + [ "$TARGET_BINARY_INTERFACE"x = x ] then - echo m88k-dg-dgux${UNAME_RELEASE} + echo m88k-dg-dgux"$UNAME_RELEASE" else - echo m88k-dg-dguxbcs${UNAME_RELEASE} + echo m88k-dg-dguxbcs"$UNAME_RELEASE" fi else - echo i586-dg-dgux${UNAME_RELEASE} + echo i586-dg-dgux"$UNAME_RELEASE" fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) @@ -554,7 +561,7 @@ EOF echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) - echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`" exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id @@ -566,14 +573,14 @@ EOF if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi - echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV" exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + eval "$set_cc_for_build" + sed 's/^ //' << EOF > "$dummy.c" #include <sys/systemcfg.h> main() @@ -584,7 +591,7 @@ EOF exit(0); } EOF - if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` then echo "$SYSTEM_NAME" else @@ -598,7 +605,7 @@ EOF exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` - if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc @@ -607,18 +614,18 @@ EOF IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi - echo ${IBM_ARCH}-ibm-aix${IBM_REV} + echo "$IBM_ARCH"-ibm-aix"$IBM_REV" exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; - ibmrt:4.4BSD:*|romp-ibm:BSD:*) + ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and - echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx @@ -633,28 +640,28 @@ EOF echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - case "${UNAME_MACHINE}" in - 9000/31? ) HP_ARCH=m68000 ;; - 9000/[34]?? ) HP_ARCH=m68k ;; + HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` + case "$UNAME_MACHINE" in + 9000/31?) HP_ARCH=m68000 ;; + 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` - case "${sc_cpu_version}" in + case "$sc_cpu_version" in 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 - case "${sc_kernel_bits}" in + case "$sc_kernel_bits" in 32) HP_ARCH=hppa2.0n ;; 64) HP_ARCH=hppa2.0w ;; '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi - if [ "${HP_ARCH}" = "" ]; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + if [ "$HP_ARCH" = "" ]; then + eval "$set_cc_for_build" + sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE #include <stdlib.h> @@ -687,13 +694,13 @@ EOF exit (0); } EOF - (CCOPTS="" $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac - if [ ${HP_ARCH} = hppa2.0w ] + if [ "$HP_ARCH" = hppa2.0w ] then - eval $set_cc_for_build + eval "$set_cc_for_build" # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler @@ -712,15 +719,15 @@ EOF HP_ARCH=hppa64 fi fi - echo ${HP_ARCH}-hp-hpux${HPUX_REV} + echo "$HP_ARCH"-hp-hpux"$HPUX_REV" exit ;; ia64:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - echo ia64-hp-hpux${HPUX_REV} + HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux"$HPUX_REV" exit ;; 3050*:HI-UX:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + eval "$set_cc_for_build" + sed 's/^ //' << EOF > "$dummy.c" #include <unistd.h> int main () @@ -745,11 +752,11 @@ EOF exit (0); } EOF - $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; - 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) @@ -758,7 +765,7 @@ EOF *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; - hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) @@ -766,9 +773,9 @@ EOF exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then - echo ${UNAME_MACHINE}-unknown-osf1mk + echo "$UNAME_MACHINE"-unknown-osf1mk else - echo ${UNAME_MACHINE}-unknown-osf1 + echo "$UNAME_MACHINE"-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) @@ -793,128 +800,109 @@ EOF echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) - echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) - echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) - echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) - echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) - echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) - echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) - echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE" exit ;; sparc*:BSD/OS:*:*) - echo sparc-unknown-bsdi${UNAME_RELEASE} + echo sparc-unknown-bsdi"$UNAME_RELEASE" exit ;; *:BSD/OS:*:*) - echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE" exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` - case ${UNAME_PROCESSOR} in + case "$UNAME_PROCESSOR" in amd64) UNAME_PROCESSOR=x86_64 ;; i386) UNAME_PROCESSOR=i586 ;; esac - echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" exit ;; i*:CYGWIN*:*) - echo ${UNAME_MACHINE}-pc-cygwin + echo "$UNAME_MACHINE"-pc-cygwin exit ;; *:MINGW64*:*) - echo ${UNAME_MACHINE}-pc-mingw64 + echo "$UNAME_MACHINE"-pc-mingw64 exit ;; *:MINGW*:*) - echo ${UNAME_MACHINE}-pc-mingw32 + echo "$UNAME_MACHINE"-pc-mingw32 exit ;; *:MSYS*:*) - echo ${UNAME_MACHINE}-pc-msys - exit ;; - i*:windows32*:*) - # uname -m includes "-pc" on this system. - echo ${UNAME_MACHINE}-mingw32 + echo "$UNAME_MACHINE"-pc-msys exit ;; i*:PW*:*) - echo ${UNAME_MACHINE}-pc-pw32 + echo "$UNAME_MACHINE"-pc-pw32 exit ;; *:Interix*:*) - case ${UNAME_MACHINE} in + case "$UNAME_MACHINE" in x86) - echo i586-pc-interix${UNAME_RELEASE} + echo i586-pc-interix"$UNAME_RELEASE" exit ;; authenticamd | genuineintel | EM64T) - echo x86_64-unknown-interix${UNAME_RELEASE} + echo x86_64-unknown-interix"$UNAME_RELEASE" exit ;; IA64) - echo ia64-unknown-interix${UNAME_RELEASE} + echo ia64-unknown-interix"$UNAME_RELEASE" exit ;; esac ;; - [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) - echo i${UNAME_MACHINE}-pc-mks - exit ;; - 8664:Windows_NT:*) - echo x86_64-pc-mks - exit ;; - i*:Windows_NT*:* | Pentium*:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we - # UNAME_MACHINE based on the output of uname instead of i386? - echo i586-pc-interix - exit ;; i*:UWIN*:*) - echo ${UNAME_MACHINE}-pc-uwin + echo "$UNAME_MACHINE"-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; - p*:CYGWIN*:*) - echo powerpcle-unknown-cygwin - exit ;; prep*:SunOS:5.*:*) - echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; *:GNU:*:*) # the GNU system - echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`" exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland - echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} + echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC" exit ;; i*86:Minix:*:*) - echo ${UNAME_MACHINE}-pc-minix + echo "$UNAME_MACHINE"-pc-minix exit ;; aarch64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in @@ -928,63 +916,63 @@ EOF esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC=gnulibc1 ; fi - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; arc:Linux:*:* | arceb:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; arm*:Linux:*:*) - eval $set_cc_for_build + eval "$set_cc_for_build" if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then - echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi else - echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf fi fi exit ;; avr32*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; cris:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-${LIBC} + echo "$UNAME_MACHINE"-axis-linux-"$LIBC" exit ;; crisv32:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-${LIBC} + echo "$UNAME_MACHINE"-axis-linux-"$LIBC" exit ;; e2k:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; frv:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; hexagon:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; i*86:Linux:*:*) - echo ${UNAME_MACHINE}-pc-linux-${LIBC} + echo "$UNAME_MACHINE"-pc-linux-"$LIBC" exit ;; ia64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; k1om:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; m32r*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; m68*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; mips:Linux:*:* | mips64:Linux:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + eval "$set_cc_for_build" + sed 's/^ //' << EOF > "$dummy.c" #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el @@ -998,70 +986,70 @@ EOF #endif #endif EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` - test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } + eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU'`" + test "x$CPU" != x && { echo "$CPU-unknown-linux-$LIBC"; exit; } ;; mips64el:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; openrisc*:Linux:*:*) - echo or1k-unknown-linux-${LIBC} + echo or1k-unknown-linux-"$LIBC" exit ;; or32:Linux:*:* | or1k*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; padre:Linux:*:*) - echo sparc-unknown-linux-${LIBC} + echo sparc-unknown-linux-"$LIBC" exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-${LIBC} + echo hppa64-unknown-linux-"$LIBC" exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in - PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; - PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; - *) echo hppa-unknown-linux-${LIBC} ;; + PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;; + PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;; + *) echo hppa-unknown-linux-"$LIBC" ;; esac exit ;; ppc64:Linux:*:*) - echo powerpc64-unknown-linux-${LIBC} + echo powerpc64-unknown-linux-"$LIBC" exit ;; ppc:Linux:*:*) - echo powerpc-unknown-linux-${LIBC} + echo powerpc-unknown-linux-"$LIBC" exit ;; ppc64le:Linux:*:*) - echo powerpc64le-unknown-linux-${LIBC} + echo powerpc64le-unknown-linux-"$LIBC" exit ;; ppcle:Linux:*:*) - echo powerpcle-unknown-linux-${LIBC} + echo powerpcle-unknown-linux-"$LIBC" exit ;; riscv32:Linux:*:* | riscv64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; s390:Linux:*:* | s390x:Linux:*:*) - echo ${UNAME_MACHINE}-ibm-linux-${LIBC} + echo "$UNAME_MACHINE"-ibm-linux-"$LIBC" exit ;; sh64*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; sh*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; tile*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; vax:Linux:*:*) - echo ${UNAME_MACHINE}-dec-linux-${LIBC} + echo "$UNAME_MACHINE"-dec-linux-"$LIBC" exit ;; x86_64:Linux:*:*) - echo ${UNAME_MACHINE}-pc-linux-${LIBC} + echo "$UNAME_MACHINE"-pc-linux-"$LIBC" exit ;; xtensa*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. @@ -1075,34 +1063,34 @@ EOF # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. - echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION" exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. - echo ${UNAME_MACHINE}-pc-os2-emx + echo "$UNAME_MACHINE"-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) - echo ${UNAME_MACHINE}-unknown-stop + echo "$UNAME_MACHINE"-unknown-stop exit ;; i*86:atheos:*:*) - echo ${UNAME_MACHINE}-unknown-atheos + echo "$UNAME_MACHINE"-unknown-atheos exit ;; i*86:syllable:*:*) - echo ${UNAME_MACHINE}-pc-syllable + echo "$UNAME_MACHINE"-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) - echo i386-unknown-lynxos${UNAME_RELEASE} + echo i386-unknown-lynxos"$UNAME_RELEASE" exit ;; i*86:*DOS:*:*) - echo ${UNAME_MACHINE}-pc-msdosdjgpp + echo "$UNAME_MACHINE"-pc-msdosdjgpp exit ;; - i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) - UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + i*86:*:4.*:*) + UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then - echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL" else - echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL" fi exit ;; i*86:*:5:[678]*) @@ -1112,12 +1100,12 @@ EOF *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac - echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}{$UNAME_VERSION}" exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name` - echo ${UNAME_MACHINE}-pc-isc$UNAME_REL + echo "$UNAME_MACHINE"-pc-isc"$UNAME_REL" elif /bin/uname -X 2>/dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 @@ -1127,9 +1115,9 @@ EOF && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 - echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL" else - echo ${UNAME_MACHINE}-pc-sysv32 + echo "$UNAME_MACHINE"-pc-sysv32 fi exit ;; pc:*:*:*) @@ -1149,9 +1137,9 @@ EOF exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then - echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. - echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) @@ -1171,9 +1159,9 @@ EOF test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; @@ -1182,28 +1170,28 @@ EOF test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) - echo m68k-unknown-lynxos${UNAME_RELEASE} + echo m68k-unknown-lynxos"$UNAME_RELEASE" exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) - echo sparc-unknown-lynxos${UNAME_RELEASE} + echo sparc-unknown-lynxos"$UNAME_RELEASE" exit ;; rs6000:LynxOS:2.*:*) - echo rs6000-unknown-lynxos${UNAME_RELEASE} + echo rs6000-unknown-lynxos"$UNAME_RELEASE" exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) - echo powerpc-unknown-lynxos${UNAME_RELEASE} + echo powerpc-unknown-lynxos"$UNAME_RELEASE" exit ;; SM[BE]S:UNIX_SV:*:*) - echo mips-dde-sysv${UNAME_RELEASE} + echo mips-dde-sysv"$UNAME_RELEASE" exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 @@ -1214,7 +1202,7 @@ EOF *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` - echo ${UNAME_MACHINE}-sni-sysv4 + echo "$UNAME_MACHINE"-sni-sysv4 else echo ns32k-sni-sysv fi @@ -1234,23 +1222,23 @@ EOF exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. - echo ${UNAME_MACHINE}-stratus-vos + echo "$UNAME_MACHINE"-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) - echo m68k-apple-aux${UNAME_RELEASE} + echo m68k-apple-aux"$UNAME_RELEASE" exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then - echo mips-nec-sysv${UNAME_RELEASE} + echo mips-nec-sysv"$UNAME_RELEASE" else - echo mips-unknown-sysv${UNAME_RELEASE} + echo mips-unknown-sysv"$UNAME_RELEASE" fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. @@ -1269,49 +1257,56 @@ EOF echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) - echo sx4-nec-superux${UNAME_RELEASE} + echo sx4-nec-superux"$UNAME_RELEASE" exit ;; SX-5:SUPER-UX:*:*) - echo sx5-nec-superux${UNAME_RELEASE} + echo sx5-nec-superux"$UNAME_RELEASE" exit ;; SX-6:SUPER-UX:*:*) - echo sx6-nec-superux${UNAME_RELEASE} + echo sx6-nec-superux"$UNAME_RELEASE" exit ;; SX-7:SUPER-UX:*:*) - echo sx7-nec-superux${UNAME_RELEASE} + echo sx7-nec-superux"$UNAME_RELEASE" exit ;; SX-8:SUPER-UX:*:*) - echo sx8-nec-superux${UNAME_RELEASE} + echo sx8-nec-superux"$UNAME_RELEASE" exit ;; SX-8R:SUPER-UX:*:*) - echo sx8r-nec-superux${UNAME_RELEASE} + echo sx8r-nec-superux"$UNAME_RELEASE" exit ;; SX-ACE:SUPER-UX:*:*) - echo sxace-nec-superux${UNAME_RELEASE} + echo sxace-nec-superux"$UNAME_RELEASE" exit ;; Power*:Rhapsody:*:*) - echo powerpc-apple-rhapsody${UNAME_RELEASE} + echo powerpc-apple-rhapsody"$UNAME_RELEASE" exit ;; *:Rhapsody:*:*) - echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE" exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown - eval $set_cc_for_build + eval "$set_cc_for_build" if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi - if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then + if test "`echo "$UNAME_RELEASE" | sed -e 's/\..*//'`" -le 10 ; then if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi + # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc + if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_PPC >/dev/null + then + UNAME_PROCESSOR=powerpc + fi fi elif test "$UNAME_PROCESSOR" = i386 ; then # Avoid executing cc on OS X 10.9, as it ships with a stub @@ -1322,7 +1317,7 @@ EOF # that Apple uses in portable devices. UNAME_PROCESSOR=x86_64 fi - echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE" exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` @@ -1330,22 +1325,25 @@ EOF UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi - echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE" exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; - NEO-?:NONSTOP_KERNEL:*:*) - echo neo-tandem-nsk${UNAME_RELEASE} + NEO-*:NONSTOP_KERNEL:*:*) + echo neo-tandem-nsk"$UNAME_RELEASE" exit ;; NSE-*:NONSTOP_KERNEL:*:*) - echo nse-tandem-nsk${UNAME_RELEASE} + echo nse-tandem-nsk"$UNAME_RELEASE" exit ;; - NSR-?:NONSTOP_KERNEL:*:*) - echo nsr-tandem-nsk${UNAME_RELEASE} + NSR-*:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk"$UNAME_RELEASE" exit ;; - NSX-?:NONSTOP_KERNEL:*:*) - echo nsx-tandem-nsk${UNAME_RELEASE} + NSV-*:NONSTOP_KERNEL:*:*) + echo nsv-tandem-nsk"$UNAME_RELEASE" + exit ;; + NSX-*:NONSTOP_KERNEL:*:*) + echo nsx-tandem-nsk"$UNAME_RELEASE" exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux @@ -1354,7 +1352,7 @@ EOF echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) - echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE" exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 @@ -1365,7 +1363,7 @@ EOF else UNAME_MACHINE="$cputype" fi - echo ${UNAME_MACHINE}-unknown-plan9 + echo "$UNAME_MACHINE"-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 @@ -1386,14 +1384,14 @@ EOF echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) - echo mips-sei-seiux${UNAME_RELEASE} + echo mips-sei-seiux"$UNAME_RELEASE" exit ;; *:DragonFly:*:*) - echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` - case "${UNAME_MACHINE}" in + case "$UNAME_MACHINE" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; @@ -1402,32 +1400,44 @@ EOF echo i386-pc-xenix exit ;; i*86:skyos:*:*) - echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE} | sed -e 's/ .*$//'` + echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`" exit ;; i*86:rdos:*:*) - echo ${UNAME_MACHINE}-pc-rdos + echo "$UNAME_MACHINE"-pc-rdos exit ;; i*86:AROS:*:*) - echo ${UNAME_MACHINE}-pc-aros + echo "$UNAME_MACHINE"-pc-aros exit ;; x86_64:VMkernel:*:*) - echo ${UNAME_MACHINE}-unknown-esx + echo "$UNAME_MACHINE"-unknown-esx exit ;; amd64:Isilon\ OneFS:*:*) echo x86_64-unknown-onefs exit ;; esac +echo "$0: unable to guess system type" >&2 + +case "$UNAME_MACHINE:$UNAME_SYSTEM" in + mips:Linux | mips64:Linux) + # If we got here on MIPS GNU/Linux, output extra information. + cat >&2 <<EOF + +NOTE: MIPS GNU/Linux systems require a C compiler to fully recognize +the system type. Please install a C compiler and try again. +EOF + ;; +esac + cat >&2 <<EOF -$0: unable to guess system type This script (version $timestamp), has failed to recognize the -operating system you are using. If your script is old, overwrite -config.guess and config.sub with the latest versions from: +operating system you are using. If your script is old, overwrite *all* +copies of config.guess and config.sub with the latest versions from: - http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess + https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess and - http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub + https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub If $0 has already been updated, send the following data and any information you think might be pertinent to config-patches@gnu.org to @@ -1450,16 +1460,16 @@ hostinfo = `(hostinfo) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` -UNAME_MACHINE = ${UNAME_MACHINE} -UNAME_RELEASE = ${UNAME_RELEASE} -UNAME_SYSTEM = ${UNAME_SYSTEM} -UNAME_VERSION = ${UNAME_VERSION} +UNAME_MACHINE = "$UNAME_MACHINE" +UNAME_RELEASE = "$UNAME_RELEASE" +UNAME_SYSTEM = "$UNAME_SYSTEM" +UNAME_VERSION = "$UNAME_VERSION" EOF exit 1 # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'write-file-functions 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" diff --git a/depends/config.site.in b/depends/config.site.in index 0a4a9c327e..8444dc26f2 100644 --- a/depends/config.site.in +++ b/depends/config.site.in @@ -64,7 +64,6 @@ LDFLAGS="-L$depends_prefix/lib $LDFLAGS" CC="@CC@" CXX="@CXX@" OBJC="${CC}" -CCACHE=$depends_prefix/native/bin/ccache PYTHONPATH=$depends_prefix/native/lib/python/dist-packages:$PYTHONPATH if test -n "@AR@"; then diff --git a/depends/config.sub b/depends/config.sub index 40ea5dfe11..818892c1c3 100755 --- a/depends/config.sub +++ b/depends/config.sub @@ -1,8 +1,8 @@ #! /bin/sh # Configuration validation subroutine script. -# Copyright 1992-2017 Free Software Foundation, Inc. +# Copyright 1992-2018 Free Software Foundation, Inc. -timestamp='2017-04-02' +timestamp='2018-01-15' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ timestamp='2017-04-02' # General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, see <http://www.gnu.org/licenses/>. +# along with this program; if not, see <https://www.gnu.org/licenses/>. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -33,7 +33,7 @@ timestamp='2017-04-02' # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub +# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases @@ -57,7 +57,7 @@ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. -Operation modes: +Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit @@ -67,7 +67,7 @@ Report bugs and patches to <config-patches@gnu.org>." version="\ GNU config.sub ($timestamp) -Copyright 1992-2017 Free Software Foundation, Inc. +Copyright 1992-2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -94,7 +94,7 @@ while test $# -gt 0 ; do *local*) # First pass through any local machine types. - echo $1 + echo "$1" exit ;; * ) @@ -112,7 +112,7 @@ esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. -maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +maybe_os=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ @@ -120,16 +120,16 @@ case $maybe_os in kopensolaris*-gnu* | cloudabi*-eabi* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; android-linux) os=-linux-android - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown + basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ;; *) - basic_machine=`echo $1 | sed 's/-[^-]*$//'` - if [ $basic_machine != $1 ] - then os=`echo $1 | sed 's/.*-/-/'` + basic_machine=`echo "$1" | sed 's/-[^-]*$//'` + if [ "$basic_machine" != "$1" ] + then os=`echo "$1" | sed 's/.*-/-/'` else os=; fi ;; esac @@ -178,44 +178,44 @@ case $os in ;; -sco6) os=-sco5v6 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -udk*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -lynx*178) os=-lynxos178 @@ -227,10 +227,7 @@ case $os in os=-lynxos ;; -ptx*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` - ;; - -windowsnt*) - os=`echo $os | sed -e 's/windowsnt/winnt/'` + basic_machine=`echo "$1" | sed -e 's/86-.*/86-sequent/'` ;; -psos*) os=-psos @@ -299,7 +296,7 @@ case $basic_machine in | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ | open8 | or1k | or1knd | or32 \ - | pdp10 | pdp11 | pj | pjl \ + | pdp10 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pru \ | pyramid \ @@ -316,7 +313,6 @@ case $basic_machine in | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | visium \ | wasm32 \ - | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown @@ -337,7 +333,7 @@ case $basic_machine in basic_machine=$basic_machine-unknown os=-none ;; - m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65) ;; ms1) basic_machine=mt-unknown @@ -366,7 +362,7 @@ case $basic_machine in ;; # Object if more than one company name word. *-*-*) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. @@ -461,7 +457,7 @@ case $basic_machine in # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) - basic_machine=i386-unknown + basic_machine=i386-pc os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) @@ -495,7 +491,7 @@ case $basic_machine in basic_machine=x86_64-pc ;; amd64-*) - basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=x86_64-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl @@ -540,7 +536,7 @@ case $basic_machine in os=-linux ;; blackfin-*) - basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=bfin-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) @@ -548,13 +544,13 @@ case $basic_machine in os=-cnk ;; c54x-*) - basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=tic54x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c55x-*) - basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=tic55x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c6x-*) - basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=tic6x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray @@ -643,7 +639,7 @@ case $basic_machine in basic_machine=rs6000-bull os=-bosx ;; - dpx2* | dpx2*-bull) + dpx2*) basic_machine=m68k-bull os=-sysv3 ;; @@ -652,7 +648,7 @@ case $basic_machine in os=$os"spe" ;; e500v[12]-*) - basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=$os"spe" ;; ebmon29k) @@ -744,9 +740,6 @@ case $basic_machine in hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; - hppa-next) - os=-nextstep3 - ;; hppaosf) basic_machine=hppa1.1-hp os=-osf @@ -759,26 +752,26 @@ case $basic_machine in basic_machine=i370-ibm ;; i*86v32) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; - i386-vsta | vsta) + vsta) basic_machine=i386-unknown os=-vsta ;; @@ -797,19 +790,16 @@ case $basic_machine in os=-sysv ;; leon-*|leon[3-9]-*) - basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'` + basic_machine=sparc-`echo "$basic_machine" | sed 's/-.*//'` ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) - basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=m68k-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=-linux ;; - m88k-omron*) - basic_machine=m88k-omron - ;; magnum | m3230) basic_machine=mips-mips os=-sysv @@ -841,10 +831,10 @@ case $basic_machine in os=-mint ;; mips3*-*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'` ;; mips3*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k @@ -863,7 +853,7 @@ case $basic_machine in os=-msdos ;; ms1-*) - basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + basic_machine=`echo "$basic_machine" | sed -e 's/ms1-/mt-/'` ;; msys) basic_machine=i686-pc @@ -905,7 +895,7 @@ case $basic_machine in basic_machine=v70-nec os=-sysv ;; - next | m*-next ) + next | m*-next) basic_machine=m68k-next case $os in -nextstep* ) @@ -950,6 +940,9 @@ case $basic_machine in nsr-tandem) basic_machine=nsr-tandem ;; + nsv-tandem) + basic_machine=nsv-tandem + ;; nsx-tandem) basic_machine=nsx-tandem ;; @@ -985,7 +978,7 @@ case $basic_machine in os=-linux ;; parisc-*) - basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=hppa-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=-linux ;; pbd) @@ -1001,7 +994,7 @@ case $basic_machine in basic_machine=i386-pc ;; pc98-*) - basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=i386-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc @@ -1016,16 +1009,16 @@ case $basic_machine in basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) - basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=i586-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentium4-*) - basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=i786-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould @@ -1035,23 +1028,23 @@ case $basic_machine in ppc | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) - basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) - basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=powerpcle-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; - ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ppc64-*) basic_machine=powerpc64-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) - basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=powerpc64le-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm @@ -1105,17 +1098,10 @@ case $basic_machine in sequent) basic_machine=i386-sequent ;; - sh) - basic_machine=sh-hitachi - os=-hms - ;; sh5el) basic_machine=sh5le-unknown ;; - sh64) - basic_machine=sh64-unknown - ;; - sparclite-wrs | simso-wrs) + simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; @@ -1134,7 +1120,7 @@ case $basic_machine in os=-sysv4 ;; strongarm-* | thumb-*) - basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` + basic_machine=arm-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun @@ -1248,9 +1234,6 @@ case $basic_machine in basic_machine=a29k-wrs os=-vxworks ;; - wasm32) - basic_machine=wasm32-unknown - ;; w65*) basic_machine=w65-wdc os=-none @@ -1259,6 +1242,9 @@ case $basic_machine in basic_machine=hppa1.1-winbond os=-proelf ;; + x64) + basic_machine=x86_64-pc + ;; xbox) basic_machine=i686-pc os=-mingw32 @@ -1267,20 +1253,12 @@ case $basic_machine in basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) - basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` + basic_machine=`echo "$basic_machine" | sed 's/^xscale/arm/'` ;; ymp) basic_machine=ymp-cray os=-unicos ;; - z8k-*-coff) - basic_machine=z8k-unknown - os=-sim - ;; - z80-*-coff) - basic_machine=z80-unknown - os=-sim - ;; none) basic_machine=none-none os=-none @@ -1309,10 +1287,6 @@ case $basic_machine in vax) basic_machine=vax-dec ;; - pdp10) - # there are many clones, so DEC is not a safe bet - basic_machine=pdp10-unknown - ;; pdp11) basic_machine=pdp11-dec ;; @@ -1322,9 +1296,6 @@ case $basic_machine in sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; - sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) - basic_machine=sparc-sun - ;; cydra) basic_machine=cydra-cydrome ;; @@ -1344,7 +1315,7 @@ case $basic_machine in # Make sure to match an already-canonicalized machine name. ;; *) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2 exit 1 ;; esac @@ -1352,10 +1323,10 @@ esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) - basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + basic_machine=`echo "$basic_machine" | sed 's/digital.*/dec/'` ;; *-commodore*) - basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + basic_machine=`echo "$basic_machine" | sed 's/commodore.*/cbm/'` ;; *) ;; @@ -1366,8 +1337,8 @@ esac if [ x"$os" != x"" ] then case $os in - # First match some system type aliases - # that might get confused with valid system types. + # First match some system type aliases that might get confused + # with valid system types. # -solaris* is a basic system type, with this one exception. -auroraux) os=-auroraux @@ -1378,18 +1349,19 @@ case $os in -solaris) os=-solaris2 ;; - -svr4*) - os=-sysv4 - ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; - # First accept the basic system types. + # es1800 is here to avoid being matched by es* (a different OS) + -es1800*) + os=-ose + ;; + # Now accept the basic system types. # The portable systems comes first. - # Each alternative MUST END IN A *, to match a version number. + # Each alternative MUST end in a * to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ @@ -1399,7 +1371,7 @@ case $os in | -aos* | -aros* | -cloudabi* | -sortix* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ - | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ + | -hiux* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ @@ -1410,14 +1382,14 @@ case $os in | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ - | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ - | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -morphos* | -superux* | -rtmk* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ - | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox*) + | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox* | -bme*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) @@ -1434,12 +1406,12 @@ case $os in -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; - -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ - | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ + -sim | -xray | -os68k* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) - os=`echo $os | sed -e 's|mac|macos|'` + os=`echo "$os" | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc @@ -1448,10 +1420,10 @@ case $os in os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) - os=`echo $os | sed -e 's|sunos5|solaris2|'` + os=`echo "$os" | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) - os=`echo $os | sed -e 's|sunos6|solaris3|'` + os=`echo "$os" | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition @@ -1462,12 +1434,6 @@ case $os in -wince*) os=-wince ;; - -osfrose*) - os=-osfrose - ;; - -osf*) - os=-osf - ;; -utek*) os=-bsd ;; @@ -1492,7 +1458,7 @@ case $os in -nova*) os=-rtmk-nova ;; - -ns2 ) + -ns2) os=-nextstep2 ;; -nsk*) @@ -1514,7 +1480,7 @@ case $os in -oss*) os=-sysv3 ;; - -svr4) + -svr4*) os=-sysv4 ;; -svr3) @@ -1529,24 +1495,28 @@ case $os in -ose*) os=-ose ;; - -es1800*) - os=-ose - ;; - -xenix) - os=-xenix - ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; - -aros*) - os=-aros - ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; + -pikeos*) + # Until real need of OS specific support for + # particular features comes up, bare metal + # configurations are quite functional. + case $basic_machine in + arm*) + os=-eabi + ;; + *) + os=-elf + ;; + esac + ;; -nacl*) ;; -ios) @@ -1556,7 +1526,7 @@ case $os in *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` - echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2 exit 1 ;; esac @@ -1652,9 +1622,6 @@ case $basic_machine in *-be) os=-beos ;; - *-haiku) - os=-haiku - ;; *-ibm) os=-aix ;; @@ -1694,7 +1661,7 @@ case $basic_machine in m88k-omron*) os=-luna ;; - *-next ) + *-next) os=-nextstep ;; *-sequent) @@ -1709,9 +1676,6 @@ case $basic_machine in i370-*) os=-mvs ;; - *-next) - os=-nextstep3 - ;; *-gould) os=-sysv ;; @@ -1821,15 +1785,15 @@ case $basic_machine in vendor=stratus ;; esac - basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + basic_machine=`echo "$basic_machine" | sed "s/unknown/$vendor/"` ;; esac -echo $basic_machine$os +echo "$basic_machine$os" exit # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'write-file-functions 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" diff --git a/depends/hosts/default.mk b/depends/hosts/default.mk index 6f60d6b3fd..144e5f88b7 100644 --- a/depends/hosts/default.mk +++ b/depends/hosts/default.mk @@ -1,3 +1,7 @@ +ifneq ($(host),$(build)) +host_toolchain:=$(host)- +endif + default_host_CC = $(host_toolchain)gcc default_host_CXX = $(host_toolchain)g++ default_host_AR = $(host_toolchain)ar diff --git a/depends/packages/expat.mk b/depends/packages/expat.mk index 7f484724a4..acbc60eea3 100644 --- a/depends/packages/expat.mk +++ b/depends/packages/expat.mk @@ -1,8 +1,8 @@ package=expat -$(package)_version=2.2.1 -$(package)_download_path=https://downloads.sourceforge.net/project/expat/expat/$($(package)_version) +$(package)_version=2.2.5 +$(package)_download_path=https://github.com/libexpat/libexpat/releases/download/R_2_2_5/ $(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=1868cadae4c82a018e361e2b2091de103cd820aaacb0d6cfa49bd2cd83978885 +$(package)_sha256_hash=d9dc32efba7e74f788fcc4f212a43216fc37cf5f23f4c2339664d473353aedf6 define $(package)_set_vars $(package)_config_opts=--disable-static diff --git a/depends/packages/miniupnpc.mk b/depends/packages/miniupnpc.mk index 1bb8cb5d26..5ad2b580d2 100644 --- a/depends/packages/miniupnpc.mk +++ b/depends/packages/miniupnpc.mk @@ -1,12 +1,12 @@ package=miniupnpc -$(package)_version=2.0.20170509 +$(package)_version=2.0.20180203 $(package)_download_path=http://miniupnp.free.fr/files $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=d3c368627f5cdfb66d3ebd64ca39ba54d6ff14a61966dbecb8dd296b7039f16a +$(package)_sha256_hash=90dda8c7563ca6cd4a83e23b3c66dbbea89603a1675bfdb852897c2c9cc220b7 define $(package)_set_vars $(package)_build_opts=CC="$($(package)_cc)" -$(package)_build_opts_darwin=OS=Darwin LIBTOOL="$($(package)_libtool)" +$(package)_build_opts_darwin=LIBTOOL="$($(package)_libtool)" $(package)_build_opts_mingw32=-f Makefile.mingw $(package)_build_env+=CFLAGS="$($(package)_cflags) $($(package)_cppflags)" AR="$($(package)_ar)" endef diff --git a/depends/packages/native_biplist.mk b/depends/packages/native_biplist.mk index 3c6e8900f6..5f247e9bf3 100644 --- a/depends/packages/native_biplist.mk +++ b/depends/packages/native_biplist.mk @@ -1,14 +1,9 @@ package=native_biplist -$(package)_version=0.9 -$(package)_download_path=https://pypi.python.org/packages/source/b/biplist +$(package)_version=1.0.3 +$(package)_download_path=https://bitbucket.org/wooster/biplist/downloads $(package)_file_name=biplist-$($(package)_version).tar.gz -$(package)_sha256_hash=b57cadfd26e4754efdf89e9e37de87885f9b5c847b2615688ca04adfaf6ca604 +$(package)_sha256_hash=4c0549764c5fe50b28042ec21aa2e14fe1a2224e239a1dae77d9e7f3932aa4c6 $(package)_install_libdir=$(build_prefix)/lib/python/dist-packages -$(package)_patches=sorted_list.patch - -define $(package)_preprocess_cmds - patch -p1 < $($(package)_patch_dir)/sorted_list.patch -endef define $(package)_build_cmds python setup.py build diff --git a/depends/packages/native_ccache.mk b/depends/packages/native_ccache.mk deleted file mode 100644 index 966804ce8b..0000000000 --- a/depends/packages/native_ccache.mk +++ /dev/null @@ -1,25 +0,0 @@ -package=native_ccache -$(package)_version=3.3.4 -$(package)_download_path=https://samba.org/ftp/ccache -$(package)_file_name=ccache-$($(package)_version).tar.bz2 -$(package)_sha256_hash=fa9d7f38367431bc86b19ad107d709ca7ecf1574fdacca01698bdf0a47cd8567 - -define $(package)_set_vars -$(package)_config_opts= -endef - -define $(package)_config_cmds - $($(package)_autoconf) -endef - -define $(package)_build_cmds - $(MAKE) -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install -endef - -define $(package)_postprocess_cmds - rm -rf lib include -endef diff --git a/depends/packages/native_mac_alias.mk b/depends/packages/native_mac_alias.mk index 488ec8b59c..306c835656 100644 --- a/depends/packages/native_mac_alias.mk +++ b/depends/packages/native_mac_alias.mk @@ -1,14 +1,9 @@ package=native_mac_alias -$(package)_version=2.0.6 +$(package)_version=2.0.7 $(package)_download_path=https://github.com/al45tair/mac_alias/archive/ $(package)_file_name=v$($(package)_version).tar.gz -$(package)_sha256_hash=78a3332d9a597eebf09ae652d38ad1e263b28db5c2e6dd725fad357b03b77371 +$(package)_sha256_hash=6f606d3b6bccd2112aeabf1a063f5b5ece87005a5d7e97c8faca23b916e88838 $(package)_install_libdir=$(build_prefix)/lib/python/dist-packages -$(package)_patches=python3.patch - -define $(package)_preprocess_cmds - patch -p1 < $($(package)_patch_dir)/python3.patch -endef define $(package)_build_cmds python setup.py build diff --git a/depends/packages/openssl.mk b/depends/packages/openssl.mk index 5ee9f17a63..37f0c28a52 100644 --- a/depends/packages/openssl.mk +++ b/depends/packages/openssl.mk @@ -47,6 +47,7 @@ $(package)_config_opts_linux=-fPIC -Wa,--noexecstack $(package)_config_opts_x86_64_linux=linux-x86_64 $(package)_config_opts_i686_linux=linux-generic32 $(package)_config_opts_arm_linux=linux-generic32 +$(package)_config_opts_armv7l_linux=linux-generic32 $(package)_config_opts_aarch64_linux=linux-generic64 $(package)_config_opts_mipsel_linux=linux-generic32 $(package)_config_opts_mips_linux=linux-generic32 diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index 088723ebd0..551c9fa70b 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -1,5 +1,4 @@ packages:=boost openssl libevent zeromq -native_packages := native_ccache qt_native_packages = native_protobuf qt_packages = qrencode protobuf zlib diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index bbfdb766ed..8491927552 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -8,7 +8,7 @@ $(package)_dependencies=openssl zlib $(package)_linux_dependencies=freetype fontconfig libxcb libX11 xproto libXext $(package)_build_subdir=qtbase $(package)_qt_libs=corelib network widgets gui plugins testlib -$(package)_patches=mac-qmake.conf mingw-uuidof.patch pidlist_absolute.patch fix-xcb-include-order.patch fix_qt_pkgconfig.patch +$(package)_patches=mac-qmake.conf mingw-uuidof.patch pidlist_absolute.patch fix-xcb-include-order.patch fix_qt_pkgconfig.patch fix-cocoahelpers-macos.patch $(package)_qttranslations_file_name=qttranslations-$($(package)_suffix) $(package)_qttranslations_sha256_hash=3a15aebd523c6d89fb97b2d3df866c94149653a26d27a00aac9b6d3020bc5a1d @@ -140,6 +140,7 @@ define $(package)_preprocess_cmds patch -p1 < $($(package)_patch_dir)/pidlist_absolute.patch && \ patch -p1 < $($(package)_patch_dir)/fix-xcb-include-order.patch && \ patch -p1 < $($(package)_patch_dir)/fix_qt_pkgconfig.patch && \ + patch -p1 < $($(package)_patch_dir)/fix-cocoahelpers-macos.patch && \ echo "!host_build: QMAKE_CFLAGS += $($(package)_cflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ echo "!host_build: QMAKE_CXXFLAGS += $($(package)_cxxflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ echo "!host_build: QMAKE_LFLAGS += $($(package)_ldflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ diff --git a/depends/packages/zeromq.mk b/depends/packages/zeromq.mk index 3f97221e10..cde523370f 100644 --- a/depends/packages/zeromq.mk +++ b/depends/packages/zeromq.mk @@ -1,9 +1,9 @@ package=zeromq -$(package)_version=4.2.2 +$(package)_version=4.2.3 $(package)_download_path=https://github.com/zeromq/libzmq/releases/download/v$($(package)_version)/ $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=5b23f4ca9ef545d5bd3af55d305765e3ee06b986263b31967435d285a3e6df6b -$(package)_patches=0001-fix-build-with-older-mingw64.patch +$(package)_sha256_hash=8f1e2b2aade4dbfde98d82366d61baef2f62e812530160d2e6d0a5bb24e40bc0 +$(package)_patches=0001-fix-build-with-older-mingw64.patch 0002-disable-pthread_set_name_np.patch define $(package)_set_vars $(package)_config_opts=--without-docs --disable-shared --without-libsodium --disable-curve --disable-curve-keygen --disable-perf @@ -13,7 +13,7 @@ endef define $(package)_preprocess_cmds patch -p1 < $($(package)_patch_dir)/0001-fix-build-with-older-mingw64.patch && \ - ./autogen.sh + patch -p1 < $($(package)_patch_dir)/0002-disable-pthread_set_name_np.patch endef define $(package)_config_cmds diff --git a/depends/patches/native_biplist/sorted_list.patch b/depends/patches/native_biplist/sorted_list.patch deleted file mode 100644 index 89abdb1b71..0000000000 --- a/depends/patches/native_biplist/sorted_list.patch +++ /dev/null @@ -1,29 +0,0 @@ ---- a/biplist/__init__.py 2014-10-26 19:03:11.000000000 +0000 -+++ b/biplist/__init__.py 2016-07-19 19:30:17.663521999 +0000 -@@ -541,7 +541,7 @@ - return HashableWrapper(n) - elif isinstance(root, dict): - n = {} -- for key, value in iteritems(root): -+ for key, value in sorted(iteritems(root)): - n[self.wrapRoot(key)] = self.wrapRoot(value) - return HashableWrapper(n) - elif isinstance(root, list): -@@ -616,7 +616,7 @@ - elif isinstance(obj, dict): - size = proc_size(len(obj)) - self.incrementByteCount('dictBytes', incr=1+size) -- for key, value in iteritems(obj): -+ for key, value in sorted(iteritems(obj)): - check_key(key) - self.computeOffsets(key, asReference=True) - self.computeOffsets(value, asReference=True) -@@ -714,7 +714,7 @@ - keys = [] - values = [] - objectsToWrite = [] -- for key, value in iteritems(obj): -+ for key, value in sorted(iteritems(obj)): - keys.append(key) - values.append(value) - for key in keys: diff --git a/depends/patches/native_mac_alias/python3.patch b/depends/patches/native_mac_alias/python3.patch deleted file mode 100644 index 6f2f5534a2..0000000000 --- a/depends/patches/native_mac_alias/python3.patch +++ /dev/null @@ -1,56 +0,0 @@ -diff -dur a/mac_alias/alias.py b/mac_alias/alias.py ---- a/mac_alias/alias.py -+++ b/mac_alias/alias.py -@@ -258,10 +258,10 @@ - alias = Alias() - alias.appinfo = appinfo - -- alias.volume = VolumeInfo (volname.replace('/',':'), -+ alias.volume = VolumeInfo (volname.decode().replace('/',':'), - voldate, fstype, disktype, - volattrs, volfsid) -- alias.target = TargetInfo (kind, filename.replace('/',':'), -+ alias.target = TargetInfo (kind, filename.decode().replace('/',':'), - folder_cnid, cnid, - crdate, creator_code, type_code) - alias.target.levels_from = levels_from -@@ -276,9 +276,9 @@ - b.read(1) - - if tag == TAG_CARBON_FOLDER_NAME: -- alias.target.folder_name = value.replace('/',':') -+ alias.target.folder_name = value.decode().replace('/',':') - elif tag == TAG_CNID_PATH: -- alias.target.cnid_path = struct.unpack(b'>%uI' % (length // 4), -+ alias.target.cnid_path = struct.unpack('>%uI' % (length // 4), - value) - elif tag == TAG_CARBON_PATH: - alias.target.carbon_path = value -@@ -313,9 +313,9 @@ - alias.target.creation_date \ - = mac_epoch + datetime.timedelta(seconds=seconds) - elif tag == TAG_POSIX_PATH: -- alias.target.posix_path = value -+ alias.target.posix_path = value.decode() - elif tag == TAG_POSIX_PATH_TO_MOUNTPOINT: -- alias.volume.posix_path = value -+ alias.volume.posix_path = value.decode() - elif tag == TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE: - alias.volume.disk_image_alias = Alias.from_bytes(value) - elif tag == TAG_USER_HOME_LENGTH_PREFIX: -@@ -467,12 +467,12 @@ - - b.write(struct.pack(b'>hhQhhQ', - TAG_HIGH_RES_VOLUME_CREATION_DATE, -- 8, long(voldate * 65536), -+ 8, int(voldate * 65536), - TAG_HIGH_RES_CREATION_DATE, -- 8, long(crdate * 65536))) -+ 8, int(crdate * 65536))) - - if self.target.cnid_path: -- cnid_path = struct.pack(b'>%uI' % len(self.target.cnid_path), -+ cnid_path = struct.pack('>%uI' % len(self.target.cnid_path), - *self.target.cnid_path) - b.write(struct.pack(b'>hh', TAG_CNID_PATH, - len(cnid_path))) diff --git a/depends/patches/qt/fix-cocoahelpers-macos.patch b/depends/patches/qt/fix-cocoahelpers-macos.patch new file mode 100644 index 0000000000..1b43a9eff8 --- /dev/null +++ b/depends/patches/qt/fix-cocoahelpers-macos.patch @@ -0,0 +1,70 @@ +From 0707260a4f8e64dfadf1df5f935e74cabb7c7d27 Mon Sep 17 00:00:00 2001 +From: Jake Petroules <jake.petroules@qt.io> +Date: Sun, 1 Oct 2017 21:48:17 -0700 +Subject: [PATCH] Fix build error with macOS 10.13 SDK +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf8 +Content-Transfer-Encoding: 8bit + +Several of these variables/macros are no longer defined. We didn't +validate the preconditions on iOS, tvOS, or watchOS, so no +need to bother validating them on macOS either. Nor did we check the +OSStatus result on any platform anyways. + +Task-number: QTBUG-63401 +Change-Id: Ife64dff767cf6d3f4b839fc53ec486181c176bf3 +(cherry-picked from 861544583511d4e6f7745d2339b26ff1cd44132b) +Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io> +Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io> +--- + src/plugins/platforms/cocoa/qcocoahelpers.h | 2 +- + src/plugins/platforms/cocoa/qcocoahelpers.mm | 13 +------------ + 2 files changed, 2 insertions(+), 13 deletions(-) + +diff --git old/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.h new/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.h +index bbb3793..74371d5 100644 +--- old/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.h ++++ new/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.h +@@ -80,7 +80,7 @@ QColor qt_mac_toQColor(CGColorRef color); + // Creates a mutable shape, it's the caller's responsibility to release. + HIMutableShapeRef qt_mac_QRegionToHIMutableShape(const QRegion ®ion); + +-OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage); ++void qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage); + + NSDragOperation qt_mac_mapDropAction(Qt::DropAction action); + NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions); +diff --git old/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.mm new/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.mm +index cd73148..3f8429e 100644 +--- old/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.mm ++++ new/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.mm +@@ -544,15 +544,8 @@ NSRect qt_mac_flipRect(const QRect &rect) + return NSMakeRect(rect.x(), flippedY, rect.width(), rect.height()); + } + +-OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage) ++void qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage) + { +- // Verbatim copy if HIViewDrawCGImage (as shown on Carbon-Dev) +- OSStatus err = noErr; +- +- require_action(inContext != NULL, InvalidContext, err = paramErr); +- require_action(inBounds != NULL, InvalidBounds, err = paramErr); +- require_action(inImage != NULL, InvalidImage, err = paramErr); +- + CGContextSaveGState( inContext ); + CGContextTranslateCTM (inContext, 0, inBounds->origin.y + CGRectGetMaxY(*inBounds)); + CGContextScaleCTM(inContext, 1, -1); +@@ -560,10 +553,6 @@ OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGIm + CGContextDrawImage(inContext, *inBounds, inImage); + + CGContextRestoreGState(inContext); +-InvalidImage: +-InvalidBounds: +-InvalidContext: +- return err; + } + + Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum) +-- +2.7.4 diff --git a/depends/patches/zeromq/0002-disable-pthread_set_name_np.patch b/depends/patches/zeromq/0002-disable-pthread_set_name_np.patch new file mode 100644 index 0000000000..d220b54f3e --- /dev/null +++ b/depends/patches/zeromq/0002-disable-pthread_set_name_np.patch @@ -0,0 +1,35 @@ +From 6e6b47d5ab381c3df3b30bb0b0a6cf210dfb1eba Mon Sep 17 00:00:00 2001 +From: Cory Fields <cory-nospam-@coryfields.com> +Date: Mon, 5 Mar 2018 14:22:05 -0500 +Subject: [PATCH] disable pthread_set_name_np + +pthread_set_name_np adds a Glibc requirement on >= 2.12. +--- + src/thread.cpp | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/thread.cpp b/src/thread.cpp +index 4fc59c3e..c3fdfd46 100644 +--- a/src/thread.cpp ++++ b/src/thread.cpp +@@ -220,7 +220,7 @@ void zmq::thread_t::setThreadName(const char *name_) + */ + if (!name_) + return; +- ++#if 0 + #if defined(ZMQ_HAVE_PTHREAD_SETNAME_1) + int rc = pthread_setname_np(name_); + if(rc) return; +@@ -233,6 +233,8 @@ void zmq::thread_t::setThreadName(const char *name_) + #elif defined(ZMQ_HAVE_PTHREAD_SET_NAME) + pthread_set_name_np(descriptor, name_); + #endif ++#endif ++ return; + } + + #endif +-- +2.11.1 + diff --git a/doc/README_osx.md b/doc/README_osx.md index 2a4460478c..975be4be9e 100644 --- a/doc/README_osx.md +++ b/doc/README_osx.md @@ -1,4 +1,4 @@ -Deterministic OS X Dmg Notes. +Deterministic OS X DMG Notes. Working OS X DMGs are created in Linux by combining a recent clang, the Apple binutils (ld, ar, etc) and DMG authoring tools. diff --git a/doc/REST-interface.md b/doc/REST-interface.md index f3dc124ece..7010edfcd3 100644 --- a/doc/REST-interface.md +++ b/doc/REST-interface.md @@ -45,7 +45,7 @@ Only supports JSON as output format. * verificationprogress : (numeric) estimate of verification progress [0..1] * chainwork : (string) total amount of work in active chain, in hexadecimal * pruned : (boolean) if the blocks are subject to pruning -* pruneheight : (numeric) heighest block available +* pruneheight : (numeric) highest block available * softforks : (array) status of softforks in progress * bip9_softforks : (object) status of BIP9 softforks in progress diff --git a/doc/build-osx.md b/doc/build-osx.md index 3e243933c8..2b84c7cc2c 100644 --- a/doc/build-osx.md +++ b/doc/build-osx.md @@ -90,23 +90,6 @@ Other commands: ./src/bitcoin-cli --help # Outputs a list of command-line options. ./src/bitcoin-cli help # Outputs a list of RPC commands when the daemon is running. -Using Qt Creator as IDE ------------------------- -You can use Qt Creator as an IDE, for bitcoin development. -Download and install the community edition of [Qt Creator](https://www.qt.io/download/). -Uncheck everything except Qt Creator during the installation process. - -1. Make sure you installed everything through Homebrew mentioned above -2. Do a proper ./configure --enable-debug -3. In Qt Creator do "New Project" -> Import Project -> Import Existing Project -4. Enter "bitcoin-qt" as project name, enter src/qt as location -5. Leave the file selection as it is -6. Confirm the "summary page" -7. In the "Projects" tab select "Manage Kits..." -8. Select the default "Desktop" kit and select "Clang (x86 64bit in /usr/bin)" as compiler -9. Select LLDB as debugger (you might need to set the path to your installation) -10. Start debugging with Qt Creator - Notes ----- diff --git a/doc/build-unix.md b/doc/build-unix.md index b823c23e0c..2d10484a65 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -326,6 +326,7 @@ For the wallet (optional): Then build using: ./autogen.sh + ./configure --disable-wallet # OR ./configure BDB_CFLAGS="-I${BDB_PREFIX}/include" BDB_LIBS="-L${BDB_PREFIX}/lib -ldb_cxx" gmake diff --git a/doc/build-windows.md b/doc/build-windows.md index 8d4afdc817..cff4ea9362 100644 --- a/doc/build-windows.md +++ b/doc/build-windows.md @@ -34,10 +34,9 @@ Full instructions to install WSL are available on the above link. To install WSL on Windows 10 with Fall Creators Update installed (version >= 16215.0) do the following: 1. Enable the Windows Subsystem for Linux feature - * From Start, search for "Turn Windows features on or off" (type 'turn') - * Select Windows Subsystem for Linux - * Click OK - * Restart if necessary + * Open the Windows Features dialog (`OptionalFeatures.exe`) + * Enable 'Windows Subsystem for Linux' + * Click 'OK' and restart if necessary 2. Install Ubuntu * Open Microsoft Store and search for Ubuntu or use [this link](https://www.microsoft.com/store/productId/9NBLGGH4MSV6) * Click Install diff --git a/doc/dependencies.md b/doc/dependencies.md index 5c5645de97..7aa96c4c9b 100644 --- a/doc/dependencies.md +++ b/doc/dependencies.md @@ -7,10 +7,9 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct | --- | --- | --- | --- | --- | --- | | Berkeley DB | [4.8.30](http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html) | 4.8.x | No | | | | Boost | [1.64.0](http://www.boost.org/users/download/) | [1.47.0](https://github.com/bitcoin/bitcoin/pull/8920) | No | | | -| ccache | [3.3.4](https://ccache.samba.org/download.html) | | No | | | | Clang | | [3.3+](http://llvm.org/releases/download.html) (C++11 support) | | | | | D-Bus | [1.10.18](https://cgit.freedesktop.org/dbus/dbus/tree/NEWS?h=dbus-1.10) | | No | Yes | | -| Expat | [2.2.1](https://libexpat.github.io/) | | No | Yes | | +| Expat | [2.2.5](https://libexpat.github.io/) | | No | Yes | | | fontconfig | [2.12.1](https://www.freedesktop.org/software/fontconfig/release/) | | No | Yes | | | FreeType | [2.7.1](http://download.savannah.gnu.org/releases/freetype) | | No | | | | GCC | | [4.8+](https://gcc.gnu.org/) | | | | @@ -18,7 +17,7 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct | libevent | [2.1.8-stable](https://github.com/libevent/libevent/releases) | 2.0.22 | No | | | | libjpeg | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L75) | | libpng | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L74) | -| MiniUPnPc | [2.0.20170509](http://miniupnp.free.fr/files) | | No | | | +| MiniUPnPc | [2.0.20180203](http://miniupnp.free.fr/files) | | No | | | | OpenSSL | [1.0.1k](https://www.openssl.org/source) | | Yes | | | | PCRE | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L76) | | protobuf | [2.6.3](https://github.com/google/protobuf/releases) | | No | | | @@ -27,5 +26,5 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct | Qt | [5.7.1](https://download.qt.io/official_releases/qt/) | 4.7+ | No | | | | XCB | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L94) (Linux only) | | xkbcommon | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L93) (Linux only) | -| ZeroMQ | [4.2.2](https://github.com/zeromq/libzmq/releases) | | No | | | +| ZeroMQ | [4.2.3](https://github.com/zeromq/libzmq/releases) | | No | | | | zlib | [1.2.11](http://zlib.net/) | | | | No | diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 9dc63a1e4b..a5468c3be3 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -132,6 +132,8 @@ Not OK (used plenty in the current source, but not picked up): A full list of comment syntaxes picked up by doxygen can be found at http://www.stack.nl/~dimitri/doxygen/manual/docblocks.html, but if possible use one of the above styles. +Documentation can be generated with `make docs` and cleaned up with `make clean-docs`. + Development tips and tricks --------------------------- @@ -140,6 +142,10 @@ Development tips and tricks Run configure with the --enable-debug option, then make. Or run configure with CXXFLAGS="-g -ggdb -O0" or whatever debug flags you need. +**compiling for gprof profiling** + +Run configure with the --enable-gprof option, then make. + **debug.log** If the code is behaving strangely, take a look in the debug.log file in the data directory; @@ -238,12 +244,8 @@ Threads - DumpAddresses : Dumps IP addresses of nodes to peers.dat. -- ThreadFlushWalletDB : Close the wallet.dat file if it hasn't been used in 500ms. - - ThreadRPCServer : Remote procedure call handler, listens on port 8332 for connections and services them. -- BitcoinMiner : Generates bitcoins (if wallet is enabled). - - Shutdown : Does an orderly shutdown of everything. Ignoring IDE/editor files @@ -380,6 +382,18 @@ C++ data structures - *Rationale*: Easier to understand what is happening, thus easier to spot mistakes, even for those that are not language lawyers +- Initialize all non-static class members where they are defined + + - *Rationale*: Initializing the members in the declaration makes it easy to spot uninitialized ones, + and avoids accidentally reading uninitialized memory + +```cpp +class A +{ + uint32_t m_count{0}; +} +``` + Strings and formatting ------------------------ @@ -415,11 +429,11 @@ member name: ```c++ class AddressBookPage { - Mode mode; + Mode m_mode; } AddressBookPage::AddressBookPage(Mode _mode) : - mode(_mode) + m_mode(_mode) ... ``` @@ -606,7 +620,7 @@ To create a scripted-diff: The scripted-diff is verified by the tool `contrib/devtools/commit-script-check.sh` -Commit `bb81e173` is an example of a scripted-diff. +Commit [`bb81e173`](https://github.com/bitcoin/bitcoin/commit/bb81e173) is an example of a scripted-diff. RPC interface guidelines -------------------------- diff --git a/doc/init.md b/doc/init.md index 75f9013f29..ffd13ae1f9 100644 --- a/doc/init.md +++ b/doc/init.md @@ -84,6 +84,8 @@ Installing this .service file consists of just copying it to To test, run `systemctl start bitcoind` and to enable for system startup run `systemctl enable bitcoind` +NOTE: When installing for systemd in Debian/Ubuntu the .service file needs to be copied to the /lib/systemd/system directory instead. + ### OpenRC Rename bitcoind.openrc to bitcoind and drop it in /etc/init.d. Double @@ -93,6 +95,8 @@ check ownership and permissions and make it executable. Test it with ### Upstart (for Debian/Ubuntu based distributions) +Upstart is the default init system for Debian/Ubuntu versions older than 15.04. If you are using version 15.04 or newer and haven't manually configured upstart you should follow the systemd instructions instead. + Drop bitcoind.conf in /etc/init. Test by running `service bitcoind start` it will automatically start on reboot. diff --git a/doc/release-notes.md b/doc/release-notes.md index 0292eaa4d2..a79012722f 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -48,7 +48,7 @@ Compatibility ============== Bitcoin Core is extensively tested on multiple operating systems using -the Linux kernel, macOS 10.8+, and Windows Vista and later. Windows XP is not supported. +the Linux kernel, macOS 10.8+, and Windows 7 and newer (Windows XP is not supported). Bitcoin Core should also work on most other Unix-like systems but is not frequently tested on them. @@ -56,6 +56,48 @@ frequently tested on them. Notable changes =============== +RPC changes +------------ + +### Low-level changes + +- The `createrawtransaction` RPC will now accept an array or dictionary (kept for compatibility) for the `outputs` parameter. This means the order of transaction outputs can be specified by the client. +- The `fundrawtransaction` RPC will reject the previously deprecated `reserveChangeKey` option. + +External wallet files +--------------------- + +The `-wallet=<path>` option now accepts full paths instead of requiring wallets +to be located in the -walletdir directory. + +Newly created wallet format +--------------------------- + +If `-wallet=<path>` is specified with a path that does not exist, it will now +create a wallet directory at the specified location (containing a wallet.dat +data file, a db.log file, and database/log.?????????? files) instead of just +creating a data file at the path and storing log files in the parent +directory. This should make backing up wallets more straightforward than +before because the specified wallet path can just be directly archived without +having to look in the parent directory for transaction log files. + +For backwards compatibility, wallet paths that are names of existing data files +in the `-walletdir` directory will continue to be accepted and interpreted the +same as before. + +Low-level RPC changes +--------------------- + +- When bitcoin is not started with any `-wallet=<path>` options, the name of + the default wallet returned by `getwalletinfo` and `listwallets` RPCs is + now the empty string `""` instead of `"wallet.dat"`. If bitcoin is started + with any `-wallet=<path>` options, there is no change in behavior, and the + name of any wallet is just its `<path>` string. + +### Logging + +- The log timestamp format is now ISO 8601 (e.g. "2018-02-28T12:34:56Z"). + Credits ======= diff --git a/doc/release-notes/release-notes-0.16.0.md b/doc/release-notes/release-notes-0.16.0.md new file mode 100644 index 0000000000..8f158b3481 --- /dev/null +++ b/doc/release-notes/release-notes-0.16.0.md @@ -0,0 +1,720 @@ +Bitcoin Core version 0.16.0 is now available from: + + <https://bitcoincore.org/bin/bitcoin-core-0.16.0/> + +This is a new major version release, including new features, various bugfixes +and performance improvements, as well as updated translations. + +Please report bugs using the issue tracker at GitHub: + + <https://github.com/bitcoin/bitcoin/issues> + +To receive security and update notifications, please subscribe to: + + <https://bitcoincore.org/en/list/announcements/join/> + +How to Upgrade +============== + +If you are running an older version, shut it down. Wait until it has completely +shut down (which might take a few minutes for older versions), then run the +installer (on Windows) or just copy over `/Applications/Bitcoin-Qt` (on Mac) +or `bitcoind`/`bitcoin-qt` (on Linux). + +The first time you run version 0.15.0 or newer, your chainstate database will be converted to a +new format, which will take anywhere from a few minutes to half an hour, +depending on the speed of your machine. + +Note that the block database format also changed in version 0.8.0 and there is no +automatic upgrade code from before version 0.8 to version 0.15.0 or higher. Upgrading +directly from 0.7.x and earlier without re-downloading the blockchain is not supported. +However, as usual, old wallet versions are still supported. + +Downgrading warning +------------------- + +Wallets created in 0.16 and later are not compatible with versions prior to 0.16 +and will not work if you try to use newly created wallets in older versions. Existing +wallets that were created with older versions are not affected by this. + +Compatibility +============== + +Bitcoin Core is extensively tested on multiple operating systems using +the Linux kernel, macOS 10.8+, and Windows Vista and later. Windows XP is not supported. + +Bitcoin Core should also work on most other Unix-like systems but is not +frequently tested on them. + +Notable changes +=============== + +Wallet changes +--------------- + +### Segwit Wallet + +Bitcoin Core 0.16.0 introduces full support for segwit in the wallet and user interfaces. A new `-addresstype` argument has been added, which supports `legacy`, `p2sh-segwit` (default), and `bech32` addresses. It controls what kind of addresses are produced by `getnewaddress`, `getaccountaddress`, and `createmultisigaddress`. A `-changetype` argument has also been added, with the same options, and by default equal to `-addresstype`, to control which kind of change is used. + +A new `address_type` parameter has been added to the `getnewaddress` and `addmultisigaddress` RPCs to specify which type of address to generate. +A `change_type` argument has been added to the `fundrawtransaction` RPC to override the `-changetype` argument for specific transactions. + +- All segwit addresses created through `getnewaddress` or `*multisig` RPCs explicitly get their redeemscripts added to the wallet file. This means that downgrading after creating a segwit address will work, as long as the wallet file is up to date. +- All segwit keys in the wallet get an implicit redeemscript added, without it being written to the file. This means recovery of an old backup will work, as long as you use new software. +- All keypool keys that are seen used in transactions explicitly get their redeemscripts added to the wallet files. This means that downgrading after recovering from a backup that includes a segwit address will work + +Note that some RPCs do not yet support segwit addresses. Notably, `signmessage`/`verifymessage` doesn't support segwit addresses, nor does `importmulti` at this time. Support for segwit in those RPCs will continue to be added in future versions. + +P2WPKH change outputs are now used by default if any destination in the transaction is a P2WPKH or P2WSH output. This is done to ensure the change output is as indistinguishable from the other outputs as possible in either case. + +### BIP173 (Bech32) Address support ("bc1..." addresses) + +Full support for native segwit addresses (BIP173 / Bech32) has now been added. +This includes the ability to send to BIP173 addresses (including non-v0 ones), and generating these +addresses (including as default new addresses, see above). + +A checkbox has been added to the GUI to select whether a Bech32 address or P2SH-wrapped address should be generated when using segwit addresses. When launched with `-addresstype=bech32` it is checked by default. When launched with `-addresstype=legacy` it is unchecked and disabled. + +### HD-wallets by default + +Due to a backward-incompatible change in the wallet database, wallets created +with version 0.16.0 will be rejected by previous versions. Also, version 0.16.0 +will only create hierarchical deterministic (HD) wallets. Note that this only applies +to new wallets; wallets made with previous versions will not be upgraded to be HD. + +### Replace-By-Fee by default in GUI + +The send screen now uses BIP125 RBF by default, regardless of `-walletrbf`. +There is a checkbox to mark the transaction as final. + +The RPC default remains unchanged: to use RBF, launch with `-walletrbf=1` or +use the `replaceable` argument for individual transactions. + +### Wallets directory configuration (`-walletdir`) + +Bitcoin Core now has more flexibility in where the wallets directory can be +located. Previously wallet database files were stored at the top level of the +bitcoin data directory. The behavior is now: + +- For new installations (where the data directory doesn't already exist), + wallets will now be stored in a new `wallets/` subdirectory inside the data + directory by default. +- For existing nodes (where the data directory already exists), wallets will be + stored in the data directory root by default. If a `wallets/` subdirectory + already exists in the data directory root, then wallets will be stored in the + `wallets/` subdirectory by default. +- The location of the wallets directory can be overridden by specifying a + `-walletdir=<path>` option where `<path>` can be an absolute path to a + directory or directory symlink. + +Care should be taken when choosing the wallets directory location, as if it +becomes unavailable during operation, funds may be lost. + +Build: Minimum GCC bumped to 4.8.x +------------------------------------ +The minimum version of the GCC compiler required to compile Bitcoin Core is now 4.8. No effort will be +made to support older versions of GCC. See discussion in issue #11732 for more information. +The minimum version for the Clang compiler is still 3.3. Other minimum dependency versions can be found in `doc/dependencies.md` in the repository. + +Support for signalling pruned nodes (BIP159) +--------------------------------------------- +Pruned nodes can now signal BIP159's NODE_NETWORK_LIMITED using service bits, in preparation for +full BIP159 support in later versions. This would allow pruned nodes to serve the most recent blocks. However, the current change does not yet include support for connecting to these pruned peers. + +Performance: SHA256 assembly enabled by default +------------------------------------------------- +The SHA256 hashing optimizations for architectures supporting SSE4, which lead to ~50% speedups in SHA256 on supported hardware (~5% faster synchronization and block validation), have now been enabled by default. In previous versions they were enabled using the `--enable-experimental-asm` flag when building, but are now the default and no longer deemed experimental. + +GUI changes +----------- +- Uses of "µBTC" in the GUI now also show the more colloquial term "bits", specified in BIP176. +- The option to reuse a previous address has now been removed. This was justified by the need to "resend" an invoice, but now that we have the request history, that need should be gone. +- Support for searching by TXID has been added, rather than just address and label. +- A "Use available balance" option has been added to the send coins dialog, to add the remaining available wallet balance to a transaction output. +- A toggle for unblinding the password fields on the password dialog has been added. + +RPC changes +------------ + +### New `rescanblockchain` RPC + +A new RPC `rescanblockchain` has been added to manually invoke a blockchain rescan. +The RPC supports start and end-height arguments for the rescan, and can be used in a +multiwallet environment to rescan the blockchain at runtime. + +### New `savemempool` RPC +A new `savemempool` RPC has been added which allows the current mempool to be saved to +disk at any time to avoid it being lost due to crashes / power loss. + +### Safe mode disabled by default + +Safe mode is now disabled by default and must be manually enabled (with `-disablesafemode=0`) if you wish to use it. Safe mode is a feature that disables a subset of RPC calls - mostly related to the wallet and sending - automatically in case certain problem conditions with the network are detected. However, developers have come to regard these checks as not reliable enough to act on automatically. Even with safe mode disabled, they will still cause warnings in the `warnings` field of the `getneworkinfo` RPC and launch the `-alertnotify` command. + +### Renamed script for creating JSON-RPC credentials + +The `share/rpcuser/rpcuser.py` script was renamed to `share/rpcauth/rpcauth.py`. This script can be +used to create `rpcauth` credentials for a JSON-RPC user. + +### Validateaddress improvements + +The `validateaddress` RPC output has been extended with a few new fields, and support for segwit addresses (both P2SH and Bech32). Specifically: +* A new field `iswitness` is True for P2WPKH and P2WSH addresses ("bc1..." addresses), but not for P2SH-wrapped segwit addresses (see below). +* The existing field `isscript` will now also report True for P2WSH addresses. +* A new field `embedded` is present for all script addresses where the script is known and matches something that can be interpreted as a known address. This is particularly true for P2SH-P2WPKH and P2SH-P2WSH addresses. The value for `embedded` includes much of the information `validateaddress` would report if invoked directly on the embedded address. +* For multisig scripts a new `pubkeys` field was added that reports the full public keys involved in the script (if known). This is a replacement for the existing `addresses` field (which reports the same information but encoded as P2PKH addresses), represented in a more useful and less confusing way. The `addresses` field remains present for non-segwit addresses for backward compatibility. +* For all single-key addresses with known key (even when wrapped in P2SH or P2WSH), the `pubkey` field will be present. In particular, this means that invoking `validateaddress` on the output of `getnewaddress` will always report the `pubkey`, even when the address type is P2SH-P2WPKH. + +### Low-level changes + +- The deprecated RPC `getinfo` was removed. It is recommended that the more specific RPCs are used: + * `getblockchaininfo` + * `getnetworkinfo` + * `getwalletinfo` + * `getmininginfo` +- The wallet RPC `getreceivedbyaddress` will return an error if called with an address not in the wallet. +- The wallet RPC `addwitnessaddress` was deprecated and will be removed in version 0.17, + set the `address_type` argument of `getnewaddress`, or option `-addresstype=[bech32|p2sh-segwit]` instead. +- `dumpwallet` now includes hex-encoded scripts from the wallet in the dumpfile, and + `importwallet` now imports these scripts, but corresponding addresses may not be added + correctly or a manual rescan may be required to find relevant transactions. +- The RPC `getblockchaininfo` now includes an `errors` field. +- A new `blockhash` parameter has been added to the `getrawtransaction` RPC which allows for a raw transaction to be fetched from a specific block if known, even without `-txindex` enabled. +- The `decoderawtransaction` and `fundrawtransaction` RPCs now have optional `iswitness` parameters to override the + heuristic witness checks if necessary. +- The `walletpassphrase` timeout is now clamped to 2^30 seconds. +- Using addresses with the `createmultisig` RPC is now deprecated, and will be removed in a later version. Public keys should be used instead. +- Blockchain rescans now no longer lock the wallet for the entire rescan process, so other RPCs can now be used at the same time (although results of balances / transactions may be incorrect or incomplete until the rescan is complete). +- The `logging` RPC has now been made public rather than hidden. +- An `initialblockdownload` boolean has been added to the `getblockchaininfo` RPC to indicate whether the node is currently in IBD or not. +- `minrelaytxfee` is now included in the output of `getmempoolinfo` + +Other changed command-line options +---------------------------------- +- `-debuglogfile=<file>` can be used to specify an alternative debug logging file. +- bitcoin-cli now has an `-stdinrpcpass` option to allow the RPC password to be read from standard input. +- The `-usehd` option has been removed. +- bitcoin-cli now supports a new `-getinfo` flag which returns an output like that of the now-removed `getinfo` RPC. + +Testing changes +---------------- +- The default regtest JSON-RPC port has been changed to 18443 to avoid conflict with testnet's default of 18332. +- Segwit is now always active in regtest mode by default. Thus, if you upgrade a regtest node you will need to either -reindex or use the old rules by adding `vbparams=segwit:0:999999999999` to your regtest bitcoin.conf. Failure to do this will result in a CheckBlockIndex() assertion failure that will look like: Assertion `(pindexFirstNeverProcessed != nullptr) == (pindex->nChainTx == 0)' failed. + +0.16.0 change log +------------------ + +### Block and transaction handling +- #10953 `aeed345` Combine scriptPubKey and amount as CTxOut in CScriptCheck (jl2012) +- #11309 `93d20a7` Minor cleanups for AcceptToMemoryPool (morcos) +- #11418 `38c201f` Add error string for CLEANSTACK script violation (maaku) +- #11411 `339da9c` Change SignatureHash input index check to an assert (jimpo) +- #11406 `e12522d` Add state message print to AcceptBlock failure message (TheBlueMatt) +- #11062 `26fee4f` Mark mempool import fails that were found in mempool as 'already there' (kallewoof) +- #11269 `61fb806` CTxMemPoolEntry::UpdateAncestorState: modifySiagOps param type (donaloconnor) +- #11747 `e970396` Fix: Open files read only if requested (Elbandi) +- #11737 `46d1ebf` Document partial validation in ConnectBlock() (sdaftuar) +- #10699 `c090262` Make all script validation flags backward compatible (sipa) +- #10279 `214046f` Add a CChainState class to validation.cpp to take another step towards clarifying internal interfaces (TheBlueMatt) +- #11824 `d9fdac1` Block ActivateBestChain to empty validationinterface queue (TheBlueMatt) +- #12127 `9501dc2` Remove unused mempool index (sdaftuar) +- #12118 `44080a9` Sort mempool by min(feerate, ancestor_feerate) (sdaftuar) +- #8498 `0e3a411` Minimize the number of times it is checked that no money... (jtimon) +- #12368 `3f5012b` Hold mempool.cs for the duration of ATMP (TheBlueMatt) +- #12401 `d44cd7e` Reset pblocktree before deleting LevelDB file (Sjors) +- #12415 `f893824` Interrupt loading thread after shutdown request (promag) + +### P2P protocol and network code +- #10596 `6866b49` Add vConnect to CConnman::Options (benma) +- #10663 `9d31ed2` Split resolve out of connect (theuni) +- #11113 `fef65c4` Ignore getheaders requests for very old side blocks (jimpo) +- #11585 `5aeaa9c` addrman: Add missing lock in Clear() (CAddrMan) (practicalswift) +- #11524 `5ef3b69` De-duplicate connection eviction logic (tjps) +- #11580 `1f4375f` Do not send (potentially) invalid headers in response to getheaders (TheBlueMatt) +- #11655 `aca77a4` Assert state.m_chain_sync.m_work_header in ConsiderEviction (practicalswift) +- #11744 `3ff6ff5` Add missing locks in net.{cpp,h} (practicalswift) +- #11740 `59d3dc8` Implement BIP159 NODE_NETWORK_LIMITED (pruned peers) *signaling only* (jonasschnelli) +- #11583 `37ffa16` Do not make it trivial for inbound peers to generate log entries (TheBlueMatt) +- #11363 `ba2f195` Split socket create/connect (theuni) +- #11917 `bc66765` Add testnet DNS seed: seed.testnet.bitcoin.sprovoost.nl (Sjors) +- #11512 `6e89de5` Use GetDesireableServiceFlags in seeds, dnsseeds, fixing static seed adding (TheBlueMatt) +- #12262 `16bac24` Hardcoded seed update (laanwj) +- #12270 `9cf6393` Update chainTxData for 0.16 (laanwj) +- #12392 `0f61651` Fix ignoring tx data requests when fPauseSend is set on a peer (TheBlueMatt) + +### Wallet +- #11039 `fc51565` Avoid second mapWallet lookup (promag) +- #10952 `2621673` Remove vchDefaultKey and have better first run detection (achow101) +- #11007 `fc5c237` Fix potential memory leak when loading a corrupted wallet file (practicalswift) +- #10976 `07c92b9` Move some static functions out of wallet.h/cpp (ryanofsky) +- #11117 `961901f` Prepare for non-Base58 addresses (sipa) +- #10916 `e6ab88a` add missing lock to crypter GetKeys() (benma) +- #10767 `791a0e6` Clarify wallet initialization / destruction interface (jnewbery) +- #11250 `c22a53c` Bump wallet version to 159900 and remove the `usehd` option (achow101) +- #11307 `4f7e37e` Display non-HD error on first run (MarcoFalke) +- #11408 `69c7ece` Fix parameter name typo in ErasePurpose walletdb method (PierreRochard) +- #11167 `aa624b6` Full BIP173 (Bech32) support (sipa) +- #11594 `0ecc630` Improve -disablewallet parameter interaction (promag) +- #10368 `77ba4bf` Remove helper conversion operator from wallet (kallewoof) +- #11074 `99ec126` Assert that CWallet::SyncMetaData finds oldest transaction (BitonicEelis) +- #11272 `e6e3fc3` CKeystore/CCrypter: move relevant implementation out of the header (jonasschnelli) +- #10286 `927a1d7` Call wallet notify callbacks in scheduler thread (without cs_main) (TheBlueMatt) +- #10600 `4ed8180` Make feebumper class stateless (ryanofsky) +- #11466 `d080a7d` Specify custom wallet directory with -walletdir param (MeshCollider) +- #11839 `8ab6c0b` Don't attempt mempool entry for wallet transactions on startup (instagibbs) +- #11854 `2214954` Split up key and script metadata for better type safety (ryanofsky) +- #11870 `ef8ba7d` Remove unnecessary mempool lock in ReacceptWalletTransactions (promag) +- #11864 `2ae58d5` Make CWallet::FundTransaction atomic (promag) +- #11886 `df71819` Clarify getbalance meaning a tiny bit in response to questions (TheBlueMatt) +- #11923 `81c89e9` Remove unused fNoncriticalErrors variable from CWalletDB::FindWalletTx (PierreRochard) +- #11726 `604e08c` Cleanups + nit fixes for walletdir PR (MeshCollider) +- #11403 `d889c03` Segwit wallet support (sipa) +- #11970 `b7450cd` Add test coverage for bitcoin-cli multiwallet calls (ryanofsky) +- #11904 `66e3af7` Add a lock to the wallet directory (MeshCollider) +- #12101 `c7978be` Clamp walletpassphrase timeout to 2^30 seconds and check its bounds (achow101) +- #12210 `17180fa` Deprecate addwitnessaddress (laanwj) +- #12220 `f4c942e` Error if relative -walletdir is specified (ryanofsky) +- #11281 `8470e64` Avoid permanent cs_main/cs_wallet lock during RescanFromTime (jonasschnelli) +- #12119 `9594139` Use P2WPKH change output if any destination is P2WPKH or P2WSH (Sjors) +- #12213 `eadb2da` Add address type option to addmultisigaddress (promag) +- #12276 `7936446` Remove duplicate mapWallet lookups (promag) + +### RPC and other APIs +- #11008 `3841aaf` Enable disablesafemode by default (gmaxwell) +- #11050 `7ed57d3` Avoid treating null RPC arguments different from missing arguments (ryanofsky) +- #10997 `affe927` Add option -stdinrpcpass to bitcoin-cli to allow RPC password to be read from standard input (jharvell) +- #11179 `e0e3cbb` Push down safe mode checks (laanwj) +- #11203 `d745b4c` add wtxid to mempool entry output (sdaftuar) +- #11099 `bc561b4` Add savemempool RPC (greenaddress) +- #10838 `66a5b41` (finally) remove getinfo (TheBlueMatt) +- #10753 `7fcd61b` test: Check RPC argument mapping (laanwj) +- #11288 `0f8e095` More user-friendly error message when partially signing (MeshCollider) +- #11031 `ef8340d` deprecate estimatefee (jnewbery) +- #10858 `9a8e916` Add "errors" field to getblockchaininfo and unify "errors" field in get*info RPCs (achow101) +- #11021 `90926db` Fix getchaintxstats() (AkioNak) +- #11367 `3a93270` getblockchaininfo: Add disk_size, prune_target_size (esotericnonsense) +- #11006 `a1d78b5` Improve shutdown process (promag) +- #11529 `ff92fbf` Avoid slow transaction search with txindex enabled (promag) +- #11618 `87d90ef` Lock cs_main in blockToJSON/blockheaderToJSON (practicalswift) +- #11626 `998c304` Make `logging` RPC public (laanwj) +- #11258 `033c786` Add initialblockdownload to getblockchaininfo (jnewbery) +- #11087 `99bc0b4` Diagnose unsuitable outputs in lockunspent() (BitonicEelis) +- #11710 `9388639` cli: Reject arguments to -getinfo (laanwj) +- #11738 `d4267a3` Fix sendrawtransaction hang when sending a tx already in mempool (TheBlueMatt) +- #11753 `32c9b57` clarify abortrescan rpc use (instagibbs) +- #11191 `ef14f2e` Improve help text and behavior of RPC-logging (AkioNak) +- #10874 `9e38d35` getblockchaininfo: Loop through the bip9 soft fork deployments instead of hard coding (achow101) +- #10275 `497d0e0` Allow fetching tx directly from specified block in getrawtransaction (kallewoof) +- #11178 `fee0370` Add iswitness parameter to decode- and fundrawtransaction RPCs (MeshCollider) +- #11667 `711d16c` Add scripts to dumpwallet RPC (MeshCollider) +- #11475 `9bad8d6` mempoolinfo should take ::minRelayTxFee into account (mess110) +- #12001 `a9a49e6` Adding ::minRelayTxFee amount to getmempoolinfo and updating help (jeffrade) +- #12198 `adce1de` Add deprecation error for `getinfo` (laanwj) +- #11415 `69ec021` Disallow using addresses in createmultisig (achow101) +- #12278 `288deac` Add special error for genesis coinbase to getrawtransaction (MeshCollider) +- #11362 `c6223b3` Remove nBlockMaxSize from miner opt struct as it is no longer used (gmaxwell) +- #10825 `28485c7` Set regtest JSON-RPC port to 18443 to avoid conflict with testnet 18332 (fametrano) +- #11303 `e542728` Fix estimatesmartfee rounding display issue (TheBlueMatt) +- #7061 `8c2de82` Add RPC call "rescanblockchain <startheight> <stopheight>" (jonasschnelli) +- #11055 `95e14dc` RPC getreceivedbyaddress should return error if called with address not owned by the wallet (jnewbery) +- #12366 `93de37a` http: Join worker threads before deleting work queue (laanwj) +- #12315 `758a41e` Bech32 addresses in dumpwallet (fivepiece) +- #12427 `3762ac1` Make signrawtransaction accept P2SH-P2WSH redeemscripts (sipa) + +### GUI +- #10964 `64e66bb` Pass SendCoinsRecipient (208 bytes) by reference (practicalswift) +- #11169 `5b8af7b` Make tabs toolbar no longer have a context menu (achow101) +- #10911 `9c8f365` Fix typo and access key in optionsdialog.ui (keystrike) +- #10770 `ea729d5` Drop upgrade-cancel callback registration for a generic "cancelable" (TheBlueMatt) +- #11156 `a3624dd` Fix memory leaks in qt/guiutil.cpp (danra) +- #11268 `31e72b2` [macOS] remove Growl support, remove unused code (jonasschnelli) +- #11193 `c5c77bd` Terminate string *pszExePath after readlink and without using memset (practicalswift) +- #11508 `ffa5159` Fix crash via division by zero assertion (jonasschnelli) +- #11499 `6157e8c` Add upload and download info to the peerlist (debug menu) (aarongolliver) +- #11480 `ffc0b11` Add toggle for unblinding password fields (tjps) +- #11316 `22cdf93` Add use available balance in send coins dialog (CryptAxe, promag) +- #3716 `13e352d` Receive: Remove option to reuse a previous address (luke-jr) +- #11690 `f0c1f8a` Fix the StartupWMClass for bitoin-qt, so gnome-shell can recognize it (eklitzke) +- #10920 `f6f8d54` Fix potential memory leak in newPossibleKey(ChangeCWallet *wallet) (practicalswift) +- #11698 `7293d06` RPC-Console nested commands documentation (lmlsna) +- #11395 `38d31f9` Enable searching by transaction id (luke-jr) +- #11556 `91eeaa0` Improved copy for RBF checkbox and tooltip (Sjors) +- #11809 `80f9dad` Fix proxy setting options dialog crash (laanwj) +- #11616 `8585bb8` Update ban-state in case of dirty-state during periodic sweep (jonasschnelli) +- #11605 `f19ca12` Enable RBF by default in QT (Sjors) +- #12074 `a1136f0` Optimizes boolean expression model && model->haveWatchOnly() (251Labs) +- #12035 `eeb6d52` Change µBTC to bits (jb55) +- #12092 `fd4ca17` Replaces numbered place marker %2 with %1 (251Labs) +- #12173 `bbc91b7` Use flexible font size for QRCode image address (jonasschnelli) +- #12211 `10d10d7` Avoid potential null dereference in ReceiveCoinsDialog constructor (ryanofsky) +- #12261 `f359afc` Bump BLOCK_CHAIN_SIZE to 200GB (laanwj) +- #11991 `062c8b6` Receive: checkbox for bech32 address (Sjors) +- #11644 `045a809` Fix qt build broken by 5a5e4e9 (TheBlueMatt) +- #11448 `d473e6d` reset addrProxy/addrSeparateProxyTor if colon char missing (mess110) +- #12377 `604f289` qt: Poll ShutdownTimer after init is done (MarcoFalke) +- #12374 `daaae36` qt: Make sure splash screen is freed on AppInitMain fail (laanwj) +- #12349 `ad10b90` shutdown: fix crash on shutdown with reindex-chainstate (theuni) + +### Build system +- #10923 `2c9f5ec` travis: Build with --enable-werror under OS X (practicalswift) +- #11176 `df8c722` build: Rename --enable-experimental-asm to --enable-asm and enable by default (laanwj) +- #11286 `11dacc6` [depends] Don't build libevent sample code (fanquake) +- #7142 `801dd40` Travis: Test build against system libs (& Qt4) (luke-jr) +- #11380 `390771b` Remove outdated share/certs/ directory (MeshCollider) +- #11391 `7632310` Remove lxcbr0 lines from gitian-build.sh (MeshCollider) +- #11435 `167cef8` build: Make "make clean" remove all files created when running "make check" (practicalswift) +- #11460 `e022463` [depends] mac_alias 2.0.6, ds_store 1.1.2 (fanquake) +- #11541 `bb9ab0f` Build: Fix Automake warnings when running autogen.sh (fanquake) +- #11611 `0e70791` [build] Don't fail when passed --disable-lcov and lcov isn't available (fanquake) +- #11651 `3c098a8` refactor: Make all #includes relative to project root (laanwj, MeshCollider, ryanofsky) +- #11621 `1f7695b` [build] Add temp_bitcoin_locale_qrc to CLEAN_QT to fix make distcheck (fanquake) +- #11755 `84fa645` [Docs] Bump minimum required version of GCC to 4.8 (fanquake) +- #9254 `6d3dc52` [depends] ZeroMQ 4.2.2 (fanquake) +- #11842 `3c8f0a3` [build] Add missing stuff to clean-local (kallewoof) +- #11936 `483bb67` [build] Warn that only libconsensus can be built without Boost (fanquake) +- #11945 `7a11ba7` Improve BSD compatibility of contrib/install_db4.sh (laanwj) +- #11981 `180a255` Fix gitian build after libzmq bump (theuni) +- #11903 `8f68fd2` [trivial] Add required package dependencies for depends cross compilation (jonasschnelli) +- #12168 `45cf8a0` #include sys/fcntl.h to just fcntl.h (without sys/) (jsarenik) +- #12095 `3fa1ab4` Use BDB_LIBS/CFLAGS and pass --disable-replication (fanquake) +- #11711 `6378e5c` bitcoin_qt.m4: Minor fixes and clean-ups (fanquake) +- #11989 `90d4104` .gitignore: add QT Creator artifacts (Sjors) +- #11577 `c0ae864` Fix warnings (-Wsign-compare) when building with DEBUG_ADDRMAN (practicalswift) + +### Tests and QA +- #11024 `3e55f13` Remove OldSetKeyFromPassphrase/OldEncrypt/OldDecrypt (practicalswift) +- #10679 `31b2612` Document the non-DER-conformance of one test in tx_valid.json (schildbach) +- #11160 `ede386c` Improve versionbits_computeblockversion test code consistency (danra) +- #10303 `f088a1b` Include ms/blk stats in Connect* benchmarks (kallewoof) +- #10777 `d81dccf` Avoid redundant assignments. Remove unused variables (practicalswift) +- #11260 `52f8877` travis: Assert default datadir isn't created, Run scripted diff only once (MarcoFalke) +- #11271 `638e6c5` travis: filter out pyenv (theuni) +- #11285 `3255d63` Add -usehd to excluded args in check-doc.py (MeshCollider) +- #11297 `16e4184` Make sure ~/.bitcoin doesn't exist before build (MeshCollider) +- #11311 `cce94c5` travis: Revert default datadir check (MarcoFalke) +- #11300 `f4ed44a` Add a lint check for trailing whitespace (MeshCollider) +- #11323 `4ce2f3d` mininode: add an optimistic write and disable nagle (theuni) +- #11370 `2d85899` Add getblockchaininfo functional test (promag) +- #11365 `f199b8a` Add Qt GUI tests to Overview and ReceiveCoin Page (anditto) +- #11293 `dbc4ae0` Deduplicate CMerkleBlock construction code, add test coverage (jamesob) +- #10440 `9e8ef9d` Add libFuzzer support (practicalswift) +- #10941 `364da2c` Add blocknotify and walletnotify functional tests (promag) +- #11420 `8928093` Bump univalue subtree and fix json formatting in tests (MarcoFalke) +- #10099 `424be03` Slightly Improve Unit Tests for Checkqueue (JeremyRubin) +- #11513 `14b860b` A few Python3 tidy ups (jnewbery) +- #11486 `2ca518d` Add uacomment tests (mess110) +- #11452 `02ac8c8` Improve ZMQ functional test (promag) +- #10409 `b5545d8` Add fuzz testing for BlockTransactions and BlockTransactionsRequest (practicalswift) +- #11389 `dd56166` Support having segwit always active in regtest (sipa, ajtowns, jnewbery) +- #11562 `5776582` bench: use std::chrono rather than gettimeofday (theuni) +- #11182 `f7388e9` Add P2P interface to TestNode (jnewbery) +- #11552 `b5f9f02` Improve wallet-accounts test (ryanofsky) +- #11638 `5e3f5e4` Dead mininode code (jnewbery) +- #11646 `fe503e1` Require a steady clock for bench with at least micro precision (TheBlueMatt) +- #11468 `76b3349` Make comp test framework more debuggable (jnewbery) +- #11623 `ee92243` Add missing locks to tests (practicalswift) +- #11035 `927e528` [contrib] Add Valgrind suppressions file (practicalswift) +- #11641 `7adeea3` Only allow disconnecting all NodeConns (MarcoFalke) +- #11677 `3bdf242` Remove unused NodeConn members (MarcoFalke) +- #11699 `66d46c7` [travis-ci] Only run linters on Pull Requests (jnewbery) +- #11654 `084f52f` Initialize recently introduced non-static class member lastCycles to zero in constructor (practicalswift) +- #11648 `ccc70a2` Add messages.py (jnewbery) +- #11713 `49667a7` Fix for mismatched extern definition in wallet tests (sipsorcery) +- #11707 `0d89fa0` Fix sendheaders (jnewbery) +- #11718 `9cdd2bc` Move pwalletMain to wallet test fixture (laanwj) +- #11714 `901ba3e` Test that mempool rejects coinbase transactions (jamesob) +- #11743 `3d6ad40` Add multiwallet prefix test (MarcoFalke) +- #11683 `a892218` Remove unused mininode functions {ser,deser}_int_vector(...). Remove unused imports (practicalswift) +- #11712 `9f2c2db` Split NodeConn from NodeConnCB (jnewbery) +- #11791 `13e31dd` Rename NodeConn and NodeConnCB (jnewbery) +- #11835 `f60b4ad` Add Travis check for unused Python imports (practicalswift) +- #11849 `ad1820c` Assert that only one NetworkThread exists (jnewbery) +- #11877 `d4991c0` Improve createrawtransaction functional tests (promag) +- #11220 `2971fd0` Check specific validation error in miner tests (Sjors) +- #11947 `797441e` Fix rawtransactions test (laanwj) +- #11946 `8049241` Remove unused variable (firstAddrnServices) (practicalswift) +- #11867 `18a1bba` Improve node network test (jnewbery) +- #11883 `cfd99dd` Add configuration file/argument testing (MeshCollider) +- #11879 `d4e404a` Remove redundant univalue_tests.cpp (jnewbery) +- #11748 `20166f8` Adding unit tests for GetDifficulty in blockchain.cpp (merehap) +- #11517 `5180a86` Improve benchmark precision (martinus) +- #11291 `a332a7d` Fix string concatenation to os.path.join and add exception case (dongsam) +- #11965 `d38d1a3` Note on test order in test_runner (MarcoFalke) +- #11997 `ddff344` util_tests.cpp: actually check ignored args (ajtowns) +- #12079 `45173fa` Improve prioritisetransaction test coverage (promag) +- #12150 `92a810d` Fix ListCoins test failure due to unset g_address_type, g_change_type (ryanofsky) +- #12133 `1d2eaba` Fix rare failure in p2p-segwit.py (sdaftuar) +- #12082 `0910cbe` Adding test case for SINGLE|ANYONECANPAY hash type in tx_valid.json (Christewart) +- #11796 `4db16ec` Functional test naming convention (ajtowns) +- #12227 `b987ca4` test_runner: Readable output if create_cache.py fails (ryanofsky) +- #12089 `126000b` Make TestNodeCLI command optional in send_cli (MarcoFalke) +- #11774 `6970b30` Rename functional tests (ajtowns) +- #12264 `598a9c4` Fix versionbits warning test (jnewbery) +- #12217 `1213be6` Add missing syncwithvalidationinterfacequeue to tests (MarcoFalke) +- #12292 `eebe458` Fix names of excluded extended tests for travis (ajtowns) +- #11789 `60d739e` [travis-ci] Combine logs on failure (jnewbery) +- #11838 `3e50024` Add getrawtransaction in_active_chain=False test (MarcoFalke) +- #12206 `898f560` Sync with validationinterface queue in sync_mempools (MarcoFalke) +- #12424 `ff44101` Fix rescan test failure due to unset g_address_type, g_change_type (ryanofsky) +- #12388 `e2431d1` travis: Full clone for git subtree check (MarcoFalke) + +### Documentation +- #10680 `6366941` Fix inconsistencies and grammar in various files (MeshCollider) +- #11011 `7db65c3` Add a comment on the use of prevector in script (gmaxwell) +- #10878 `c58128f` Fix Markdown formatting issues in init.md (dongcarl) +- #11066 `9e00a62` Document the preference of nullptr over NULL or (void*)0 (practicalswift) +- #11094 `271e40a` Hash in ZMQ hash is raw bytes, not hex (runn1ng) +- #11026 `ea3ac59` Bugfix: Use testnet RequireStandard for -acceptnonstdtxn default (luke-jr) +- #11058 `4b65fa5` Comments: More comments on functions/globals in standard.h (jimpo) +- #11112 `3f726c9` [developer-notes] By default, declare single-argument constructors "explicit" (practicalswift) +- #11155 `a084767` Trivial: Documentation fixes for CVectorWriter ctors (danra) +- #11136 `108222b` Docs: Add python3 to list of dependencies on some platforms (danra) +- #11216 `81f8c03` Update hmac_sha256.h (utsavgupta) +- #11236 `ba05971` Add note on translations to CONTRIBUTING.md (MeshCollider) +- #11173 `4eb1f39` RPC: Fix currency unit string in the help text (AkioNak) +- #11135 `21e2f2f` Update developer notes with RPC response guidelines (promag) +- #11219 `bcc8a62` explain how to recompile a modified unit test (Sjors) +- #10779 `f656147` Create dependencies.md (flack) +- #10682 `2a56baf` Move the AreInputsStandard documentation next to its implementation (esneider) +- #11276 `ee50c9e` Update CONTRIBUTING.md to reduce unnecessary review workload (jonasschnelli) +- #11264 `b148803` Fix broken Markdown table in dependencies.md (practicalswift) +- #10691 `ce82985` Properly comment about shutdown process in init.cpp file (wraith7) +- #11330 `ae233c4` Fix comments for DEFAULT_WHITELIST[FORCE]RELAY (danra) +- #11340 `d6d2c85` Fix validation comments (danra) +- #11305 `2847480` Update release notes and manpages for 0.16 (MarcoFalke) +- #11132 `551d7bf` Document assumptions that are being made to avoid NULL pointer dereferences (practicalswift) +- #11390 `12ed800` Document scripted-diff (jnewbery) +- #11392 `a3b4c59` Fix stale link in gitian-building.md (shooterman) +- #11401 `4202273` Move gitian building to external repo (MarcoFalke) +- #11414 `bbc901d` Remove partial gitian build instructions from descriptors dir (fanquake) +- #11571 `c95832d` Fixed a couple small grammatical errors (BitsInMyBlood) +- #11624 `f9b74ef` Change formatting for sequence of steps (vivganes) +- #11597 `6f01dcf` Fix error messages in CFeeBumper (kallewoof) +- #11438 `7fbf3c6` Updated Windows build doc for WSL/Xenial workaround (sipsorcery) +- #11663 `41aa9c4` Add getreceivedbyaddress release notes (MarcoFalke) +- #11533 `cbb54e7` Update WSL installation notes for Fall Creators update (Thoragh) +- #11680 `4db82b7` Add instructions for lcov report generation (jamesob) +- #11686 `54aedc0` Make ISSUE_TEMPLATE a bit shorter, mention hardware tests (TheBlueMatt) +- #11704 `ea68190` Windows build doc update (sipsorcery) +- #11706 `5197100` Make default issue text all comments to make issues more readable (TheBlueMatt) +- #11140 `1429132` Improve #endif comments (danra) +- #11729 `7a43fbb` links to code style guides (Sjors) +- #11793 `8879d50` Bump OS X version to 10.13 (Varunram) +- #11783 `16fff80` Fix shutdown in case of errors during initialization (laanwj) +- #11804 `00d25e9` Fixed outdated link with archive.is (TimothyShimmin) +- #11960 `4307062` Fix link to installation script (laudaa) +- #12027 `63a4dc1` Remove boost --c++ flag from osx build instructions (fernandezpablo85) +- #12062 `5961b23` Increment MIT Licence copyright header year on files modified in 2017 (akx20000a) +- #12063 `36a5a44` Update license year range to 2018 (akx20000a) +- #12093 `5691028` Fix incorrect Markdown link (practicalswift) +- #12143 `b0d626d` Fix link for BIP159 pull request (azuchi) +- #12112 `3c62868` Remove the ending slashes from RPC URI format (jackycjh) +- #12166 `e839d65` Clarify -walletdir usage (jnewbery) +- #12241 `b030133` Fix incorrect link in /test/ README.md (fanquake) +- #12187 `b5e4b9b` Updating benchmarkmarking.md with an updated sample output (jeffrade) +- #12294 `7cf1aea` Create NetBSD build instructions and fix compilation (fanquake) +- #12251 `cc5870a` initwallet: Do not translate highly technical addresstype help (MarcoFalke) +- #11984 `efae366` Update OpenBSD build instructions for 6.2 (cont'd) (laanwj) +- #12293 `9d9c418` Mention that HD is enabled if hdmasterkeyid is present in getwalletinfo RPC help (fanquake) +- #12077 `c04cb48` Correct `sendmany` curl example (251Labs) +- #10677 `b3ecb7b` Document that addmultisigaddress is intended for non-watchonly addresses (instagibbs) +- #12177 `cad504b` Fix address_type help text of getnewaddress and getrawchangeaddress (mruddy) + +### Refactoring +- #9964 `b6a4891` Add const to methods that do not modify the object for which it is called (practicalswift) +- #10965 `655970d` Replace deprecated throw() with noexcept specifier (C++11) (practicalswift) +- #10645 `c484ec6` Use nullptr (C++11) instead of zero (0) as the null pointer constant (practicalswift) +- #10901 `22e301a` Fix constness of ArgsManager methods (promag) +- #10969 `4afb5aa` Declare single-argument (non-converting) constructors "explicit" (practicalswift) +- #11071 `dbf6bd6` Use static_assert(…, …) (C++11) instead of assert(…) where appropriate (practicalswift) +- #10809 `c559884` optim: mark a few classes final (theuni) +- #10843 `2ab7c63` Add attribute [[noreturn]] (C++11) to functions that will not return (practicalswift) +- #11151 `7fd49d0` Fix header guards using reserved identifiers (danra) +- #11138 `2982511` Compat: Simplify bswap_16 implementation (danra) +- #11161 `745bbdc` Remove redundant explicitly defined copy ctors (danra) +- #11144 `cee4fe1` Move local include to before system includes (danra) +- #10781 `60dd9cc` Python cleanups (practicalswift) +- #10701 `50fae68` Remove the virtual specifier for functions with the override specifier (practicalswift) +- #11164 `38a54a5` Fix boost headers included as user instead of system headers (danra) +- #11143 `3aa60b7` Fix include path for bitcoin-config.h (danra) +- #8330 `59e1789` Structure Packing Optimizations in C{,Mutable}Transaction (JeremyRubin) +- #10845 `39ae413` Remove unreachable code (practicalswift) +- #11238 `6acdb1f` Add assertions before potential null deferences (MeshCollider) +- #11259 `089b742` Remove duplicate destination decoding (promag) +- #11232 `2f0d3e6` Ensure that data types are consistent (jjz) +- #10793 `efb4383` Changing &var[0] to var.data() (MeshCollider) +- #11196 `e278f86` Switch memory_cleanse implementation to BoringSSL's to ensure memory clearing even with -lto (maaku) +- #10888 `9821274` range-based loops and const qualifications in net.cpp (benma) +- #11351 `6c4fecf` Refactor: Modernize disallowed copy constructors/assignment (danra) +- #11385 `94c9015` Remove some unused functions and methods (sipa) +- #11301 `8776787` add m_added_nodes to connman options (benma) +- #11432 `058c0f9` Remove unused fTry from push_lock (promag) +- #11107 `e93fff1` Fix races in AppInitMain and others with lock and atomic bools (MeshCollider) +- #9572 `17f2ace` Skip witness sighash cache for non-segwit transactions (jl2012) +- #10961 `da0478e` Improve readability of DecodeBase58Check(...) (practicalswift) +- #11133 `a865b38` Document assumptions that are being made to avoid division by zero (practicalswift) +- #11073 `3bb77eb` Remove dead store in ecdsa_signature_parse_der_lax (BitonicEelis) +- #10898 `470c730` Fix invalid checks (NULL checks after dereference, redundant checks, etc.) (practicalswift) +- #11495 `50d72b3` [trivial] Make namespace explicit for is_regular_file (jnewbery) +- #11511 `db2f83e` [Init] Remove redundant exit(EXIT_FAILURE) instances and replace with return false (donaloconnor) +- #10866 `ef8a634` Fix -Wthread-safety-analysis warnings. Compile with -Wthread-safety-analysis if available (practicalswift) +- #11221 `0dec4cc` Refactor: simpler read (gnuser) +- #10696 `ef3758d` Remove redundant nullptr checks before deallocation (practicalswift) +- #11043 `5e9be16` Use std::unique_ptr (C++11) where possible (practicalswift) +- #11353 `05a7619` Small refactor of CCoinsViewCache::BatchWrite() (danra) +- #10749 `2adbddb` Use compile-time constants instead of unnamed enumerations (remove "enum hack") (practicalswift) +- #11603 `a933cb1` Move RPC registration out of AppInitParameterInteraction (ryanofsky) +- #11722 `26efc22` Switched sync.{cpp,h} to std threading primitives (tjps) +- #10493 `fbce66a` Use range-based for loops (C++11) when looping over map elements (practicalswift) +- #11337 `0d7e0a3` Fix code constness in CBlockIndex::GetAncestor() overloads (danra) +- #11516 `0e722e8` crypto: Add test cases covering the relevant HMAC-SHA{256,512} key length boundaries (practicalswift) +- #10574 `5d132e8` Remove includes in .cpp files for things the corresponding .h file already included (practicalswift) +- #11884 `66479c0` Remove unused include in hash.cpp (kallewoof) +- #10839 `c66adb2` Don't use pass by reference to const for cheaply-copied types (bool, char, etc.) (practicalswift) +- #10657 `79399c8` Utils: Improvements to ECDSA key-handling code (str4d) +- #12250 `e37ca2b` Make CKey::Load references const (ryanofsky) +- #12108 `9220426` Remove unused fQuit var from checkqueue.h (donaloconnor) +- #12159 `f3c7062` Use the character based overload for std::string::find (kekimusmaximus) +- #12266 `3448907` Move scheduler/threadGroup into common-init instead of per-app (TheBlueMatt) + +### Miscellaneous +- #11246 `777519b` github-merge: Coalesce git fetches (laanwj) +- #10871 `c9a4aa8` Handle getinfo in bitcoin-cli w/ -getinfo (revival of #8843) (achow101) +- #11419 `093074b` Utils: Fix launchctl not being able to stop bitcoind (OmeGak) +- #11394 `6e4e98e` Perform a weaker subtree check in Travis (sipa) +- #11702 `4122112` [build] Add a script for installing db4 (jamesob) +- #11794 `dd49862` Prefix leveldb debug logging (laanwj) +- #11781 `24df9af` Add `-debuglogfile` option (laanwj) +- #10773 `c17f11f` Shell script cleanups (practicalswift) +- #11829 `7630a1f` Test datadir specified in conf file exists (MeshCollider) +- #11836 `d44535d` Rename rpcuser.py to rpcauth.py (hkjn) +- #11831 `d48ab83` Always return true if AppInitMain got to the end (TheBlueMatt) +- #11943 `1808660` contrib: fix typo in install_db4.sh help message (laanwj) +- #12075 `c991b30` [scripts] Add missing univalue file to copyright_header.py (fanquake) +- #12197 `000ac4f` Log debug build status and warn when running benchmarks (laanwj) +- #10672 `6ab0e4c` Avoid division by zero in the case of a corrupt estimates file (practicalswift) +- #11273 `cdd6bbf` Ignore old format estimation file (Xekyo) +- #11951 `1fb34e0` Remove dead feeest-file read code for old versions (TheBlueMatt) +- #11421 `9ccafb1` Merge current secp256k1 subtree (MarcoFalke) +- #11573 `2631d55` [Util] Update tinyformat.h (fanquake) +- #10529 `331352f` Improve bitcoind systemd service file (Flowdalic) +- #11620 `70fec9e` [build] .gitignore: add background.tiff (Sjors) +- #11558 `68e021e` Minimal code changes to allow msvc compilation (sipsorcery) +- #11284 `10bee0d` Fix invalid memory access in CScript::operator+= (guidovranken, ajtowns) +- #10939 `a1f7f18` [init] Check non-emptiness of -blocknotify command prior to executing (practicalswift) +- #11467 `937613d` Fix typos. Use nullptr instead of NULL (practicalswift) +- #11834 `5bea05b` [verify-commits] Fix gpg.sh's echoing for commits with '\n' (TheBlueMatt) +- #11830 `a13e443` rpcuser.py: Use 'python' not 'python2' (hkjn) +- #12194 `7abb0f0` Add change type option to fundrawtransaction (promag) +- #12269 `2ae7cf8` Update defaultAssumeValid to block 506067 (gmaxwell) +- #11952 `9ab9963` univalue: Bump subtree (MarcoFalke) +- #12367 `09fc859` Fix two fast-shutdown bugs (TheBlueMatt) +- #12422 `4d54e7a` util: Make LockDirectory thread-safe, consistent, and fix OpenBSD 6.2 build (laanwj) + +Credits +======= + +Thanks to everyone who directly contributed to this release: + +- 251 +- Aaron Clauson +- Aaron Golliver +- aaron-hanson +- Adam Langley +- Akio Nakamura +- Akira Takizawa +- Alejandro Avilés +- Alex Morcos +- Alin Rus +- Anditto Heristyo +- Andras Elso +- Andreas Schildbach +- Andrew Chow +- Anthony Towns +- azuchi +- Carl Dong +- Chris Moore +- Chris Stewart +- Christian Gentry +- Cory Fields +- Cristian Mircea Messel +- CryptAxe +- Dan Raviv +- Daniel Edgecumbe +- danra +- david60 +- Donal O'Connor +- dongsamb +- Dusty Williams +- Eelis +- esneider +- Evan Klitzke +- fanquake +- Ferdinando M. Ametrano +- fivepiece +- flack +- Florian Schmaus +- gnuser +- Gregory Maxwell +- Gregory Sanders +- Henrik Jonsson +- Jack Grigg +- Jacky C +- James Evans +- James O'Beirne +- Jan Sarenik +- Jeff Rade +- Jeremiah Buddenhagen +- Jeremy Rubin +- Jim Posen +- jjz +- Joe Harvell +- Johannes Kanig +- John Newbery +- Johnson Lau +- Jonas Nick +- Jonas Schnelli +- João Barbosa +- Jorge Timón +- Karel BÃlek +- Karl-Johan Alm +- klemens +- Kyuntae Ethan Kim +- laudaa +- Lawrence Nahum +- Lucas Betschart +- Luke Dashjr +- Luke Mlsna +- MarcoFalke +- Mark Friedenbach +- Marko Bencun +- Martin Ankerl +- Matt Corallo +- mruddy +- Murch +- NicolasDorier +- Pablo Fernandez +- Paul Berg +- Pedro Branco +- Pierre Rochard +- Pieter Wuille +- practicalswift +- Randolf Richardson +- Russell Yanofsky +- Samuel Dobson +- Sean Erle Johnson +- Shooter +- Sjors Provoost +- Suhas Daftuar +- Thomas Snider +- Thoragh +- Tim Shimmin +- Tomas van der Wansem +- Utsav Gupta +- Varunram Ganesh +- Vivek Ganesan +- Werner Lemberg +- William Casarin +- Willy Ko +- Wladimir J. van der Laan + +As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/). diff --git a/doc/release-process.md b/doc/release-process.md index 430a5a7ed3..a988c74ba5 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -63,7 +63,7 @@ If you're using the automated script (found in [contrib/gitian-build.sh](/contri Setup Gitian descriptors: pushd ./bitcoin - export SIGNER=(your Gitian key, ie bluematt, sipa, etc) + export SIGNER="(your Gitian key, ie bluematt, sipa, etc)" export VERSION=(new version, e.g. 0.8.0) git fetch git checkout v${VERSION} @@ -93,7 +93,9 @@ Create the OS X SDK tarball, see the [OS X readme](README_osx.md) for details, a ### Optional: Seed the Gitian sources cache and offline git repositories -By default, Gitian will fetch source files as needed. To cache them ahead of time: +NOTE: Gitian is sometimes unable to download files. If you have errors, try the step below. + +By default, Gitian will fetch source files as needed. To cache them ahead of time, make sure you have checked out the tag you want to build in bitcoin, then: pushd ./gitian-builder make -C ../bitcoin/depends download SOURCES_PATH=`pwd`/cache/common @@ -113,16 +115,16 @@ The gbuild invocations below <b>DO NOT DO THIS</b> by default. pushd ./gitian-builder ./bin/gbuild --num-make 2 --memory 3000 --commit bitcoin=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml - ./bin/gsign --signer $SIGNER --release ${VERSION}-linux --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml + ./bin/gsign --signer "$SIGNER" --release ${VERSION}-linux --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml mv build/out/bitcoin-*.tar.gz build/out/src/bitcoin-*.tar.gz ../ ./bin/gbuild --num-make 2 --memory 3000 --commit bitcoin=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-win.yml - ./bin/gsign --signer $SIGNER --release ${VERSION}-win-unsigned --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win.yml + ./bin/gsign --signer "$SIGNER" --release ${VERSION}-win-unsigned --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win.yml mv build/out/bitcoin-*-win-unsigned.tar.gz inputs/bitcoin-win-unsigned.tar.gz mv build/out/bitcoin-*.zip build/out/bitcoin-*.exe ../ ./bin/gbuild --num-make 2 --memory 3000 --commit bitcoin=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml - ./bin/gsign --signer $SIGNER --release ${VERSION}-osx-unsigned --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml + ./bin/gsign --signer "$SIGNER" --release ${VERSION}-osx-unsigned --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml mv build/out/bitcoin-*-osx-unsigned.tar.gz inputs/bitcoin-osx-unsigned.tar.gz mv build/out/bitcoin-*.tar.gz build/out/bitcoin-*.dmg ../ popd @@ -152,9 +154,9 @@ Verify the signatures Commit your signature to gitian.sigs: pushd gitian.sigs - git add ${VERSION}-linux/${SIGNER} - git add ${VERSION}-win-unsigned/${SIGNER} - git add ${VERSION}-osx-unsigned/${SIGNER} + git add ${VERSION}-linux/"${SIGNER}" + git add ${VERSION}-win-unsigned/"${SIGNER}" + git add ${VERSION}-osx-unsigned/"${SIGNER}" git commit -a git push # Assuming you can push to the gitian.sigs tree popd @@ -199,7 +201,7 @@ Create (and optionally verify) the signed OS X binary: pushd ./gitian-builder ./bin/gbuild -i --commit signature=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml - ./bin/gsign --signer $SIGNER --release ${VERSION}-osx-signed --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml + ./bin/gsign --signer "$SIGNER" --release ${VERSION}-osx-signed --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-osx-signed ../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml mv build/out/bitcoin-osx-signed.dmg ../bitcoin-${VERSION}-osx.dmg popd @@ -208,7 +210,7 @@ Create (and optionally verify) the signed Windows binaries: pushd ./gitian-builder ./bin/gbuild -i --commit signature=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml - ./bin/gsign --signer $SIGNER --release ${VERSION}-win-signed --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml + ./bin/gsign --signer "$SIGNER" --release ${VERSION}-win-signed --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-win-signed ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml mv build/out/bitcoin-*win64-setup.exe ../bitcoin-${VERSION}-win64-setup.exe mv build/out/bitcoin-*win32-setup.exe ../bitcoin-${VERSION}-win32-setup.exe @@ -217,8 +219,8 @@ Create (and optionally verify) the signed Windows binaries: Commit your signature for the signed OS X/Windows binaries: pushd gitian.sigs - git add ${VERSION}-osx-signed/${SIGNER} - git add ${VERSION}-win-signed/${SIGNER} + git add ${VERSION}-osx-signed/"${SIGNER}" + git add ${VERSION}-win-signed/"${SIGNER}" git commit -a git push # Assuming you can push to the gitian.sigs tree popd diff --git a/doc/translation_process.md b/doc/translation_process.md index 1702637d53..5a9c59914e 100644 --- a/doc/translation_process.md +++ b/doc/translation_process.md @@ -52,7 +52,7 @@ The client it used to fetch updated translations. If you are having problems, or `pip install transifex-client` -Setup your transifex client config as follows. Please *ignore the token field*. +Setup your Transifex client config as follows. Please *ignore the token field*. ```ini nano ~/.transifexrc diff --git a/doc/zmq.md b/doc/zmq.md index 38c58fb7fd..5d67df9d22 100644 --- a/doc/zmq.md +++ b/doc/zmq.md @@ -101,6 +101,6 @@ and just the tip will be notified. It is up to the subscriber to retrieve the chain from the last known block to the new tip. There are several possibilities that ZMQ notification can get lost -during transmission depending on the communication type your are +during transmission depending on the communication type you are using. Bitcoind appends an up-counting sequence number to each notification which allows listeners to detect lost notifications. diff --git a/src/Makefile.am b/src/Makefile.am index 4fbd605d9e..0a9370c85c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,8 +4,8 @@ DIST_SUBDIRS = secp256k1 univalue -AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) -AM_CXXFLAGS = $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) +AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(GPROF_LDFLAGS) +AM_CXXFLAGS = $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) $(GPROF_CXXFLAGS) AM_CPPFLAGS = $(HARDENED_CPPFLAGS) EXTRA_LIBRARIES = @@ -105,6 +105,7 @@ BITCOIN_CORE_H = \ indirectmap.h \ init.h \ key.h \ + key_io.h \ keystore.h \ dbwrapper.h \ limitedmap.h \ @@ -132,13 +133,14 @@ BITCOIN_CORE_H = \ rpc/protocol.h \ rpc/safemode.h \ rpc/server.h \ + rpc/rawtransaction.h \ rpc/register.h \ rpc/util.h \ scheduler.h \ + script/ismine.h \ script/sigcache.h \ script/sign.h \ script/standard.h \ - script/ismine.h \ streams.h \ support/allocators/secure.h \ support/allocators/zeroafterfree.h \ @@ -216,7 +218,6 @@ libbitcoin_server_a_SOURCES = \ rpc/safemode.cpp \ rpc/server.cpp \ script/sigcache.cpp \ - script/ismine.cpp \ timedata.cpp \ torcontrol.cpp \ txdb.cpp \ @@ -327,12 +328,14 @@ libbitcoin_common_a_SOURCES = \ core_read.cpp \ core_write.cpp \ key.cpp \ + key_io.cpp \ keystore.cpp \ netaddress.cpp \ netbase.cpp \ policy/feerate.cpp \ protocol.cpp \ scheduler.cpp \ + script/ismine.cpp \ script/sign.cpp \ script/standard.cpp \ warnings.cpp \ @@ -389,10 +392,10 @@ endif bitcoind_LDADD = \ $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_WALLET) \ $(LIBBITCOIN_COMMON) \ $(LIBUNIVALUE) \ $(LIBBITCOIN_UTIL) \ - $(LIBBITCOIN_WALLET) \ $(LIBBITCOIN_ZMQ) \ $(LIBBITCOIN_CONSENSUS) \ $(LIBBITCOIN_CRYPTO) \ diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index 8e2e587d32..748c5b7887 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -27,7 +27,7 @@ bench_bench_bitcoin_SOURCES = \ bench/lockedpool.cpp \ bench/perf.cpp \ bench/perf.h \ - bench/prevector_destructor.cpp + bench/prevector.cpp nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_BENCH_FILES) @@ -35,6 +35,7 @@ bench_bench_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CLFAGS bench_bench_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) bench_bench_bitcoin_LDADD = \ $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_WALLET) \ $(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CONSENSUS) \ @@ -51,7 +52,6 @@ endif if ENABLE_WALLET bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp -bench_bench_bitcoin_LDADD += $(LIBBITCOIN_WALLET) $(LIBBITCOIN_CRYPTO) endif bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index d4d972b2bb..4ee9102519 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -9,13 +9,13 @@ TEST_SRCDIR = test TEST_BINARY=test/test_bitcoin$(EXEEXT) JSON_TEST_FILES = \ - test/data/script_tests.json \ - test/data/base58_keys_valid.json \ test/data/base58_encode_decode.json \ - test/data/base58_keys_invalid.json \ + test/data/key_io_valid.json \ + test/data/key_io_invalid.json \ + test/data/script_tests.json \ + test/data/sighash.json \ test/data/tx_invalid.json \ - test/data/tx_valid.json \ - test/data/sighash.json + test/data/tx_valid.json RAW_TEST_FILES = @@ -45,6 +45,7 @@ BITCOIN_TESTS =\ test/DoS_tests.cpp \ test/getarg_tests.cpp \ test/hash_tests.cpp \ + test/key_io_tests.cpp \ test/key_tests.cpp \ test/limitedmap_tests.cpp \ test/dbwrapper_tests.cpp \ diff --git a/src/addrman.cpp b/src/addrman.cpp index 9eefffb45b..e811dd4bea 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -187,7 +187,7 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId) info.fInTried = true; } -void CAddrMan::Good_(const CService& addr, int64_t nTime) +void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime) { int nId; @@ -233,10 +233,22 @@ void CAddrMan::Good_(const CService& addr, int64_t nTime) if (nUBucket == -1) return; - LogPrint(BCLog::ADDRMAN, "Moving %s to tried\n", addr.ToString()); + // which tried bucket to move the entry to + int tried_bucket = info.GetTriedBucket(nKey); + int tried_bucket_pos = info.GetBucketPosition(nKey, false, tried_bucket); + + // Will moving this address into tried evict another entry? + if (test_before_evict && (vvTried[tried_bucket][tried_bucket_pos] != -1)) { + LogPrint(BCLog::ADDRMAN, "Collision inserting element into tried table, moving %s to m_tried_collisions=%d\n", addr.ToString(), m_tried_collisions.size()); + if (m_tried_collisions.size() < ADDRMAN_SET_TRIED_COLLISION_SIZE) { + m_tried_collisions.insert(nId); + } + } else { + LogPrint(BCLog::ADDRMAN, "Moving %s to tried\n", addr.ToString()); - // move nId to the tried tables - MakeTried(info, nId); + // move nId to the tried tables + MakeTried(info, nId); + } } bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty) @@ -521,3 +533,82 @@ void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices) int CAddrMan::RandomInt(int nMax){ return GetRandInt(nMax); } + +void CAddrMan::ResolveCollisions_() +{ + for (std::set<int>::iterator it = m_tried_collisions.begin(); it != m_tried_collisions.end();) { + int id_new = *it; + + bool erase_collision = false; + + // If id_new not found in mapInfo remove it from m_tried_collisions + if (mapInfo.count(id_new) != 1) { + erase_collision = true; + } else { + CAddrInfo& info_new = mapInfo[id_new]; + + // Which tried bucket to move the entry to. + int tried_bucket = info_new.GetTriedBucket(nKey); + int tried_bucket_pos = info_new.GetBucketPosition(nKey, false, tried_bucket); + if (!info_new.IsValid()) { // id_new may no longer map to a valid address + erase_collision = true; + } else if (vvTried[tried_bucket][tried_bucket_pos] != -1) { // The position in the tried bucket is not empty + + // Get the to-be-evicted address that is being tested + int id_old = vvTried[tried_bucket][tried_bucket_pos]; + CAddrInfo& info_old = mapInfo[id_old]; + + // Has successfully connected in last X hours + if (GetAdjustedTime() - info_old.nLastSuccess < ADDRMAN_REPLACEMENT_HOURS*(60*60)) { + erase_collision = true; + } else if (GetAdjustedTime() - info_old.nLastTry < ADDRMAN_REPLACEMENT_HOURS*(60*60)) { // attempted to connect and failed in last X hours + + // Give address at least 60 seconds to successfully connect + if (GetAdjustedTime() - info_old.nLastTry > 60) { + LogPrint(BCLog::ADDRMAN, "Swapping %s for %s in tried table\n", info_new.ToString(), info_old.ToString()); + + // Replaces an existing address already in the tried table with the new address + Good_(info_new, false, GetAdjustedTime()); + erase_collision = true; + } + } + } else { // Collision is not actually a collision anymore + Good_(info_new, false, GetAdjustedTime()); + erase_collision = true; + } + } + + if (erase_collision) { + m_tried_collisions.erase(it++); + } else { + it++; + } + } +} + +CAddrInfo CAddrMan::SelectTriedCollision_() +{ + if (m_tried_collisions.size() == 0) return CAddrInfo(); + + std::set<int>::iterator it = m_tried_collisions.begin(); + + // Selects a random element from m_tried_collisions + std::advance(it, GetRandInt(m_tried_collisions.size())); + int id_new = *it; + + // If id_new not found in mapInfo remove it from m_tried_collisions + if (mapInfo.count(id_new) != 1) { + m_tried_collisions.erase(it); + return CAddrInfo(); + } + + CAddrInfo& newInfo = mapInfo[id_new]; + + // which tried bucket to move the entry to + int tried_bucket = newInfo.GetTriedBucket(nKey); + int tried_bucket_pos = newInfo.GetBucketPosition(nKey, false, tried_bucket); + + int id_old = vvTried[tried_bucket][tried_bucket_pos]; + + return mapInfo[id_old]; +} diff --git a/src/addrman.h b/src/addrman.h index 38da754afb..67423c6c55 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -165,6 +165,9 @@ public: //! ... in at least this many days #define ADDRMAN_MIN_FAIL_DAYS 7 +//! how recent a successful connection should be before we allow an address to be evicted from tried +#define ADDRMAN_REPLACEMENT_HOURS 4 + //! the maximum percentage of nodes to return in a getaddr call #define ADDRMAN_GETADDR_MAX_PCT 23 @@ -176,6 +179,9 @@ public: #define ADDRMAN_NEW_BUCKET_COUNT (1 << ADDRMAN_NEW_BUCKET_COUNT_LOG2) #define ADDRMAN_BUCKET_SIZE (1 << ADDRMAN_BUCKET_SIZE_LOG2) +//! the maximum number of tried addr collisions to store +#define ADDRMAN_SET_TRIED_COLLISION_SIZE 10 + /** * Stochastical (IP) address manager */ @@ -212,6 +218,9 @@ private: //! last time Good was called (memory only) int64_t nLastGood; + //! Holds addrs inserted into tried table that collide with existing entries. Test-before-evict discpline used to resolve these collisions. + std::set<int> m_tried_collisions; + protected: //! secret key to randomize bucket select with uint256 nKey; @@ -239,7 +248,7 @@ protected: void ClearNew(int nUBucket, int nUBucketPos); //! Mark an entry "good", possibly moving it from "new" to "tried". - void Good_(const CService &addr, int64_t nTime); + void Good_(const CService &addr, bool test_before_evict, int64_t time); //! Add an entry to the "new" table. bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty); @@ -250,6 +259,12 @@ protected: //! Select an address to connect to, if newOnly is set to true, only the new table is selected from. CAddrInfo Select_(bool newOnly); + //! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions. + void ResolveCollisions_(); + + //! Return a random to-be-evicted tried table address. + CAddrInfo SelectTriedCollision_(); + //! Wraps GetRandInt to allow tests to override RandomInt and make it determinismistic. virtual int RandomInt(int nMax); @@ -537,11 +552,11 @@ public: } //! Mark an entry as accessible. - void Good(const CService &addr, int64_t nTime = GetAdjustedTime()) + void Good(const CService &addr, bool test_before_evict = true, int64_t nTime = GetAdjustedTime()) { LOCK(cs); Check(); - Good_(addr, nTime); + Good_(addr, test_before_evict, nTime); Check(); } @@ -554,6 +569,28 @@ public: Check(); } + //! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions. + void ResolveCollisions() + { + LOCK(cs); + Check(); + ResolveCollisions_(); + Check(); + } + + //! Randomly select an address in tried that another address is attempting to evict. + CAddrInfo SelectTriedCollision() + { + CAddrInfo ret; + { + LOCK(cs); + Check(); + ret = SelectTriedCollision_(); + Check(); + } + return ret; + } + /** * Choose an address to connect to. */ diff --git a/src/arith_uint256.h b/src/arith_uint256.h index dc2627592e..3f4cc8c2bf 100644 --- a/src/arith_uint256.h +++ b/src/arith_uint256.h @@ -85,7 +85,7 @@ public: base_uint ret; for (int i = 0; i < WIDTH; i++) ret.pn[i] = ~pn[i]; - ret++; + ++ret; return ret; } diff --git a/src/base58.cpp b/src/base58.cpp index 499afbe382..982e123a1d 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -4,20 +4,12 @@ #include <base58.h> -#include <bech32.h> #include <hash.h> -#include <script/script.h> #include <uint256.h> -#include <utilstrencodings.h> -#include <boost/variant/apply_visitor.hpp> -#include <boost/variant/static_visitor.hpp> - -#include <algorithm> #include <assert.h> #include <string.h> - /** All alphanumeric characters except for "0", "I", "O", and "l" */ static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; @@ -151,227 +143,3 @@ bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRe { return DecodeBase58Check(str.c_str(), vchRet); } - -CBase58Data::CBase58Data() -{ - vchVersion.clear(); - vchData.clear(); -} - -void CBase58Data::SetData(const std::vector<unsigned char>& vchVersionIn, const void* pdata, size_t nSize) -{ - vchVersion = vchVersionIn; - vchData.resize(nSize); - if (!vchData.empty()) - memcpy(vchData.data(), pdata, nSize); -} - -void CBase58Data::SetData(const std::vector<unsigned char>& vchVersionIn, const unsigned char* pbegin, const unsigned char* pend) -{ - SetData(vchVersionIn, (void*)pbegin, pend - pbegin); -} - -bool CBase58Data::SetString(const char* psz, unsigned int nVersionBytes) -{ - std::vector<unsigned char> vchTemp; - bool rc58 = DecodeBase58Check(psz, vchTemp); - if ((!rc58) || (vchTemp.size() < nVersionBytes)) { - vchData.clear(); - vchVersion.clear(); - return false; - } - vchVersion.assign(vchTemp.begin(), vchTemp.begin() + nVersionBytes); - vchData.resize(vchTemp.size() - nVersionBytes); - if (!vchData.empty()) - memcpy(vchData.data(), vchTemp.data() + nVersionBytes, vchData.size()); - memory_cleanse(vchTemp.data(), vchTemp.size()); - return true; -} - -bool CBase58Data::SetString(const std::string& str) -{ - return SetString(str.c_str()); -} - -std::string CBase58Data::ToString() const -{ - std::vector<unsigned char> vch = vchVersion; - vch.insert(vch.end(), vchData.begin(), vchData.end()); - return EncodeBase58Check(vch); -} - -int CBase58Data::CompareTo(const CBase58Data& b58) const -{ - if (vchVersion < b58.vchVersion) - return -1; - if (vchVersion > b58.vchVersion) - return 1; - if (vchData < b58.vchData) - return -1; - if (vchData > b58.vchData) - return 1; - return 0; -} - -namespace -{ -class DestinationEncoder : public boost::static_visitor<std::string> -{ -private: - const CChainParams& m_params; - -public: - DestinationEncoder(const CChainParams& params) : m_params(params) {} - - std::string operator()(const CKeyID& id) const - { - std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); - data.insert(data.end(), id.begin(), id.end()); - return EncodeBase58Check(data); - } - - std::string operator()(const CScriptID& id) const - { - std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); - data.insert(data.end(), id.begin(), id.end()); - return EncodeBase58Check(data); - } - - std::string operator()(const WitnessV0KeyHash& id) const - { - std::vector<unsigned char> data = {0}; - ConvertBits<8, 5, true>(data, id.begin(), id.end()); - return bech32::Encode(m_params.Bech32HRP(), data); - } - - std::string operator()(const WitnessV0ScriptHash& id) const - { - std::vector<unsigned char> data = {0}; - ConvertBits<8, 5, true>(data, id.begin(), id.end()); - return bech32::Encode(m_params.Bech32HRP(), data); - } - - std::string operator()(const WitnessUnknown& id) const - { - if (id.version < 1 || id.version > 16 || id.length < 2 || id.length > 40) { - return {}; - } - std::vector<unsigned char> data = {(unsigned char)id.version}; - ConvertBits<8, 5, true>(data, id.program, id.program + id.length); - return bech32::Encode(m_params.Bech32HRP(), data); - } - - std::string operator()(const CNoDestination& no) const { return {}; } -}; - -CTxDestination DecodeDestination(const std::string& str, const CChainParams& params) -{ - std::vector<unsigned char> data; - uint160 hash; - if (DecodeBase58Check(str, data)) { - // base58-encoded Bitcoin addresses. - // Public-key-hash-addresses have version 0 (or 111 testnet). - // The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. - const std::vector<unsigned char>& pubkey_prefix = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); - if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) { - std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin()); - return CKeyID(hash); - } - // Script-hash-addresses have version 5 (or 196 testnet). - // The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script. - const std::vector<unsigned char>& script_prefix = params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); - if (data.size() == hash.size() + script_prefix.size() && std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) { - std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin()); - return CScriptID(hash); - } - } - data.clear(); - auto bech = bech32::Decode(str); - if (bech.second.size() > 0 && bech.first == params.Bech32HRP()) { - // Bech32 decoding - int version = bech.second[0]; // The first 5 bit symbol is the witness version (0-16) - // The rest of the symbols are converted witness program bytes. - if (ConvertBits<5, 8, false>(data, bech.second.begin() + 1, bech.second.end())) { - if (version == 0) { - { - WitnessV0KeyHash keyid; - if (data.size() == keyid.size()) { - std::copy(data.begin(), data.end(), keyid.begin()); - return keyid; - } - } - { - WitnessV0ScriptHash scriptid; - if (data.size() == scriptid.size()) { - std::copy(data.begin(), data.end(), scriptid.begin()); - return scriptid; - } - } - return CNoDestination(); - } - if (version > 16 || data.size() < 2 || data.size() > 40) { - return CNoDestination(); - } - WitnessUnknown unk; - unk.version = version; - std::copy(data.begin(), data.end(), unk.program); - unk.length = data.size(); - return unk; - } - } - return CNoDestination(); -} -} // namespace - -void CBitcoinSecret::SetKey(const CKey& vchSecret) -{ - assert(vchSecret.IsValid()); - SetData(Params().Base58Prefix(CChainParams::SECRET_KEY), vchSecret.begin(), vchSecret.size()); - if (vchSecret.IsCompressed()) - vchData.push_back(1); -} - -CKey CBitcoinSecret::GetKey() -{ - CKey ret; - assert(vchData.size() >= 32); - ret.Set(vchData.begin(), vchData.begin() + 32, vchData.size() > 32 && vchData[32] == 1); - return ret; -} - -bool CBitcoinSecret::IsValid() const -{ - bool fExpectedFormat = vchData.size() == 32 || (vchData.size() == 33 && vchData[32] == 1); - bool fCorrectVersion = vchVersion == Params().Base58Prefix(CChainParams::SECRET_KEY); - return fExpectedFormat && fCorrectVersion; -} - -bool CBitcoinSecret::SetString(const char* pszSecret) -{ - return CBase58Data::SetString(pszSecret) && IsValid(); -} - -bool CBitcoinSecret::SetString(const std::string& strSecret) -{ - return SetString(strSecret.c_str()); -} - -std::string EncodeDestination(const CTxDestination& dest) -{ - return boost::apply_visitor(DestinationEncoder(Params()), dest); -} - -CTxDestination DecodeDestination(const std::string& str) -{ - return DecodeDestination(str, Params()); -} - -bool IsValidDestinationString(const std::string& str, const CChainParams& params) -{ - return IsValidDestination(DecodeDestination(str, params)); -} - -bool IsValidDestinationString(const std::string& str) -{ - return IsValidDestinationString(str, Params()); -} diff --git a/src/base58.h b/src/base58.h index 39eb4eacc2..8f2833bec9 100644 --- a/src/base58.h +++ b/src/base58.h @@ -14,12 +14,6 @@ #ifndef BITCOIN_BASE58_H #define BITCOIN_BASE58_H -#include <chainparams.h> -#include <key.h> -#include <pubkey.h> -#include <script/standard.h> -#include <support/allocators/zeroafterfree.h> - #include <string> #include <vector> @@ -56,95 +50,12 @@ std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn); * Decode a base58-encoded string (psz) that includes a checksum into a byte * vector (vchRet), return true if decoding is successful */ -inline bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet); +bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet); /** * Decode a base58-encoded string (str) that includes a checksum into a byte * vector (vchRet), return true if decoding is successful */ -inline bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet); - -/** - * Base class for all base58-encoded data - */ -class CBase58Data -{ -protected: - //! the version byte(s) - std::vector<unsigned char> vchVersion; - - //! the actually encoded data - typedef std::vector<unsigned char, zero_after_free_allocator<unsigned char> > vector_uchar; - vector_uchar vchData; - - CBase58Data(); - void SetData(const std::vector<unsigned char> &vchVersionIn, const void* pdata, size_t nSize); - void SetData(const std::vector<unsigned char> &vchVersionIn, const unsigned char *pbegin, const unsigned char *pend); - -public: - bool SetString(const char* psz, unsigned int nVersionBytes = 1); - bool SetString(const std::string& str); - std::string ToString() const; - int CompareTo(const CBase58Data& b58) const; - - bool operator==(const CBase58Data& b58) const { return CompareTo(b58) == 0; } - bool operator<=(const CBase58Data& b58) const { return CompareTo(b58) <= 0; } - bool operator>=(const CBase58Data& b58) const { return CompareTo(b58) >= 0; } - bool operator< (const CBase58Data& b58) const { return CompareTo(b58) < 0; } - bool operator> (const CBase58Data& b58) const { return CompareTo(b58) > 0; } -}; - -/** - * A base58-encoded secret key - */ -class CBitcoinSecret : public CBase58Data -{ -public: - void SetKey(const CKey& vchSecret); - CKey GetKey(); - bool IsValid() const; - bool SetString(const char* pszSecret); - bool SetString(const std::string& strSecret); - - CBitcoinSecret(const CKey& vchSecret) { SetKey(vchSecret); } - CBitcoinSecret() {} -}; - -template<typename K, int Size, CChainParams::Base58Type Type> class CBitcoinExtKeyBase : public CBase58Data -{ -public: - void SetKey(const K &key) { - unsigned char vch[Size]; - key.Encode(vch); - SetData(Params().Base58Prefix(Type), vch, vch+Size); - } - - K GetKey() { - K ret; - if (vchData.size() == Size) { - // If base58 encoded data does not hold an ext key, return a !IsValid() key - ret.Decode(vchData.data()); - } - return ret; - } - - CBitcoinExtKeyBase(const K &key) { - SetKey(key); - } - - CBitcoinExtKeyBase(const std::string& strBase58c) { - SetString(strBase58c.c_str(), Params().Base58Prefix(Type).size()); - } - - CBitcoinExtKeyBase() {} -}; - -typedef CBitcoinExtKeyBase<CExtKey, BIP32_EXTKEY_SIZE, CChainParams::EXT_SECRET_KEY> CBitcoinExtKey; -typedef CBitcoinExtKeyBase<CExtPubKey, BIP32_EXTKEY_SIZE, CChainParams::EXT_PUBLIC_KEY> CBitcoinExtPubKey; - -std::string EncodeDestination(const CTxDestination& dest); -CTxDestination DecodeDestination(const std::string& str); -bool IsValidDestinationString(const std::string& str); -bool IsValidDestinationString(const std::string& str, const CChainParams& params); +bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet); #endif // BITCOIN_BASE58_H diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp index 06d2abeac6..98965840c7 100644 --- a/src/bench/coin_selection.cpp +++ b/src/bench/coin_selection.cpp @@ -32,16 +32,11 @@ static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<CO // (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484) static void CoinSelection(benchmark::State& state) { - const CWallet wallet; + const CWallet wallet("dummy", CWalletDBWrapper::CreateDummy()); std::vector<COutput> vCoins; LOCK(wallet.cs_wallet); while (state.KeepRunning()) { - // Empty wallet. - for (COutput output : vCoins) - delete output.tx; - vCoins.clear(); - // Add coins. for (int i = 0; i < 1000; i++) addCoin(1000 * COIN, wallet, vCoins); @@ -53,6 +48,12 @@ static void CoinSelection(benchmark::State& state) assert(success); assert(nValueRet == 1003 * COIN); assert(setCoinsRet.size() == 2); + + // Empty wallet. + for (COutput& output : vCoins) { + delete output.tx; + } + vCoins.clear(); } } diff --git a/src/bench/prevector.cpp b/src/bench/prevector.cpp new file mode 100644 index 0000000000..d0f28d1a3e --- /dev/null +++ b/src/bench/prevector.cpp @@ -0,0 +1,77 @@ +// Copyright (c) 2015-2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <compat.h> +#include <prevector.h> + +#include <bench/bench.h> + +struct nontrivial_t { + int x; + nontrivial_t() :x(-1) {} +}; +static_assert(!IS_TRIVIALLY_CONSTRUCTIBLE<nontrivial_t>::value, + "expected nontrivial_t to not be trivially constructible"); + +typedef unsigned char trivial_t; +static_assert(IS_TRIVIALLY_CONSTRUCTIBLE<trivial_t>::value, + "expected trivial_t to be trivially constructible"); + +template <typename T> +static void PrevectorDestructor(benchmark::State& state) +{ + while (state.KeepRunning()) { + for (auto x = 0; x < 1000; ++x) { + prevector<28, T> t0; + prevector<28, T> t1; + t0.resize(28); + t1.resize(29); + } + } +} + +template <typename T> +static void PrevectorClear(benchmark::State& state) +{ + + while (state.KeepRunning()) { + for (auto x = 0; x < 1000; ++x) { + prevector<28, T> t0; + prevector<28, T> t1; + t0.resize(28); + t0.clear(); + t1.resize(29); + t0.clear(); + } + } +} + +template <typename T> +void PrevectorResize(benchmark::State& state) +{ + while (state.KeepRunning()) { + prevector<28, T> t0; + prevector<28, T> t1; + for (auto x = 0; x < 1000; ++x) { + t0.resize(28); + t0.resize(0); + t1.resize(29); + t1.resize(0); + } + } +} + +#define PREVECTOR_TEST(name, nontrivops, trivops) \ + static void Prevector ## name ## Nontrivial(benchmark::State& state) { \ + PrevectorResize<nontrivial_t>(state); \ + } \ + BENCHMARK(Prevector ## name ## Nontrivial, nontrivops); \ + static void Prevector ## name ## Trivial(benchmark::State& state) { \ + PrevectorResize<trivial_t>(state); \ + } \ + BENCHMARK(Prevector ## name ## Trivial, trivops); + +PREVECTOR_TEST(Clear, 28300, 88600) +PREVECTOR_TEST(Destructor, 28800, 88900) +PREVECTOR_TEST(Resize, 28900, 90300) diff --git a/src/bench/prevector_destructor.cpp b/src/bench/prevector_destructor.cpp deleted file mode 100644 index 39d0ee5eb1..0000000000 --- a/src/bench/prevector_destructor.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2015-2017 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include <bench/bench.h> -#include <prevector.h> - -static void PrevectorDestructor(benchmark::State& state) -{ - while (state.KeepRunning()) { - for (auto x = 0; x < 1000; ++x) { - prevector<28, unsigned char> t0; - prevector<28, unsigned char> t1; - t0.resize(28); - t1.resize(29); - } - } -} - -static void PrevectorClear(benchmark::State& state) -{ - - while (state.KeepRunning()) { - for (auto x = 0; x < 1000; ++x) { - prevector<28, unsigned char> t0; - prevector<28, unsigned char> t1; - t0.resize(28); - t0.clear(); - t1.resize(29); - t0.clear(); - } - } -} - -BENCHMARK(PrevectorDestructor, 5700); -BENCHMARK(PrevectorClear, 5600); diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index a60d3b3b6d..41f1e5786c 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -46,7 +46,7 @@ std::string HelpMessageCli() strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort())); strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections")); strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start")); - strUsage += HelpMessageOpt("-rpcwallet=<walletname>", _("Send RPC for non-default wallet on RPC server (argument is wallet filename in bitcoind directory, required if bitcoind/-Qt runs with multiple wallets)")); + strUsage += HelpMessageOpt("-rpcwallet=<walletname>", _("Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind)")); strUsage += HelpMessageOpt("-stdin", _("Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password.")); strUsage += HelpMessageOpt("-stdinrpcpass", strprintf(_("Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password."))); @@ -339,8 +339,8 @@ static UniValue CallRPC(BaseRequestHandler *rh, const std::string& strMethod, co // check if we should use a special wallet endpoint std::string endpoint = "/"; - std::string walletName = gArgs.GetArg("-rpcwallet", ""); - if (!walletName.empty()) { + if (!gArgs.GetArgs("-rpcwallet").empty()) { + std::string walletName = gArgs.GetArg("-rpcwallet", ""); char *encodedURI = evhttp_uriencode(walletName.c_str(), walletName.size(), false); if (encodedURI) { endpoint = "/wallet/"+ std::string(encodedURI); diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index a9f7264f68..8218e883a6 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -6,11 +6,11 @@ #include <config/bitcoin-config.h> #endif -#include <base58.h> #include <clientversion.h> #include <coins.h> #include <consensus/consensus.h> #include <core_io.h> +#include <key_io.h> #include <keystore.h> #include <policy/policy.h> #include <policy/rbf.h> @@ -551,7 +551,6 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) // mergedTx will end up with all the signatures; it // starts as a clone of the raw tx: CMutableTransaction mergedTx(txVariants[0]); - bool fComplete = true; CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); @@ -563,12 +562,10 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) for (unsigned int kidx = 0; kidx < keysObj.size(); kidx++) { if (!keysObj[kidx].isStr()) throw std::runtime_error("privatekey not a std::string"); - CBitcoinSecret vchSecret; - bool fGood = vchSecret.SetString(keysObj[kidx].getValStr()); - if (!fGood) + CKey key = DecodeSecret(keysObj[kidx].getValStr()); + if (!key.IsValid()) { throw std::runtime_error("privatekey not valid"); - - CKey key = vchSecret.GetKey(); + } tempKeystore.AddKey(key); } @@ -639,7 +636,6 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) CTxIn& txin = mergedTx.vin[i]; const Coin& coin = view.AccessCoin(txin.prevout); if (coin.IsSpent()) { - fComplete = false; continue; } const CScript& prevPubKey = coin.out.scriptPubKey; @@ -654,14 +650,6 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) for (const CTransaction& txv : txVariants) sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i)); UpdateTransaction(mergedTx, i, sigdata); - - if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i, amount))) - fComplete = false; - } - - if (fComplete) { - // do nothing... for now - // perhaps store this for later optional JSON output } tx = mergedTx; diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 6eb223171f..c2b3480f9d 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -175,6 +175,9 @@ public: // (the tx=... number in the SetBestChain debug.log lines) 3.5 // * estimated number of transactions per second after that timestamp }; + + /* disable fallback fee on mainnet */ + m_fallback_fee_enabled = false; } }; @@ -266,6 +269,8 @@ public: 0.09 }; + /* enable fallback fee on testnet */ + m_fallback_fee_enabled = true; } }; @@ -343,6 +348,9 @@ public: base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94}; bech32_hrp = "bcrt"; + + /* enable fallback fee on regtest */ + m_fallback_fee_enabled = true; } }; diff --git a/src/chainparams.h b/src/chainparams.h index d478da9891..6b1f813afb 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -65,6 +65,8 @@ public: bool MineBlocksOnDemand() const { return fMineBlocksOnDemand; } /** Return the BIP70 network string (main, test or regtest) */ std::string NetworkIDString() const { return strNetworkID; } + /** Return true if the fallback fee is by default enabled for this network */ + bool IsFallbackFeeEnabled() const { return m_fallback_fee_enabled; } /** Return the list of hostnames to look up for DNS seeds */ const std::vector<std::string>& DNSSeeds() const { return vSeeds; } const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } @@ -91,6 +93,7 @@ protected: bool fMineBlocksOnDemand; CCheckpointData checkpointData; ChainTxData chainTxData; + bool m_fallback_fee_enabled; }; /** diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index 89dd8549b9..a04258fd40 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -24,44 +24,6 @@ void AppendParamsHelpMessages(std::string& strUsage, bool debugHelp) strUsage += HelpMessageOpt("-testnet", _("Use the test chain")); } -/** - * Main network - */ -class CBaseMainParams : public CBaseChainParams -{ -public: - CBaseMainParams() - { - nRPCPort = 8332; - } -}; - -/** - * Testnet (v3) - */ -class CBaseTestNetParams : public CBaseChainParams -{ -public: - CBaseTestNetParams() - { - nRPCPort = 18332; - strDataDir = "testnet3"; - } -}; - -/* - * Regression test - */ -class CBaseRegTestParams : public CBaseChainParams -{ -public: - CBaseRegTestParams() - { - nRPCPort = 18443; - strDataDir = "regtest"; - } -}; - static std::unique_ptr<CBaseChainParams> globalChainBaseParams; const CBaseChainParams& BaseParams() @@ -73,11 +35,11 @@ const CBaseChainParams& BaseParams() std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain) { if (chain == CBaseChainParams::MAIN) - return std::unique_ptr<CBaseChainParams>(new CBaseMainParams()); + return MakeUnique<CBaseChainParams>("", 8332); else if (chain == CBaseChainParams::TESTNET) - return std::unique_ptr<CBaseChainParams>(new CBaseTestNetParams()); + return MakeUnique<CBaseChainParams>("testnet3", 18332); else if (chain == CBaseChainParams::REGTEST) - return std::unique_ptr<CBaseChainParams>(new CBaseRegTestParams()); + return MakeUnique<CBaseChainParams>("regtest", 18443); else throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain)); } diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h index b4d2bb4f08..2cb860380e 100644 --- a/src/chainparamsbase.h +++ b/src/chainparamsbase.h @@ -24,9 +24,10 @@ public: const std::string& DataDir() const { return strDataDir; } int RPCPort() const { return nRPCPort; } -protected: - CBaseChainParams() {} + CBaseChainParams() = delete; + CBaseChainParams(const std::string& data_dir, int rpc_port) : nRPCPort(rpc_port), strDataDir(data_dir) {} +private: int nRPCPort; std::string strDataDir; }; diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h index 2b102c464f..6e2b3c34a2 100644 --- a/src/chainparamsseeds.h +++ b/src/chainparamsseeds.h @@ -5,7 +5,7 @@ * AUTOGENERATED by contrib/seeds/generate-seeds.py * * Each line contains a 16-byte IPv6 address and a port. - * IPv4 as well as onion addresses are wrapped inside a IPv6 address accordingly. + * IPv4 as well as onion addresses are wrapped inside an IPv6 address accordingly. */ static SeedSpec6 pnSeed6_main[] = { {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x13,0x05,0x7f}, 8333}, diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index 9189c9a8ad..816d854db3 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -21,9 +21,10 @@ namespace Checkpoints { for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints)) { const uint256& hash = i.second; - BlockMap::const_iterator t = mapBlockIndex.find(hash); - if (t != mapBlockIndex.end()) - return t->second; + CBlockIndex* pindex = LookupBlockIndex(hash); + if (pindex) { + return pindex; + } } return nullptr; } diff --git a/src/checkpoints.h b/src/checkpoints.h index bf935f80a7..564b486393 100644 --- a/src/checkpoints.h +++ b/src/checkpoints.h @@ -19,7 +19,7 @@ struct CCheckpointData; namespace Checkpoints { -//! Returns last CBlockIndex* in mapBlockIndex that is a checkpoint +//! Returns last CBlockIndex* that is a checkpoint CBlockIndex* GetLastCheckpoint(const CCheckpointData& data); } //namespace Checkpoints diff --git a/src/clientversion.cpp b/src/clientversion.cpp index 7aa0a8d660..662fbb6e77 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -43,7 +43,7 @@ const std::string CLIENT_NAME("Satoshi"); //! git will put "#define GIT_ARCHIVE 1" on the next line inside archives. $Format:%n#define GIT_ARCHIVE 1$ #ifdef GIT_ARCHIVE -#define GIT_COMMIT_ID "$Format:%h$" +#define GIT_COMMIT_ID "$Format:%H$" #define GIT_COMMIT_DATE "$Format:%cD$" #endif diff --git a/src/compat.h b/src/compat.h index aae84b1181..8a0f901304 100644 --- a/src/compat.h +++ b/src/compat.h @@ -10,6 +10,16 @@ #include <config/bitcoin-config.h> #endif +#include <type_traits> + +// GCC 4.8 is missing some C++11 type_traits, +// https://www.gnu.org/software/gcc/gcc-5/changes.html +#if defined(__GNUC__) && __GNUC__ < 5 +#define IS_TRIVIALLY_CONSTRUCTIBLE std::is_trivial +#else +#define IS_TRIVIALLY_CONSTRUCTIBLE std::is_trivially_constructible +#endif + #ifdef WIN32 #ifdef _WIN32_WINNT #undef _WIN32_WINNT diff --git a/src/core_write.cpp b/src/core_write.cpp index ab6918e41d..91742b7d1b 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -4,9 +4,9 @@ #include <core_io.h> -#include <base58.h> #include <consensus/consensus.h> #include <consensus/validation.h> +#include <key_io.h> #include <script/script.h> #include <script/standard.h> #include <serialize.h> diff --git a/src/crypto/common.h b/src/crypto/common.h index 825b430978..6e9d6dc82a 100644 --- a/src/crypto/common.h +++ b/src/crypto/common.h @@ -82,12 +82,12 @@ void static inline WriteBE64(unsigned char* ptr, uint64_t x) /** Return the smallest number n such that (x >> n) == 0 (or 64 if the highest bit in x is set. */ uint64_t static inline CountBits(uint64_t x) { -#ifdef HAVE_DECL___BUILTIN_CLZL +#if HAVE_DECL___BUILTIN_CLZL if (sizeof(unsigned long) >= sizeof(uint64_t)) { return x ? 8 * sizeof(unsigned long) - __builtin_clzl(x) : 0; } #endif -#ifdef HAVE_DECL___BUILTIN_CLZLL +#if HAVE_DECL___BUILTIN_CLZLL if (sizeof(unsigned long long) >= sizeof(uint64_t)) { return x ? 8 * sizeof(unsigned long long) - __builtin_clzll(x) : 0; } diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp index 4e1e403f69..fb0d4215a2 100644 --- a/src/dbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -89,6 +89,7 @@ static leveldb::Options GetOptions(size_t nCacheSize) } CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool obfuscate) + : m_name(fs::basename(path)) { penv = nullptr; readoptions.verify_checksums = true; @@ -155,11 +156,30 @@ CDBWrapper::~CDBWrapper() bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync) { + const bool log_memory = LogAcceptCategory(BCLog::LEVELDB); + double mem_before = 0; + if (log_memory) { + mem_before = DynamicMemoryUsage() / 1024 / 1024; + } leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch); dbwrapper_private::HandleError(status); + if (log_memory) { + double mem_after = DynamicMemoryUsage() / 1024 / 1024; + LogPrint(BCLog::LEVELDB, "WriteBatch memory usage: db=%s, before=%.1fMiB, after=%.1fMiB\n", + m_name, mem_before, mem_after); + } return true; } +size_t CDBWrapper::DynamicMemoryUsage() const { + std::string memory; + if (!pdb->GetProperty("leveldb.approximate-memory-usage", &memory)) { + LogPrint(BCLog::LEVELDB, "Failed to get approximate-memory-usage property\n"); + return 0; + } + return stoul(memory); +} + // Prefixed with null character to avoid collisions with other keys // // We must use a string constructor which specifies length so that we copy @@ -198,14 +218,10 @@ void HandleError(const leveldb::Status& status) { if (status.ok()) return; - LogPrintf("%s\n", status.ToString()); - if (status.IsCorruption()) - throw dbwrapper_error("Database corrupted"); - if (status.IsIOError()) - throw dbwrapper_error("Database I/O error"); - if (status.IsNotFound()) - throw dbwrapper_error("Database entry missing"); - throw dbwrapper_error("Unknown database error"); + const std::string errmsg = "Fatal LevelDB error: " + status.ToString(); + LogPrintf("%s\n", errmsg); + LogPrintf("You can use -debug=leveldb to get more complete diagnostic messages\n"); + throw dbwrapper_error(errmsg); } const std::vector<unsigned char>& GetObfuscateKey(const CDBWrapper &w) diff --git a/src/dbwrapper.h b/src/dbwrapper.h index a29938ce33..6f80eedc7a 100644 --- a/src/dbwrapper.h +++ b/src/dbwrapper.h @@ -198,6 +198,9 @@ private: //! the database itself leveldb::DB* pdb; + //! the name of this database + std::string m_name; + //! a key used for optional XOR-obfuscation of the database std::vector<unsigned char> obfuscate_key; @@ -284,6 +287,9 @@ public: bool WriteBatch(CDBBatch& batch, bool fSync = false); + // Get an estimate of LevelDB memory usage (in bytes). + size_t DynamicMemoryUsage() const; + // not available for LevelDB; provide for compatibility with BDB bool Flush() { diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 5e9e419744..82ae733006 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -4,9 +4,9 @@ #include <httprpc.h> -#include <base58.h> #include <chainparams.h> #include <httpserver.h> +#include <key_io.h> #include <rpc/protocol.h> #include <rpc/server.h> #include <random.h> diff --git a/src/init.cpp b/src/init.cpp index 84398d978c..e0efe5c0a2 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -165,6 +165,7 @@ void Interrupt() InterruptRPC(); InterruptREST(); InterruptTorControl(); + InterruptMapPort(); if (g_connman) g_connman->Interrupt(); } @@ -191,7 +192,7 @@ void Shutdown() #ifdef ENABLE_WALLET FlushWallets(); #endif - MapPort(false); + StopMapPort(); // Because these depend on each-other, we make sure that neither can be // using the other before destroying them. @@ -447,6 +448,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT)); strUsage += HelpMessageOpt("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT)); strUsage += HelpMessageOpt("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)"); + strUsage += HelpMessageOpt("-addrmantest", "Allows to test address relay on localhost"); } strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " + _("If <category> is not supplied or if <category> = 1, output all debugging information.") + " " + _("<category> can be:") + " " + ListLogCategories() + "."); @@ -489,7 +491,8 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-whitelistrelay", strprintf(_("Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)"), DEFAULT_WHITELISTRELAY)); strUsage += HelpMessageGroup(_("Block creation options:")); - strUsage += HelpMessageOpt("-blockmaxsize=<n>", _("Set maximum BIP141 block weight to this * 4. Deprecated, use blockmaxweight")); + if (showDebug) + strUsage += HelpMessageOpt("-blockmaxsize=<n>", "Set maximum BIP141 block weight to this * 4. Deprecated, use blockmaxweight"); strUsage += HelpMessageOpt("-blockmaxweight=<n>", strprintf(_("Set maximum BIP141 block weight (default: %d)"), DEFAULT_BLOCK_MAX_WEIGHT)); strUsage += HelpMessageOpt("-blockmintxfee=<amt>", strprintf(_("Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE))); if (showDebug) @@ -545,7 +548,8 @@ static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex std::string strCmd = gArgs.GetArg("-blocknotify", ""); if (!strCmd.empty()) { boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex()); - boost::thread t(runCommand, strCmd); // thread runs free + std::thread t(runCommand, strCmd); + t.detach(); // thread runs free } } @@ -678,11 +682,13 @@ void ThreadImport(std::vector<fs::path> vImportFiles) if (!ActivateBestChain(state, chainparams)) { LogPrintf("Failed to connect best block"); StartShutdown(); + return; } if (gArgs.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) { LogPrintf("Stopping after block import\n"); StartShutdown(); + return; } } // End scope of CImportingNow if (gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) { @@ -920,7 +926,7 @@ bool AppInitParameterInteraction() nMaxConnections = std::max(nUserMaxConnections, 0); // Trim requested connection counts, to fit into system limitations - nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS)), 0); + nMaxConnections = std::max(std::min(nMaxConnections, FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS), 0); nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS + MAX_ADDNODE_CONNECTIONS); if (nFD < MIN_CORE_FILEDESCRIPTORS) return InitError(_("Not enough file descriptors available.")); @@ -1077,7 +1083,7 @@ bool AppInitParameterInteraction() if (gArgs.IsArgSet("-dustrelayfee")) { CAmount n = 0; - if (!ParseMoney(gArgs.GetArg("-dustrelayfee", ""), n) || 0 == n) + if (!ParseMoney(gArgs.GetArg("-dustrelayfee", ""), n)) return InitError(AmountErrMsg("dustrelayfee", gArgs.GetArg("-dustrelayfee", ""))); dustRelayFee = CFeeRate(n); } @@ -1218,7 +1224,7 @@ bool AppInitMain() } if (!fLogTimestamps) - LogPrintf("Startup time: %s\n", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime())); + LogPrintf("Startup time: %s\n", FormatISO8601DateTime(GetTime())); LogPrintf("Default data directory %s\n", GetDefaultDataDir().string()); LogPrintf("Using data directory %s\n", GetDataDir().string()); LogPrintf("Using config file %s\n", GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)).string()); @@ -1418,6 +1424,8 @@ bool AppInitMain() uiInterface.InitMessage(_("Loading block index...")); + LOCK(cs_main); + nStart = GetTimeMillis(); do { try { @@ -1425,6 +1433,9 @@ bool AppInitMain() pcoinsTip.reset(); pcoinsdbview.reset(); pcoinscatcher.reset(); + // new CBlockTreeDB tries to delete the existing file, which + // fails if it's still open from the previous loop. Close it first: + pblocktree.reset(); pblocktree.reset(new CBlockTreeDB(nBlockTreeDBCache, false, fReset)); if (fReset) { @@ -1448,8 +1459,9 @@ bool AppInitMain() // If the loaded chain has a wrong genesis, bail out immediately // (we're likely using a testnet datadir, or the other way around). - if (!mapBlockIndex.empty() && mapBlockIndex.count(chainparams.GetConsensus().hashGenesisBlock) == 0) + if (!mapBlockIndex.empty() && !LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) { return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?")); + } // Check for changed -txindex state if (fTxIndex != gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { @@ -1519,20 +1531,17 @@ bool AppInitMain() if (!is_coinsview_empty) { uiInterface.InitMessage(_("Verifying blocks...")); if (fHavePruned && gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) { - LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks", + LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n", MIN_BLOCKS_TO_KEEP); } - { - LOCK(cs_main); - CBlockIndex* tip = chainActive.Tip(); - RPCNotifyBlockChange(true, tip); - if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) { - strLoadError = _("The block database contains a block which appears to be from the future. " - "This may be due to your computer's date and time being set incorrectly. " - "Only rebuild the block database if you are sure that your computer's date and time are correct"); - break; - } + CBlockIndex* tip = chainActive.Tip(); + RPCNotifyBlockChange(true, tip); + if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) { + strLoadError = _("The block database contains a block which appears to be from the future. " + "This may be due to your computer's date and time being set incorrectly. " + "Only rebuild the block database if you are sure that your computer's date and time are correct"); + break; } if (!CVerifyDB().VerifyDB(chainparams, pcoinsdbview.get(), gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL), @@ -1671,12 +1680,14 @@ bool AppInitMain() LogPrintf("nBestHeight = %d\n", chain_active_height); if (gArgs.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) - StartTorControl(threadGroup, scheduler); + StartTorControl(); - Discover(threadGroup); + Discover(); // Map ports with UPnP - MapPort(gArgs.GetBoolArg("-upnp", DEFAULT_UPNP)); + if (gArgs.GetBoolArg("-upnp", DEFAULT_UPNP)) { + StartMapPort(); + } CConnman::Options connOptions; connOptions.nLocalServices = nLocalServices; diff --git a/src/key.cpp b/src/key.cpp index e998e3db6e..042e687772 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -44,7 +44,7 @@ static int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *ou if (end - privkey < 1 || !(*privkey & 0x80u)) { return 0; } - size_t lenb = *privkey & ~0x80u; privkey++; + ptrdiff_t lenb = *privkey & ~0x80u; privkey++; if (lenb < 1 || lenb > 2) { return 0; } @@ -52,7 +52,7 @@ static int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *ou return 0; } /* sequence length */ - size_t len = privkey[lenb-1] | (lenb > 1 ? privkey[lenb-2] << 8 : 0u); + ptrdiff_t len = privkey[lenb-1] | (lenb > 1 ? privkey[lenb-2] << 8 : 0u); privkey += lenb; if (end - privkey < len) { return 0; @@ -66,7 +66,7 @@ static int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *ou if (end - privkey < 2 || privkey[0] != 0x04u) { return 0; } - size_t oslen = privkey[1]; + ptrdiff_t oslen = privkey[1]; privkey += 2; if (oslen > 32 || end - privkey < oslen) { return 0; @@ -170,7 +170,7 @@ CPrivKey CKey::GetPrivKey() const { size_t privkeylen; privkey.resize(PRIVATE_KEY_SIZE); privkeylen = PRIVATE_KEY_SIZE; - ret = ec_privkey_export_der(secp256k1_context_sign, (unsigned char*) privkey.data(), &privkeylen, begin(), fCompressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED); + ret = ec_privkey_export_der(secp256k1_context_sign, privkey.data(), &privkeylen, begin(), fCompressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED); assert(ret); privkey.resize(privkeylen); return privkey; @@ -199,7 +199,7 @@ bool CKey::Sign(const uint256 &hash, std::vector<unsigned char>& vchSig, uint32_ secp256k1_ecdsa_signature sig; int ret = secp256k1_ecdsa_sign(secp256k1_context_sign, &sig, hash.begin(), begin(), secp256k1_nonce_function_rfc6979, test_case ? extra_entropy : nullptr); assert(ret); - secp256k1_ecdsa_signature_serialize_der(secp256k1_context_sign, (unsigned char*)vchSig.data(), &nSigLen, &sig); + secp256k1_ecdsa_signature_serialize_der(secp256k1_context_sign, vchSig.data(), &nSigLen, &sig); vchSig.resize(nSigLen); return true; } @@ -226,7 +226,7 @@ bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig) secp256k1_ecdsa_recoverable_signature sig; int ret = secp256k1_ecdsa_sign_recoverable(secp256k1_context_sign, &sig, hash.begin(), begin(), secp256k1_nonce_function_rfc6979, nullptr); assert(ret); - secp256k1_ecdsa_recoverable_signature_serialize_compact(secp256k1_context_sign, (unsigned char*)&vchSig[1], &rec, &sig); + secp256k1_ecdsa_recoverable_signature_serialize_compact(secp256k1_context_sign, &vchSig[1], &rec, &sig); assert(ret); assert(rec != -1); vchSig[0] = 27 + rec + (fCompressed ? 4 : 0); diff --git a/src/key_io.cpp b/src/key_io.cpp new file mode 100644 index 0000000000..c2dc511989 --- /dev/null +++ b/src/key_io.cpp @@ -0,0 +1,227 @@ +// Copyright (c) 2014-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <key_io.h> + +#include <base58.h> +#include <bech32.h> +#include <script/script.h> +#include <utilstrencodings.h> + +#include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> + +#include <assert.h> +#include <string.h> +#include <algorithm> + +namespace +{ +class DestinationEncoder : public boost::static_visitor<std::string> +{ +private: + const CChainParams& m_params; + +public: + DestinationEncoder(const CChainParams& params) : m_params(params) {} + + std::string operator()(const CKeyID& id) const + { + std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); + data.insert(data.end(), id.begin(), id.end()); + return EncodeBase58Check(data); + } + + std::string operator()(const CScriptID& id) const + { + std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); + data.insert(data.end(), id.begin(), id.end()); + return EncodeBase58Check(data); + } + + std::string operator()(const WitnessV0KeyHash& id) const + { + std::vector<unsigned char> data = {0}; + data.reserve(33); + ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.begin(), id.end()); + return bech32::Encode(m_params.Bech32HRP(), data); + } + + std::string operator()(const WitnessV0ScriptHash& id) const + { + std::vector<unsigned char> data = {0}; + data.reserve(53); + ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.begin(), id.end()); + return bech32::Encode(m_params.Bech32HRP(), data); + } + + std::string operator()(const WitnessUnknown& id) const + { + if (id.version < 1 || id.version > 16 || id.length < 2 || id.length > 40) { + return {}; + } + std::vector<unsigned char> data = {(unsigned char)id.version}; + data.reserve(1 + (id.length * 8 + 4) / 5); + ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.program, id.program + id.length); + return bech32::Encode(m_params.Bech32HRP(), data); + } + + std::string operator()(const CNoDestination& no) const { return {}; } +}; + +CTxDestination DecodeDestination(const std::string& str, const CChainParams& params) +{ + std::vector<unsigned char> data; + uint160 hash; + if (DecodeBase58Check(str, data)) { + // base58-encoded Bitcoin addresses. + // Public-key-hash-addresses have version 0 (or 111 testnet). + // The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. + const std::vector<unsigned char>& pubkey_prefix = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); + if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) { + std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin()); + return CKeyID(hash); + } + // Script-hash-addresses have version 5 (or 196 testnet). + // The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script. + const std::vector<unsigned char>& script_prefix = params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); + if (data.size() == hash.size() + script_prefix.size() && std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) { + std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin()); + return CScriptID(hash); + } + } + data.clear(); + auto bech = bech32::Decode(str); + if (bech.second.size() > 0 && bech.first == params.Bech32HRP()) { + // Bech32 decoding + int version = bech.second[0]; // The first 5 bit symbol is the witness version (0-16) + // The rest of the symbols are converted witness program bytes. + data.reserve(((bech.second.size() - 1) * 5) / 8); + if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, bech.second.begin() + 1, bech.second.end())) { + if (version == 0) { + { + WitnessV0KeyHash keyid; + if (data.size() == keyid.size()) { + std::copy(data.begin(), data.end(), keyid.begin()); + return keyid; + } + } + { + WitnessV0ScriptHash scriptid; + if (data.size() == scriptid.size()) { + std::copy(data.begin(), data.end(), scriptid.begin()); + return scriptid; + } + } + return CNoDestination(); + } + if (version > 16 || data.size() < 2 || data.size() > 40) { + return CNoDestination(); + } + WitnessUnknown unk; + unk.version = version; + std::copy(data.begin(), data.end(), unk.program); + unk.length = data.size(); + return unk; + } + } + return CNoDestination(); +} +} // namespace + +CKey DecodeSecret(const std::string& str) +{ + CKey key; + std::vector<unsigned char> data; + if (DecodeBase58Check(str, data)) { + const std::vector<unsigned char>& privkey_prefix = Params().Base58Prefix(CChainParams::SECRET_KEY); + if ((data.size() == 32 + privkey_prefix.size() || (data.size() == 33 + privkey_prefix.size() && data.back() == 1)) && + std::equal(privkey_prefix.begin(), privkey_prefix.end(), data.begin())) { + bool compressed = data.size() == 33 + privkey_prefix.size(); + key.Set(data.begin() + privkey_prefix.size(), data.begin() + privkey_prefix.size() + 32, compressed); + } + } + memory_cleanse(data.data(), data.size()); + return key; +} + +std::string EncodeSecret(const CKey& key) +{ + assert(key.IsValid()); + std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::SECRET_KEY); + data.insert(data.end(), key.begin(), key.end()); + if (key.IsCompressed()) { + data.push_back(1); + } + std::string ret = EncodeBase58Check(data); + memory_cleanse(data.data(), data.size()); + return ret; +} + +CExtPubKey DecodeExtPubKey(const std::string& str) +{ + CExtPubKey key; + std::vector<unsigned char> data; + if (DecodeBase58Check(str, data)) { + const std::vector<unsigned char>& prefix = Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY); + if (data.size() == BIP32_EXTKEY_SIZE + prefix.size() && std::equal(prefix.begin(), prefix.end(), data.begin())) { + key.Decode(data.data() + prefix.size()); + } + } + return key; +} + +std::string EncodeExtPubKey(const CExtPubKey& key) +{ + std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY); + size_t size = data.size(); + data.resize(size + BIP32_EXTKEY_SIZE); + key.Encode(data.data() + size); + std::string ret = EncodeBase58Check(data); + return ret; +} + +CExtKey DecodeExtKey(const std::string& str) +{ + CExtKey key; + std::vector<unsigned char> data; + if (DecodeBase58Check(str, data)) { + const std::vector<unsigned char>& prefix = Params().Base58Prefix(CChainParams::EXT_SECRET_KEY); + if (data.size() == BIP32_EXTKEY_SIZE + prefix.size() && std::equal(prefix.begin(), prefix.end(), data.begin())) { + key.Decode(data.data() + prefix.size()); + } + } + return key; +} + +std::string EncodeExtKey(const CExtKey& key) +{ + std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::EXT_SECRET_KEY); + size_t size = data.size(); + data.resize(size + BIP32_EXTKEY_SIZE); + key.Encode(data.data() + size); + std::string ret = EncodeBase58Check(data); + memory_cleanse(data.data(), data.size()); + return ret; +} + +std::string EncodeDestination(const CTxDestination& dest) +{ + return boost::apply_visitor(DestinationEncoder(Params()), dest); +} + +CTxDestination DecodeDestination(const std::string& str) +{ + return DecodeDestination(str, Params()); +} + +bool IsValidDestinationString(const std::string& str, const CChainParams& params) +{ + return IsValidDestination(DecodeDestination(str, params)); +} + +bool IsValidDestinationString(const std::string& str) +{ + return IsValidDestinationString(str, Params()); +} diff --git a/src/key_io.h b/src/key_io.h new file mode 100644 index 0000000000..6fc9a8059a --- /dev/null +++ b/src/key_io.h @@ -0,0 +1,29 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_KEYIO_H +#define BITCOIN_KEYIO_H + +#include <chainparams.h> +#include <key.h> +#include <pubkey.h> +#include <script/standard.h> + +#include <string> + +CKey DecodeSecret(const std::string& str); +std::string EncodeSecret(const CKey& key); + +CExtKey DecodeExtKey(const std::string& str); +std::string EncodeExtKey(const CExtKey& extkey); +CExtPubKey DecodeExtPubKey(const std::string& str); +std::string EncodeExtPubKey(const CExtPubKey& extpubkey); + +std::string EncodeDestination(const CTxDestination& dest); +CTxDestination DecodeDestination(const std::string& str); +bool IsValidDestinationString(const std::string& str); +bool IsValidDestinationString(const std::string& str, const CChainParams& params); + +#endif // BITCOIN_KEYIO_H diff --git a/src/leveldb/db/db_impl.cc b/src/leveldb/db/db_impl.cc index f43ad76794..3bb58e560a 100644 --- a/src/leveldb/db/db_impl.cc +++ b/src/leveldb/db/db_impl.cc @@ -414,7 +414,7 @@ Status DBImpl::RecoverLogFile(uint64_t log_number, bool last_log, status.ok()) { if (record.size() < 12) { reporter.Corruption( - record.size(), Status::Corruption("log record too small")); + record.size(), Status::Corruption("log record too small", fname)); continue; } WriteBatchInternal::SetContents(&batch, record); diff --git a/src/leveldb/db/leveldbutil.cc b/src/leveldb/db/leveldbutil.cc index 9f4b7dd70c..d06d64d640 100644 --- a/src/leveldb/db/leveldbutil.cc +++ b/src/leveldb/db/leveldbutil.cc @@ -19,6 +19,7 @@ class StdoutPrinter : public WritableFile { virtual Status Close() { return Status::OK(); } virtual Status Flush() { return Status::OK(); } virtual Status Sync() { return Status::OK(); } + virtual std::string GetName() const { return "[stdout]"; } }; bool HandleDumpCommand(Env* env, char** files, int num) { diff --git a/src/leveldb/db/log_reader.cc b/src/leveldb/db/log_reader.cc index a6d304545d..8b6ad136d7 100644 --- a/src/leveldb/db/log_reader.cc +++ b/src/leveldb/db/log_reader.cc @@ -186,7 +186,7 @@ uint64_t Reader::LastRecordOffset() { } void Reader::ReportCorruption(uint64_t bytes, const char* reason) { - ReportDrop(bytes, Status::Corruption(reason)); + ReportDrop(bytes, Status::Corruption(reason, file_->GetName())); } void Reader::ReportDrop(uint64_t bytes, const Status& reason) { diff --git a/src/leveldb/db/repair.cc b/src/leveldb/db/repair.cc index 4cd4bb047f..7281e3d345 100644 --- a/src/leveldb/db/repair.cc +++ b/src/leveldb/db/repair.cc @@ -203,7 +203,7 @@ class Repairer { while (reader.ReadRecord(&record, &scratch)) { if (record.size() < 12) { reporter.Corruption( - record.size(), Status::Corruption("log record too small")); + record.size(), Status::Corruption("log record too small", logname)); continue; } WriteBatchInternal::SetContents(&batch, record); diff --git a/src/leveldb/helpers/memenv/memenv.cc b/src/leveldb/helpers/memenv/memenv.cc index 9a98884daf..68c0614a59 100644 --- a/src/leveldb/helpers/memenv/memenv.cc +++ b/src/leveldb/helpers/memenv/memenv.cc @@ -176,6 +176,7 @@ class SequentialFileImpl : public SequentialFile { return Status::OK(); } + virtual std::string GetName() const { return "[memenv]"; } private: FileState* file_; uint64_t pos_; @@ -196,6 +197,7 @@ class RandomAccessFileImpl : public RandomAccessFile { return file_->Read(offset, n, result, scratch); } + virtual std::string GetName() const { return "[memenv]"; } private: FileState* file_; }; @@ -218,6 +220,7 @@ class WritableFileImpl : public WritableFile { virtual Status Flush() { return Status::OK(); } virtual Status Sync() { return Status::OK(); } + virtual std::string GetName() const { return "[memenv]"; } private: FileState* file_; }; diff --git a/src/leveldb/include/leveldb/env.h b/src/leveldb/include/leveldb/env.h index 99b6c21414..275d441eae 100644 --- a/src/leveldb/include/leveldb/env.h +++ b/src/leveldb/include/leveldb/env.h @@ -191,6 +191,9 @@ class SequentialFile { // REQUIRES: External synchronization virtual Status Skip(uint64_t n) = 0; + // Get a name for the file, only for error reporting + virtual std::string GetName() const = 0; + private: // No copying allowed SequentialFile(const SequentialFile&); @@ -215,6 +218,9 @@ class RandomAccessFile { virtual Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const = 0; + // Get a name for the file, only for error reporting + virtual std::string GetName() const = 0; + private: // No copying allowed RandomAccessFile(const RandomAccessFile&); @@ -234,6 +240,9 @@ class WritableFile { virtual Status Flush() = 0; virtual Status Sync() = 0; + // Get a name for the file, only for error reporting + virtual std::string GetName() const = 0; + private: // No copying allowed WritableFile(const WritableFile&); diff --git a/src/leveldb/table/format.cc b/src/leveldb/table/format.cc index 24e4e02445..285e1c0de3 100644 --- a/src/leveldb/table/format.cc +++ b/src/leveldb/table/format.cc @@ -82,7 +82,7 @@ Status ReadBlock(RandomAccessFile* file, } if (contents.size() != n + kBlockTrailerSize) { delete[] buf; - return Status::Corruption("truncated block read"); + return Status::Corruption("truncated block read", file->GetName()); } // Check the crc of the type and the block contents @@ -92,7 +92,7 @@ Status ReadBlock(RandomAccessFile* file, const uint32_t actual = crc32c::Value(data, n + 1); if (actual != crc) { delete[] buf; - s = Status::Corruption("block checksum mismatch"); + s = Status::Corruption("block checksum mismatch", file->GetName()); return s; } } @@ -119,13 +119,13 @@ Status ReadBlock(RandomAccessFile* file, size_t ulength = 0; if (!port::Snappy_GetUncompressedLength(data, n, &ulength)) { delete[] buf; - return Status::Corruption("corrupted compressed block contents"); + return Status::Corruption("corrupted compressed block contents", file->GetName()); } char* ubuf = new char[ulength]; if (!port::Snappy_Uncompress(data, n, ubuf)) { delete[] buf; delete[] ubuf; - return Status::Corruption("corrupted compressed block contents"); + return Status::Corruption("corrupted compressed block contents", file->GetName()); } delete[] buf; result->data = Slice(ubuf, ulength); @@ -135,7 +135,7 @@ Status ReadBlock(RandomAccessFile* file, } default: delete[] buf; - return Status::Corruption("bad block type"); + return Status::Corruption("bad block type", file->GetName()); } return Status::OK(); diff --git a/src/leveldb/util/env_posix.cc b/src/leveldb/util/env_posix.cc index dd852af354..4676bc2240 100644 --- a/src/leveldb/util/env_posix.cc +++ b/src/leveldb/util/env_posix.cc @@ -121,6 +121,8 @@ class PosixSequentialFile: public SequentialFile { } return Status::OK(); } + + virtual std::string GetName() const { return filename_; } }; // pread() based random-access @@ -172,6 +174,8 @@ class PosixRandomAccessFile: public RandomAccessFile { } return s; } + + virtual std::string GetName() const { return filename_; } }; // mmap() based random-access @@ -206,6 +210,8 @@ class PosixMmapReadableFile: public RandomAccessFile { } return s; } + + virtual std::string GetName() const { return filename_; } }; class PosixWritableFile : public WritableFile { @@ -287,6 +293,8 @@ class PosixWritableFile : public WritableFile { } return s; } + + virtual std::string GetName() const { return filename_; } }; static int LockOrUnlock(int fd, bool lock) { diff --git a/src/leveldb/util/env_win.cc b/src/leveldb/util/env_win.cc index d32c4e676c..81380216bb 100644 --- a/src/leveldb/util/env_win.cc +++ b/src/leveldb/util/env_win.cc @@ -78,6 +78,7 @@ public: virtual Status Read(size_t n, Slice* result, char* scratch); virtual Status Skip(uint64_t n); BOOL isEnable(); + virtual std::string GetName() const { return _filename; } private: BOOL _Init(); void _CleanUp(); @@ -94,6 +95,7 @@ public: virtual ~Win32RandomAccessFile(); virtual Status Read(uint64_t offset, size_t n, Slice* result,char* scratch) const; BOOL isEnable(); + virtual std::string GetName() const { return _filename; } private: BOOL _Init(LPCWSTR path); void _CleanUp(); @@ -114,6 +116,7 @@ public: virtual Status Flush(); virtual Status Sync(); BOOL isEnable(); + virtual std::string GetName() const { return filename_; } private: std::string filename_; ::HANDLE _hFile; diff --git a/src/miner.cpp b/src/miner.cpp index dda52790c6..fcb376c6cb 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -30,12 +30,6 @@ #include <queue> #include <utility> -////////////////////////////////////////////////////////////////////////////// -// -// BitcoinMiner -// - -// // Unconfirmed transactions in the memory pool often depend on other // transactions in the memory pool. When we select transactions from the // pool, we select by highest fee rate of a transaction combined with all diff --git a/src/net.cpp b/src/net.cpp index 03ed7e7fc1..53a0a9b180 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -181,6 +181,10 @@ void AdvertiseLocal(CNode *pnode) if (fListen && pnode->fSuccessfullyConnected) { CAddress addrLocal = GetLocalAddress(&pnode->addr, pnode->GetLocalServices()); + if (gArgs.GetBoolArg("-addrmantest", false)) { + // use IPv4 loopback during addrmantest + addrLocal = CAddress(CService(LookupNumeric("127.0.0.1", GetListenPort())), pnode->GetLocalServices()); + } // If discovery is enabled, sometimes give our peer the address it // tells us that it sees us as in case it has a better idea of our // address than we do. @@ -189,7 +193,7 @@ void AdvertiseLocal(CNode *pnode) { addrLocal.SetIP(pnode->GetAddrLocal()); } - if (addrLocal.IsRoutable()) + if (addrLocal.IsRoutable() || gArgs.GetBoolArg("-addrmantest", false)) { LogPrint(BCLog::NET, "AdvertiseLocal: advertising address %s\n", addrLocal.ToString()); FastRandomContext insecure_rand; @@ -1459,6 +1463,8 @@ void CConnman::WakeMessageHandler() #ifdef USE_UPNP +static CThreadInterrupt g_upnp_interrupt; +static std::thread g_upnp_thread; void ThreadMapPort() { std::string port = strprintf("%u", GetListenPort()); @@ -1509,35 +1515,29 @@ void ThreadMapPort() std::string strDesc = "Bitcoin " + FormatFullVersion(); - try { - while (true) { + do { #ifndef UPNPDISCOVER_SUCCESS - /* miniupnpc 1.5 */ - r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, - port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0); + /* miniupnpc 1.5 */ + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, + port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0); #else - /* miniupnpc 1.6 */ - r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, - port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); + /* miniupnpc 1.6 */ + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, + port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); #endif - if(r!=UPNPCOMMAND_SUCCESS) - LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", - port, port, lanaddr, r, strupnperror(r)); - else - LogPrintf("UPnP Port Mapping successful.\n"); - - MilliSleep(20*60*1000); // Refresh every 20 minutes - } - } - catch (const boost::thread_interrupted&) - { - r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); - LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r); - freeUPNPDevlist(devlist); devlist = nullptr; - FreeUPNPUrls(&urls); - throw; + if(r!=UPNPCOMMAND_SUCCESS) + LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", + port, port, lanaddr, r, strupnperror(r)); + else + LogPrintf("UPnP Port Mapping successful.\n"); } + while(g_upnp_interrupt.sleep_for(std::chrono::minutes(20))); + + r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); + LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r); + freeUPNPDevlist(devlist); devlist = nullptr; + FreeUPNPUrls(&urls); } else { LogPrintf("No valid UPnP IGDs found\n"); freeUPNPDevlist(devlist); devlist = nullptr; @@ -1546,27 +1546,39 @@ void ThreadMapPort() } } -void MapPort(bool fUseUPnP) +void StartMapPort() { - static std::unique_ptr<boost::thread> upnp_thread; + if (!g_upnp_thread.joinable()) { + assert(!g_upnp_interrupt); + g_upnp_thread = std::thread((std::bind(&TraceThread<void (*)()>, "upnp", &ThreadMapPort))); + } +} - if (fUseUPnP) - { - if (upnp_thread) { - upnp_thread->interrupt(); - upnp_thread->join(); - } - upnp_thread.reset(new boost::thread(boost::bind(&TraceThread<void (*)()>, "upnp", &ThreadMapPort))); +void InterruptMapPort() +{ + if(g_upnp_thread.joinable()) { + g_upnp_interrupt(); } - else if (upnp_thread) { - upnp_thread->interrupt(); - upnp_thread->join(); - upnp_thread.reset(); +} + +void StopMapPort() +{ + if(g_upnp_thread.joinable()) { + g_upnp_thread.join(); + g_upnp_interrupt.reset(); } } #else -void MapPort(bool) +void StartMapPort() +{ + // Intentionally left blank. +} +void InterruptMapPort() +{ + // Intentionally left blank. +} +void StopMapPort() { // Intentionally left blank. } @@ -1619,7 +1631,8 @@ void CConnman::ThreadDNSAddressSeed() if (!resolveSource.SetInternal(host)) { continue; } - if (LookupHost(host.c_str(), vIPs, 0, true)) + unsigned int nMaxIPs = 256; // Limits number of IPs learned from a DNS seed + if (LookupHost(host.c_str(), vIPs, nMaxIPs, true)) { for (const CNetAddr& ip : vIPs) { @@ -1816,11 +1829,18 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) } } + addrman.ResolveCollisions(); + int64_t nANow = GetAdjustedTime(); int nTries = 0; while (!interruptNet) { - CAddrInfo addr = addrman.Select(fFeeler); + CAddrInfo addr = addrman.SelectTriedCollision(); + + // SelectTriedCollision returns an invalid address if it is empty. + if (!fFeeler || !addr.IsValid()) { + addr = addrman.Select(fFeeler); + } // if we selected an invalid address, restart if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr)) @@ -2121,7 +2141,7 @@ bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, b return true; } -void Discover(boost::thread_group& threadGroup) +void Discover() { if (!fDiscover) return; @@ -2710,6 +2730,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn fOneShot = false; m_manual_connection = false; fClient = false; // set by version message + m_limited_node = false; // set by version message fFeeler = false; fSuccessfullyConnected = false; fDisconnect = false; @@ -2774,7 +2795,7 @@ void CNode::AskFor(const CInv& inv) nRequestTime = it->second; else nRequestTime = 0; - LogPrint(BCLog::NET, "askfor %s %d (%s) peer=%d\n", inv.ToString(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000), id); + LogPrint(BCLog::NET, "askfor %s %d (%s) peer=%d\n", inv.ToString(), nRequestTime, FormatISO8601Time(nRequestTime/1000000), id); // Make sure not to reuse time indexes to keep things in the same order int64_t nNow = GetTimeMicros() - 1000000; @@ -37,10 +37,6 @@ class CScheduler; class CNode; -namespace boost { - class thread_group; -} // namespace boost - /** Time between pings automatically sent out for latency probing and keepalive (in seconds). */ static const int PING_INTERVAL = 2 * 60; /** Time after which to disconnect, after waiting for a ping response (or inactivity). */ @@ -441,8 +437,10 @@ private: friend struct CConnmanTest; }; extern std::unique_ptr<CConnman> g_connman; -void Discover(boost::thread_group& threadGroup); -void MapPort(bool fUseUPnP); +void Discover(); +void StartMapPort(); +void InterruptMapPort(); +void StopMapPort(); unsigned short GetListenPort(); bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false); @@ -471,6 +469,13 @@ public: virtual bool SendMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0; virtual void InitializeNode(CNode* pnode) = 0; virtual void FinalizeNode(NodeId id, bool& update_connection_time) = 0; + +protected: + /** + * Protected destructor so that instances can only be deleted by derived classes. + * If that restriction is no longer desired, this should be made public and virtual. + */ + ~NetEventsInterface() = default; }; enum @@ -643,6 +648,7 @@ public: bool fOneShot; bool m_manual_connection; bool fClient; + bool m_limited_node; //after BIP159 const bool fInbound; std::atomic_bool fSuccessfullyConnected; std::atomic_bool fDisconnect; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index fc0ba82d8b..f5073fe903 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -374,10 +374,11 @@ void ProcessBlockAvailability(NodeId nodeid) { assert(state != nullptr); if (!state->hashLastUnknownBlock.IsNull()) { - BlockMap::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock); - if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0) { - if (state->pindexBestKnownBlock == nullptr || itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) - state->pindexBestKnownBlock = itOld->second; + const CBlockIndex* pindex = LookupBlockIndex(state->hashLastUnknownBlock); + if (pindex && pindex->nChainWork > 0) { + if (state->pindexBestKnownBlock == nullptr || pindex->nChainWork >= state->pindexBestKnownBlock->nChainWork) { + state->pindexBestKnownBlock = pindex; + } state->hashLastUnknownBlock.SetNull(); } } @@ -390,17 +391,24 @@ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) { ProcessBlockAvailability(nodeid); - BlockMap::iterator it = mapBlockIndex.find(hash); - if (it != mapBlockIndex.end() && it->second->nChainWork > 0) { + const CBlockIndex* pindex = LookupBlockIndex(hash); + if (pindex && pindex->nChainWork > 0) { // An actually better block was announced. - if (state->pindexBestKnownBlock == nullptr || it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) - state->pindexBestKnownBlock = it->second; + if (state->pindexBestKnownBlock == nullptr || pindex->nChainWork >= state->pindexBestKnownBlock->nChainWork) { + state->pindexBestKnownBlock = pindex; + } } else { // An unknown block was announced; just assume that the latest one is the best one. state->hashLastUnknownBlock = hash; } } +/** + * When a peer sends us a valid block, instruct it to announce blocks to us + * using CMPCTBLOCK if possible by adding its nodeid to the end of + * lNodesAnnouncingHeaderAndIDs, and keeping that list under a certain size by + * removing the first element if necessary. + */ void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connman) { AssertLockHeld(cs_main); CNodeState* nodestate = State(nodeid); @@ -749,7 +757,11 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) return nEvicted; } -// Requires cs_main. +/** + * Mark a misbehaving peer to be banned depending upon the value of `-banscore`. + * + * Requires cs_main. + */ void Misbehaving(NodeId pnode, int howmuch, const std::string& message) { if (howmuch == 0) @@ -808,6 +820,10 @@ PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, CScheduler &schedu scheduler.scheduleEvery(std::bind(&PeerLogicValidation::CheckForStaleTipAndEvictPeers, this, consensusParams), EXTRA_PEER_CHECK_INTERVAL * 1000); } +/** + * Evict orphan txn pool entries (EraseOrphanTx) based on a newly connected + * block. Also save the time of the last tip update. + */ void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex, const std::vector<CTransactionRef>& vtxConflicted) { LOCK(g_cs_orphans); @@ -828,7 +844,7 @@ void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pb } } - // Erase orphan transactions include or precluded by this block + // Erase orphan transactions included or precluded by this block if (vOrphanErase.size()) { int nErased = 0; for (uint256 &orphanHash : vOrphanErase) { @@ -847,6 +863,10 @@ static std::shared_ptr<const CBlockHeaderAndShortTxIDs> most_recent_compact_bloc static uint256 most_recent_block_hash; static bool fWitnessesPresentInMostRecentCompactBlock; +/** + * Maintain state about the best-seen block and fast-announce a compact block + * to compatible peers. + */ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) { std::shared_ptr<const CBlockHeaderAndShortTxIDs> pcmpctblock = std::make_shared<const CBlockHeaderAndShortTxIDs> (*pblock, true); const CNetMsgMaker msgMaker(PROTOCOL_VERSION); @@ -888,10 +908,15 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std: }); } +/** + * Update our best height and announce any block hashes which weren't previously + * in chainActive to our peers. + */ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) { const int nNewHeight = pindexNew->nHeight; connman->SetBestHeight(nNewHeight); + SetServiceFlagsIBDCache(!fInitialDownload); if (!fInitialDownload) { // Find the hashes of all blocks that weren't previously in the best chain. std::vector<uint256> vHashes; @@ -919,6 +944,10 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB nTimeBestReceived = GetTime(); } +/** + * Handle invalid block rejection and consequent peer banning, maintain which + * peers announce compact blocks. + */ void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationState& state) { LOCK(cs_main); @@ -988,7 +1017,7 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) } case MSG_BLOCK: case MSG_WITNESS_BLOCK: - return mapBlockIndex.count(inv.hash); + return LookupBlockIndex(inv.hash) != nullptr; } // Don't know what it is, just say we already got one return true; @@ -1055,11 +1084,10 @@ void static ProcessGetBlockData(CNode* pfrom, const Consensus::Params& consensus bool need_activate_chain = false; { LOCK(cs_main); - BlockMap::iterator mi = mapBlockIndex.find(inv.hash); - if (mi != mapBlockIndex.end()) - { - if (mi->second->nChainTx && !mi->second->IsValid(BLOCK_VALID_SCRIPTS) && - mi->second->IsValid(BLOCK_VALID_TREE)) { + const CBlockIndex* pindex = LookupBlockIndex(inv.hash); + if (pindex) { + if (pindex->nChainTx && !pindex->IsValid(BLOCK_VALID_SCRIPTS) && + pindex->IsValid(BLOCK_VALID_TREE)) { // If we have the block and all of its parents, but have not yet validated it, // we might be in the middle of connecting it (ie in the unlock of cs_main // before ActivateBestChain but after AcceptBlock). @@ -1075,9 +1103,9 @@ void static ProcessGetBlockData(CNode* pfrom, const Consensus::Params& consensus } LOCK(cs_main); - BlockMap::iterator mi = mapBlockIndex.find(inv.hash); - if (mi != mapBlockIndex.end()) { - send = BlockRequestAllowed(mi->second, consensusParams); + const CBlockIndex* pindex = LookupBlockIndex(inv.hash); + if (pindex) { + send = BlockRequestAllowed(pindex, consensusParams); if (!send) { LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId()); } @@ -1085,7 +1113,7 @@ void static ProcessGetBlockData(CNode* pfrom, const Consensus::Params& consensus const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); // disconnect node in case we have reached the outbound limit for serving historical blocks // never disconnect whitelisted nodes - if (send && connman->OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted) + if (send && connman->OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted) { LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId()); @@ -1095,7 +1123,7 @@ void static ProcessGetBlockData(CNode* pfrom, const Consensus::Params& consensus } // Avoid leaking prune-height by never sending blocks below the NODE_NETWORK_LIMITED threshold if (send && !pfrom->fWhitelisted && ( - (((pfrom->GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom->GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (chainActive.Tip()->nHeight - mi->second->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) ) + (((pfrom->GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom->GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (chainActive.Tip()->nHeight - pindex->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) ) )) { LogPrint(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED threshold from peer=%d\n", pfrom->GetId()); @@ -1105,15 +1133,15 @@ void static ProcessGetBlockData(CNode* pfrom, const Consensus::Params& consensus } // Pruned nodes may have deleted the block, so check whether // it's available before trying to send. - if (send && (mi->second->nStatus & BLOCK_HAVE_DATA)) + if (send && (pindex->nStatus & BLOCK_HAVE_DATA)) { std::shared_ptr<const CBlock> pblock; - if (a_recent_block && a_recent_block->GetHash() == (*mi).second->GetBlockHash()) { + if (a_recent_block && a_recent_block->GetHash() == pindex->GetBlockHash()) { pblock = a_recent_block; } else { // Send block from disk std::shared_ptr<CBlock> pblockRead = std::make_shared<CBlock>(); - if (!ReadBlockFromDisk(*pblockRead, (*mi).second, consensusParams)) + if (!ReadBlockFromDisk(*pblockRead, pindex, consensusParams)) assert(!"cannot load block from disk"); pblock = pblockRead; } @@ -1155,8 +1183,8 @@ void static ProcessGetBlockData(CNode* pfrom, const Consensus::Params& consensus // instead we respond with the full, non-compact block. bool fPeerWantsWitness = State(pfrom->GetId())->fWantsCmpctWitness; int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS; - if (CanDirectFetch(consensusParams) && mi->second->nHeight >= chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) { - if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == mi->second->GetBlockHash()) { + if (CanDirectFetch(consensusParams) && pindex->nHeight >= chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) { + if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) { connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block)); } else { CBlockHeaderAndShortTxIDs cmpctblock(*pblock, fPeerWantsWitness); @@ -1226,10 +1254,10 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } // release cs_main - if (it != pfrom->vRecvGetData.end()) { + if (it != pfrom->vRecvGetData.end() && !pfrom->fPauseSend) { const CInv &inv = *it; - it++; if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK) { + it++; ProcessGetBlockData(pfrom, consensusParams, inv, connman, interruptMsgProc); } } @@ -1296,7 +1324,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve // don't connect before giving DoS points // - Once a headers message is received that is valid and does connect, // nUnconnectingHeaders gets reset back to 0. - if (mapBlockIndex.find(headers[0].hashPrevBlock) == mapBlockIndex.end() && nCount < MAX_BLOCKS_TO_ANNOUNCE) { + if (!LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) { nodestate->nUnconnectingHeaders++; connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256())); LogPrint(BCLog::NET, "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n", @@ -1326,7 +1354,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve // If we don't have the last header, then they'll have given us // something new (if these headers are valid). - if (mapBlockIndex.find(hashLastBlock) == mapBlockIndex.end()) { + if (!LookupBlockIndex(hashLastBlock)) { received_new_header = true; } } @@ -1342,7 +1370,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve } else { LogPrint(BCLog::NET, "peer=%d: invalid header received\n", pfrom->GetId()); } - if (punish_duplicate_invalid && mapBlockIndex.find(first_invalid_header.GetHash()) != mapBlockIndex.end()) { + if (punish_duplicate_invalid && LookupBlockIndex(first_invalid_header.GetHash())) { // Goal: don't allow outbound peers to use up our outbound // connection slots if they are on incompatible chains. // @@ -1642,7 +1670,13 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr pfrom->cleanSubVer = cleanSubVer; } pfrom->nStartingHeight = nStartingHeight; - pfrom->fClient = !(nServices & NODE_NETWORK); + + // set nodes not relaying blocks and tx and not serving (parts) of the historical blockchain as "clients" + pfrom->fClient = (!(nServices & NODE_NETWORK) && !(nServices & NODE_NETWORK_LIMITED)); + + // set nodes not capable of serving the complete blockchain history as "limited nodes" + pfrom->m_limited_node = (!(nServices & NODE_NETWORK) && (nServices & NODE_NETWORK_LIMITED)); + { LOCK(pfrom->cs_filter); pfrom->fRelayTxes = fRelay; // set to true after we get the first filter* message @@ -1801,7 +1835,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // We only bother storing full nodes, though this may include // things which we would not make an outbound connection to, in // part because we may make feeler connections to them. - if (!MayHaveUsefulAddressDB(addr.nServices)) + if (!MayHaveUsefulAddressDB(addr.nServices) && !HasAllDesirableServiceFlags(addr.nServices)) continue; if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) @@ -2017,13 +2051,13 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr LOCK(cs_main); - BlockMap::iterator it = mapBlockIndex.find(req.blockhash); - if (it == mapBlockIndex.end() || !(it->second->nStatus & BLOCK_HAVE_DATA)) { + const CBlockIndex* pindex = LookupBlockIndex(req.blockhash); + if (!pindex || !(pindex->nStatus & BLOCK_HAVE_DATA)) { LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have", pfrom->GetId()); return true; } - if (it->second->nHeight < chainActive.Height() - MAX_BLOCKTXN_DEPTH) { + if (pindex->nHeight < chainActive.Height() - MAX_BLOCKTXN_DEPTH) { // If an older block is requested (should never happen in practice, // but can happen in tests) send a block response instead of a // blocktxn response. Sending a full block response instead of a @@ -2041,7 +2075,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr } CBlock block; - bool ret = ReadBlockFromDisk(block, it->second, chainparams.GetConsensus()); + bool ret = ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()); assert(ret); SendBlockTransactions(block, req, pfrom, connman); @@ -2065,10 +2099,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (locator.IsNull()) { // If locator is null, return the hashStop block - BlockMap::iterator mi = mapBlockIndex.find(hashStop); - if (mi == mapBlockIndex.end()) + pindex = LookupBlockIndex(hashStop); + if (!pindex) { return true; - pindex = (*mi).second; + } if (!BlockRequestAllowed(pindex, chainparams.GetConsensus())) { LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block header that isn't in the main chain\n", __func__, pfrom->GetId()); @@ -2307,14 +2341,14 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr { LOCK(cs_main); - if (mapBlockIndex.find(cmpctblock.header.hashPrevBlock) == mapBlockIndex.end()) { + if (!LookupBlockIndex(cmpctblock.header.hashPrevBlock)) { // Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers if (!IsInitialBlockDownload()) connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256())); return true; } - if (mapBlockIndex.find(cmpctblock.header.GetHash()) == mapBlockIndex.end()) { + if (!LookupBlockIndex(cmpctblock.header.GetHash())) { received_new_header = true; } } @@ -3296,9 +3330,8 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptM // then send all headers past that one. If we come across any // headers that aren't on chainActive, give up. for (const uint256 &hash : pto->vBlockHashesToAnnounce) { - BlockMap::iterator mi = mapBlockIndex.find(hash); - assert(mi != mapBlockIndex.end()); - const CBlockIndex *pindex = mi->second; + const CBlockIndex* pindex = LookupBlockIndex(hash); + assert(pindex); if (chainActive[pindex->nHeight] != pindex) { // Bail out if we reorged away from this block fRevertToInv = true; @@ -3389,9 +3422,8 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptM // in the past. if (!pto->vBlockHashesToAnnounce.empty()) { const uint256 &hashToAnnounce = pto->vBlockHashesToAnnounce.back(); - BlockMap::iterator mi = mapBlockIndex.find(hashToAnnounce); - assert(mi != mapBlockIndex.end()); - const CBlockIndex *pindex = mi->second; + const CBlockIndex* pindex = LookupBlockIndex(hashToAnnounce); + assert(pindex); // Warn if we're announcing a block that is not on the main chain. // This should be very rare and could be optimized out. @@ -3611,7 +3643,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptM // Message: getdata (blocks) // std::vector<CInv> vGetData; - if (!pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { + if (!pto->fClient && ((fFetch && !pto->m_limited_node) || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { std::vector<const CBlockIndex*> vToDownload; NodeId staller = -1; FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, consensusParams); diff --git a/src/net_processing.h b/src/net_processing.h index b534ef01c3..11543129cf 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -35,20 +35,33 @@ static constexpr int64_t EXTRA_PEER_CHECK_INTERVAL = 45; /** Minimum time an outbound-peer-eviction candidate must be connected for, in order to evict, in seconds */ static constexpr int64_t MINIMUM_CONNECT_TIME = 30; -class PeerLogicValidation : public CValidationInterface, public NetEventsInterface { +class PeerLogicValidation final : public CValidationInterface, public NetEventsInterface { private: CConnman* const connman; public: explicit PeerLogicValidation(CConnman* connman, CScheduler &scheduler); + /** + * Overridden from CValidationInterface. + */ void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected, const std::vector<CTransactionRef>& vtxConflicted) override; + /** + * Overridden from CValidationInterface. + */ void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override; + /** + * Overridden from CValidationInterface. + */ void BlockChecked(const CBlock& block, const CValidationState& state) override; + /** + * Overridden from CValidationInterface. + */ void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) override; - + /** Initialize a peer by adding it to mapNodeState and pushing a message requesting its version */ void InitializeNode(CNode* pnode) override; + /** Handle removal of a peer by updating various state and removing it from mapNodeState */ void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) override; /** Process protocol messages received from a given node */ bool ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt) override; @@ -61,8 +74,11 @@ public: */ bool SendMessages(CNode* pto, std::atomic<bool>& interrupt) override; + /** Consider evicting an outbound peer based on the amount of time they've been behind our tip */ void ConsiderEviction(CNode *pto, int64_t time_in_seconds); + /** Evict extra outbound peers. If we think our tip may be stale, connect to an extra outbound */ void CheckForStaleTipAndEvictPeers(const Consensus::Params &consensusParams); + /** If we have extra outbound peers, try to disconnect the one with the oldest block announcement */ void EvictExtraOutboundPeers(int64_t time_in_seconds); private: diff --git a/src/netbase.cpp b/src/netbase.cpp index 5be3fe34f8..3ea3141d5e 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -139,7 +139,7 @@ bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault, if (pszName[0] == 0) return false; int port = portDefault; - std::string hostname = ""; + std::string hostname; SplitHostPort(std::string(pszName), port, hostname); std::vector<CNetAddr> vIP; diff --git a/src/prevector.h b/src/prevector.h index f8d6a09145..103ead82cc 100644 --- a/src/prevector.h +++ b/src/prevector.h @@ -10,9 +10,12 @@ #include <stdint.h> #include <string.h> +#include <cstddef> #include <iterator> #include <type_traits> +#include <compat.h> + #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 @@ -194,16 +197,42 @@ private: T* item_ptr(difference_type pos) { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); } const T* item_ptr(difference_type pos) const { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); } + void fill(T* dst, ptrdiff_t count) { + if (IS_TRIVIALLY_CONSTRUCTIBLE<T>::value) { + // The most common use of prevector is where T=unsigned char. For + // trivially constructible types, we can use memset() to avoid + // looping. + ::memset(dst, 0, count * sizeof(T)); + } else { + for (auto i = 0; i < count; ++i) { + new(static_cast<void*>(dst + i)) T(); + } + } + } + + void fill(T* dst, ptrdiff_t count, const T& value) { + for (auto i = 0; i < count; ++i) { + new(static_cast<void*>(dst + i)) T(value); + } + } + + template<typename InputIterator> + void fill(T* dst, InputIterator first, InputIterator last) { + while (first != last) { + new(static_cast<void*>(dst)) T(*first); + ++dst; + ++first; + } + } + public: void assign(size_type n, const T& val) { clear(); if (capacity() < n) { change_capacity(n); } - while (size() < n) { - _size++; - new(static_cast<void*>(item_ptr(size() - 1))) T(val); - } + _size += n; + fill(item_ptr(0), n, val); } template<typename InputIterator> @@ -213,11 +242,8 @@ public: if (capacity() < n) { change_capacity(n); } - while (first != last) { - _size++; - new(static_cast<void*>(item_ptr(size() - 1))) T(*first); - ++first; - } + _size += n; + fill(item_ptr(0), first, last); } prevector() : _size(0), _union{{}} {} @@ -228,31 +254,23 @@ public: explicit prevector(size_type n, const T& val = T()) : _size(0) { change_capacity(n); - while (size() < n) { - _size++; - new(static_cast<void*>(item_ptr(size() - 1))) T(val); - } + _size += n; + fill(item_ptr(0), n, val); } template<typename InputIterator> prevector(InputIterator first, InputIterator last) : _size(0) { size_type n = last - first; change_capacity(n); - while (first != last) { - _size++; - new(static_cast<void*>(item_ptr(size() - 1))) T(*first); - ++first; - } + _size += n; + fill(item_ptr(0), first, last); } prevector(const prevector<N, T, Size, Diff>& other) : _size(0) { - change_capacity(other.size()); - const_iterator it = other.begin(); - while (it != other.end()) { - _size++; - new(static_cast<void*>(item_ptr(size() - 1))) T(*it); - ++it; - } + size_type n = other.size(); + change_capacity(n); + _size += n; + fill(item_ptr(0), other.begin(), other.end()); } prevector(prevector<N, T, Size, Diff>&& other) : _size(0) { @@ -263,14 +281,7 @@ public: if (&other == this) { return *this; } - resize(0); - change_capacity(other.size()); - const_iterator it = other.begin(); - while (it != other.end()) { - _size++; - new(static_cast<void*>(item_ptr(size() - 1))) T(*it); - ++it; - } + assign(other.begin(), other.end()); return *this; } @@ -314,16 +325,20 @@ public: } void resize(size_type new_size) { - if (size() > new_size) { + size_type cur_size = size(); + if (cur_size == new_size) { + return; + } + if (cur_size > new_size) { erase(item_ptr(new_size), end()); + return; } if (new_size > capacity()) { change_capacity(new_size); } - while (size() < new_size) { - _size++; - new(static_cast<void*>(item_ptr(size() - 1))) T(); - } + ptrdiff_t increase = new_size - cur_size; + fill(item_ptr(cur_size), increase); + _size += increase; } void reserve(size_type new_capacity) { @@ -346,10 +361,11 @@ public: if (capacity() < new_size) { change_capacity(new_size + (new_size >> 1)); } - memmove(item_ptr(p + 1), item_ptr(p), (size() - p) * sizeof(T)); + T* ptr = item_ptr(p); + memmove(ptr + 1, ptr, (size() - p) * sizeof(T)); _size++; - new(static_cast<void*>(item_ptr(p))) T(value); - return iterator(item_ptr(p)); + new(static_cast<void*>(ptr)) T(value); + return iterator(ptr); } void insert(iterator pos, size_type count, const T& value) { @@ -358,11 +374,10 @@ public: if (capacity() < new_size) { change_capacity(new_size + (new_size >> 1)); } - memmove(item_ptr(p + count), item_ptr(p), (size() - p) * sizeof(T)); + T* ptr = item_ptr(p); + memmove(ptr + count, ptr, (size() - p) * sizeof(T)); _size += count; - for (size_type i = 0; i < count; i++) { - new(static_cast<void*>(item_ptr(p + i))) T(value); - } + fill(item_ptr(p), count, value); } template<typename InputIterator> @@ -373,13 +388,10 @@ public: if (capacity() < new_size) { change_capacity(new_size + (new_size >> 1)); } - memmove(item_ptr(p + count), item_ptr(p), (size() - p) * sizeof(T)); + T* ptr = item_ptr(p); + memmove(ptr + count, ptr, (size() - p) * sizeof(T)); _size += count; - while (first != last) { - new(static_cast<void*>(item_ptr(p))) T(*first); - ++p; - ++first; - } + fill(ptr, first, last); } iterator erase(iterator pos) { diff --git a/src/protocol.cpp b/src/protocol.cpp index c412ad9ffe..2ec26fbd3e 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -12,6 +12,8 @@ # include <arpa/inet.h> #endif +static std::atomic<bool> g_initial_block_download_completed(false); + namespace NetMsgType { const char *VERSION="version"; const char *VERACK="verack"; @@ -127,6 +129,17 @@ bool CMessageHeader::IsValid(const MessageStartChars& pchMessageStartIn) const } +ServiceFlags GetDesirableServiceFlags(ServiceFlags services) { + if ((services & NODE_NETWORK_LIMITED) && g_initial_block_download_completed) { + return ServiceFlags(NODE_NETWORK_LIMITED | NODE_WITNESS); + } + return ServiceFlags(NODE_NETWORK | NODE_WITNESS); +} + +void SetServiceFlagsIBDCache(bool state) { + g_initial_block_download_completed = state; +} + CAddress::CAddress() : CService() { diff --git a/src/protocol.h b/src/protocol.h index 42eb57e4f0..e518d11944 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -15,6 +15,7 @@ #include <uint256.h> #include <version.h> +#include <atomic> #include <stdint.h> #include <string> @@ -301,9 +302,10 @@ enum ServiceFlags : uint64_t { * If the NODE_NONE return value is changed, contrib/seeds/makeseeds.py * should be updated appropriately to filter for the same nodes. */ -static ServiceFlags GetDesirableServiceFlags(ServiceFlags services) { - return ServiceFlags(NODE_NETWORK | NODE_WITNESS); -} +ServiceFlags GetDesirableServiceFlags(ServiceFlags services); + +/** Set the current IBD status in order to figure out the desirable service flags */ +void SetServiceFlagsIBDCache(bool status); /** * A shortcut for (services & GetDesirableServiceFlags(services)) @@ -316,10 +318,10 @@ static inline bool HasAllDesirableServiceFlags(ServiceFlags services) { /** * Checks if a peer with the given service flags may be capable of having a - * robust address-storage DB. Currently an alias for checking NODE_NETWORK. + * robust address-storage DB. */ static inline bool MayHaveUsefulAddressDB(ServiceFlags services) { - return services & NODE_NETWORK; + return (services & NODE_NETWORK) || (services & NODE_NETWORK_LIMITED); } /** A CService with information about it as peer */ diff --git a/src/qt/README.md b/src/qt/README.md new file mode 100644 index 0000000000..7ffea98170 --- /dev/null +++ b/src/qt/README.md @@ -0,0 +1,95 @@ +This directory contains the BitcoinQT graphical user interface (GUI). It uses the cross platform framework [QT](https://www1.qt.io/developers/). + +The current precise version for QT 5 is specified in [qt.mk](/depends/packages/qt.mk). QT 4 is also supported (see [#8263](https://github.com/bitcoin/bitcoin/issues/8263)). + +## Compile and run + +See build instructions ([OSX](/doc/build-osx.md), [Windows](/doc/build-windows.md), [Unix](/doc/build-unix.md), etc). + +To run: + +```sh +./src/qt/bitcoin-qt +``` + +## Files and directories + +### forms + +Contains [Designer UI](http://doc.qt.io/qt-5.9/designer-using-a-ui-file.html) files. They are created with [Qt Creator](#use-qt-Creator-as IDE), but can be edited using any text editor. + +### locale + +Contains translations. They are periodically updated. The process is described [here](/doc/translation_process.md). + +### res + +Resources such as the icon. + +### test + +Tests. + +### bitcoingui.(h/cpp) + +Represents the main window of the Bitcoin UI. + +### \*model.(h/cpp) + +The model. When it has a corresponding controller, it generally inherits from [QAbstractTableModel](http://doc.qt.io/qt-5/qabstracttablemodel.html). Models that are used by controllers as helpers inherit from other QT classes like [QValidator](http://doc.qt.io/qt-5/qvalidator.html). + +ClientModel is used by the main application `bitcoingui` and several models like `peertablemodel`. + +### \*page.(h/cpp) + +A controller. `:NAMEpage.cpp` generally includes `:NAMEmodel.h` and `forms/:NAME.page.ui` with a similar `:NAME`. + +### \*dialog.(h/cpp) + +Various dialogs, e.g. to open a URL. Inherit from [QDialog](http://doc.qt.io/qt-4.8/qdialog.html). + +### paymentserver.(h/cpp) + +Used to process BIP21 and BIP70 (see https://github.com/bitcoin/bitcoin/pull/11622) payment URI / requests. Also handles URI based application switching (e.g. when following a bitcoin:... link from a browser). + +### walletview.(h/cpp) + +Represents the view to a single wallet. + +### Other .h/cpp files + +* UI elements like BitcoinAmountField, which inherit from QWidget. +* `bitcoinstrings.cpp`: automatically generated +* `bitcoinunits.(h/cpp)`: BTC / mBTC / etc handling +* `callback.h` +* `guiconstants.h`: UI colors, app name, etc +* `guiutil.h`: several helper functions +* `macdockiconhandler.(h/cpp)` +* `macdockiconhandler.(h/cpp)`: display notifications in OSX + +## Contribute + +See [CONTRIBUTING.md](/CONTRIBUTING.md) for general guidelines. Specifically for QT: + +* don't change `local/bitcoin_en.ts`; this happens [automatically](/doc/translation_process.md#writing-code-with-translations) + +## Using Qt Creator as IDE + +You can use Qt Creator as an IDE. This is especially useful if you want to change +the UI layout. + +Download and install the community edition of [Qt Creator](https://www.qt.io/download/). +Uncheck everything except Qt Creator during the installation process. + +Instructions for OSX: + +1. Make sure you installed everything through Homebrew mentioned in the [OSX build instructions](/docs/build-osx.md) +2. Use `./configure` with the `--enable-debug` flag +3. In Qt Creator do "New Project" -> Import Project -> Import Existing Project +4. Enter "bitcoin-qt" as project name, enter src/qt as location +5. Leave the file selection as it is +6. Confirm the "summary page" +7. In the "Projects" tab select "Manage Kits..." +8. Select the default "Desktop" kit and select "Clang (x86 64bit in /usr/bin)" as compiler +9. Select LLDB as debugger (you might need to set the path to your installation) +10. Start debugging with Qt Creator (you might need to the executable to "bitcoin-qt" under "Run", which is where you can also add command line arguments) diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 74f0db3520..4f9a79d654 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -7,10 +7,9 @@ #include <qt/guiutil.h> #include <qt/walletmodel.h> -#include <base58.h> +#include <key_io.h> #include <wallet/wallet.h> - #include <QFont> #include <QDebug> @@ -393,11 +392,8 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con } // Add entry - { - LOCK(wallet->cs_wallet); - wallet->SetAddressBook(DecodeDestination(strAddress), strLabel, - (type == Send ? "send" : "receive")); - } + wallet->SetAddressBook(DecodeDestination(strAddress), strLabel, + (type == Send ? "send" : "receive")); return QString::fromStdString(strAddress); } @@ -411,10 +407,7 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent // Also refuse to remove receiving addresses. return false; } - { - LOCK(wallet->cs_wallet); - wallet->DelAddressBook(DecodeDestination(rec->address.toStdString())); - } + wallet->DelAddressBook(DecodeDestination(rec->address.toStdString())); return true; } diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index e4c088d379..ab381bfb5d 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -29,6 +29,7 @@ #include <init.h> #include <rpc/server.h> #include <ui_interface.h> +#include <uint256.h> #include <util.h> #include <warnings.h> @@ -80,6 +81,7 @@ Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin); // Declare meta types used for QMetaObject::invokeMethod Q_DECLARE_METATYPE(bool*) Q_DECLARE_METATYPE(CAmount) +Q_DECLARE_METATYPE(uint256) static void InitMessage(const std::string &message) { @@ -706,7 +708,7 @@ int main(int argc, char *argv[]) if (BitcoinCore::baseInitialize()) { app.requestInitialize(); #if defined(Q_OS_WIN) && QT_VERSION >= 0x050000 - WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely...").arg(QObject::tr(PACKAGE_NAME)), static_cast<HWND>(app.getMainWinId())); + WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely...").arg(QObject::tr(PACKAGE_NAME)), (HWND)app.getMainWinId()); #endif app.exec(); app.requestShutdown(); diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp index 395ab447d2..6a76358a78 100644 --- a/src/qt/bitcoinaddressvalidator.cpp +++ b/src/qt/bitcoinaddressvalidator.cpp @@ -4,7 +4,7 @@ #include <qt/bitcoinaddressvalidator.h> -#include <base58.h> +#include <key_io.h> /* Base58 characters are: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 4e868b7c17..427eb95a84 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -923,6 +923,7 @@ void BitcoinGUI::message(const QString &title, const QString &message, unsigned showNormalIfMinimized(); QMessageBox mBox(static_cast<QMessageBox::Icon>(nMBoxIcon), strTitle, message, buttons, this); + mBox.setTextFormat(Qt::PlainText); int r = mBox.exec(); if (ret != nullptr) *ret = r == QMessageBox::Ok; diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 3642e5d4d4..eaf2896bc3 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -138,9 +138,9 @@ size_t ClientModel::getMempoolDynamicUsage() const double ClientModel::getVerificationProgress(const CBlockIndex *tipIn) const { CBlockIndex *tip = const_cast<CBlockIndex *>(tipIn); + LOCK(cs_main); if (!tip) { - LOCK(cs_main); tip = chainActive.Tip(); } return GuessVerificationProgress(Params().TxData(), tip); diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 8d2e5619e0..b83755ab30 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -15,6 +15,7 @@ #include <wallet/coincontrol.h> #include <init.h> +#include <key_io.h> #include <policy/fees.h> #include <policy/policy.h> #include <validation.h> // For mempool diff --git a/src/qt/forms/modaloverlay.ui b/src/qt/forms/modaloverlay.ui index fdc52dc455..b5a69c578d 100644 --- a/src/qt/forms/modaloverlay.ui +++ b/src/qt/forms/modaloverlay.ui @@ -351,6 +351,12 @@ QLabel { color: rgb(40,40,40); }</string> <property name="text"> <string>Hide</string> </property> + <property name="focusPolicy"> + <enum>Qt::StrongFocus</enum> + </property> + <property name="default"> + <bool>true</bool> + </property> </widget> </item> </layout> diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 195a5560f7..6b31ddea90 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -848,7 +848,9 @@ <item> <widget class="QLabel" name="labelCustomPerKilobyte"> <property name="toolTip"> - <string>If the custom fee is set to 1000 satoshis and the transaction is only 250 bytes, then "per kilobyte" only pays 250 satoshis in fee, while "total at least" pays 1000 satoshis. For transactions bigger than a kilobyte both pay by kilobyte.</string> + <string>Specify a custom fee per kB (1,000 bytes) of the transaction's virtual size. + +Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis per kB" for a transaction size of 500 bytes (half of 1 kB) would ultimately yield a fee of only 50 satoshis.</string> </property> <property name="text"> <string>per kilobyte</string> diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 558d4f108c..7c3c68bfef 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -9,7 +9,10 @@ #include <qt/qvalidatedlineedit.h> #include <qt/walletmodel.h> +#include <base58.h> +#include <chainparams.h> #include <primitives/transaction.h> +#include <key_io.h> #include <init.h> #include <policy/policy.h> #include <protocol.h> @@ -101,7 +104,7 @@ QFont fixedPitchFont() #endif } -// Just some dummy data to generate an convincing random-looking (but consistent) address +// Just some dummy data to generate a convincing random-looking (but consistent) address static const uint8_t dummydata[] = {0xeb,0x15,0x23,0x1d,0xfc,0xeb,0x60,0x92,0x58,0x86,0xb6,0x7d,0x06,0x52,0x99,0x92,0x59,0x15,0xae,0xb1,0x72,0xc0,0x66,0x47}; // Generate a dummy address with invalid CRC, starting with the network prefix. @@ -417,7 +420,7 @@ void openDebugLogfile() bool openBitcoinConf() { - boost::filesystem::path pathConfig = GetConfigFile(BITCOIN_CONF_FILENAME); + boost::filesystem::path pathConfig = GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)); /* Create the file */ boost::filesystem::ofstream configFile(pathConfig, std::ios_base::app); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 4ade88d843..909be1c264 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -315,7 +315,12 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in break; case MapPortUPnP: // core option - can be changed on-the-fly settings.setValue("fUseUPnP", value.toBool()); - MapPort(value.toBool()); + if (value.toBool()) { + StartMapPort(); + } else { + InterruptMapPort(); + StopMapPort(); + } break; case MinimizeOnClose: fMinimizeOnClose = value.toBool(); diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp index b0ef475b35..357e98a53c 100644 --- a/src/qt/paymentrequestplus.cpp +++ b/src/qt/paymentrequestplus.cpp @@ -9,6 +9,7 @@ #include <qt/paymentrequestplus.h> +#include <script/script.h> #include <util.h> #include <stdexcept> diff --git a/src/qt/paymentrequestplus.h b/src/qt/paymentrequestplus.h index be3923304f..b1b60cf582 100644 --- a/src/qt/paymentrequestplus.h +++ b/src/qt/paymentrequestplus.h @@ -10,7 +10,8 @@ #include <qt/paymentrequest.pb.h> #pragma GCC diagnostic pop -#include <base58.h> +#include <amount.h> +#include <script/script.h> #include <openssl/x509.h> diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index bc69d4f945..4b6fdc8d57 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -8,9 +8,9 @@ #include <qt/guiutil.h> #include <qt/optionsmodel.h> -#include <base58.h> #include <chainparams.h> #include <policy/policy.h> +#include <key_io.h> #include <ui_interface.h> #include <util.h> #include <wallet/wallet.h> @@ -770,7 +770,7 @@ bool PaymentServer::verifyExpired(const payments::PaymentDetails& requestDetails { bool fVerified = (requestDetails.has_expires() && (int64_t)requestDetails.expires() < GetTime()); if (fVerified) { - const QString requestExpires = QString::fromStdString(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", (int64_t)requestDetails.expires())); + const QString requestExpires = QString::fromStdString(FormatISO8601DateTime((int64_t)requestDetails.expires())); qWarning() << QString("PaymentServer::%1: Payment request expired \"%2\".") .arg(__func__) .arg(requestExpires); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index c4b209a880..1aa4de03ca 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -70,6 +70,7 @@ const QStringList historyFilter = QStringList() << "importmulti" << "signmessagewithprivkey" << "signrawtransaction" + << "signrawtransactionwithkey" << "walletpassphrase" << "walletpassphrasechange" << "encryptwallet"; @@ -624,7 +625,7 @@ void RPCConsole::setClientModel(ClientModel *model) connect(model->getPeerTableModel(), SIGNAL(layoutChanged()), this, SLOT(peerLayoutChanged())); // peer table signal handling - cache selected node ids connect(model->getPeerTableModel(), SIGNAL(layoutAboutToBeChanged()), this, SLOT(peerLayoutAboutToChange())); - + // set up ban table ui->banlistWidget->setModel(model->getBanTableModel()); ui->banlistWidget->verticalHeader()->hide(); @@ -772,7 +773,7 @@ void RPCConsole::clear(bool clearHistory) #else QString clsKey = "Ctrl-L"; #endif - + message(CMD_REPLY, (tr("Welcome to the %1 RPC console.").arg(tr(PACKAGE_NAME)) + "<br>" + tr("Use up and down arrows to navigate history, and %1 to clear screen.").arg("<b>"+clsKey+"</b>") + "<br>" + tr("Type %1 for an overview of available commands.").arg("<b>help</b>") + "<br>" + @@ -1144,7 +1145,7 @@ void RPCConsole::disconnectSelectedNode() { if(!g_connman) return; - + // Get selected peer addresses QList<QModelIndex> nodes = GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId); for(int i = 0; i < nodes.count(); i++) @@ -1161,7 +1162,7 @@ void RPCConsole::banSelectedNode(int bantime) { if (!clientModel || !g_connman) return; - + // Get selected peer addresses QList<QModelIndex> nodes = GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId); for(int i = 0; i < nodes.count(); i++) diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 871822ccb4..ec7edd48cd 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -14,8 +14,8 @@ #include <qt/platformstyle.h> #include <qt/sendcoinsentry.h> -#include <base58.h> #include <chainparams.h> +#include <key_io.h> #include <wallet/coincontrol.h> #include <validation.h> // mempool and minRelayTxFee #include <ui_interface.h> @@ -369,12 +369,19 @@ void SendCoinsDialog::on_sendButton_clicked() accept(); CoinControlDialog::coinControl()->UnSelectAll(); coinControlUpdateLabels(); + Q_EMIT coinsSent(currentTransaction.getTransaction()->GetHash()); } fNewRecipientAllowed = true; } void SendCoinsDialog::clear() { + // Clear coin control settings + CoinControlDialog::coinControl()->UnSelectAll(); + ui->checkBoxCoinControlChange->setChecked(false); + ui->lineEditCoinControlChange->clear(); + coinControlUpdateLabels(); + // Remove entries until only one left while(ui->entries->count()) { diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 7c27785d12..48885bbcad 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -54,6 +54,9 @@ public Q_SLOTS: void setBalance(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance, const CAmount& watchOnlyBalance, const CAmount& watchUnconfBalance, const CAmount& watchImmatureBalance); +Q_SIGNALS: + void coinsSent(const uint256& txid); + private: Ui::SendCoinsDialog *ui; ClientModel *clientModel; diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index 364dcd6f45..8dade8df79 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -10,8 +10,8 @@ #include <qt/platformstyle.h> #include <qt/walletmodel.h> -#include <base58.h> #include <init.h> +#include <key_io.h> #include <validation.h> // For strMessageMagic #include <wallet/wallet.h> diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp index 6e80625123..29ef4b4c9e 100644 --- a/src/qt/test/paymentservertests.cpp +++ b/src/qt/test/paymentservertests.cpp @@ -8,6 +8,7 @@ #include <qt/test/paymentrequestdata.h> #include <amount.h> +#include <chainparams.h> #include <random.h> #include <script/script.h> #include <script/standard.h> diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index aaec15cc13..9d0e0b97d1 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -82,8 +82,8 @@ void RPCNestedTests::rpcNestedTests() QVERIFY(filtered == "signmessagewithprivkey(…)"); RPCConsole::RPCParseCommandLine(result, "signmessagewithprivkey abc,def", false, &filtered); QVERIFY(filtered == "signmessagewithprivkey(…)"); - RPCConsole::RPCParseCommandLine(result, "signrawtransaction(abc)", false, &filtered); - QVERIFY(filtered == "signrawtransaction(…)"); + RPCConsole::RPCParseCommandLine(result, "signrawtransactionwithkey(abc)", false, &filtered); + QVERIFY(filtered == "signrawtransactionwithkey(…)"); RPCConsole::RPCParseCommandLine(result, "walletpassphrase(help())", false, &filtered); QVERIFY(filtered == "walletpassphrase(…)"); RPCConsole::RPCParseCommandLine(result, "walletpassphrasechange(help(walletpassphrasechange(abc)))", false, &filtered); diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index cd49292138..976aadc0af 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -10,6 +10,7 @@ #include <qt/transactiontablemodel.h> #include <qt/transactionview.h> #include <qt/walletmodel.h> +#include <key_io.h> #include <test/test_bitcoin.h> #include <validation.h> #include <wallet/wallet.h> @@ -157,9 +158,7 @@ void TestGUI() for (int i = 0; i < 5; ++i) { test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey())); } - bitdb.MakeMock(); - std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat")); - CWallet wallet(std::move(dbw)); + CWallet wallet("mock", CWalletDBWrapper::CreateMock()); bool firstRun; wallet.LoadWallet(firstRun); { @@ -260,9 +259,6 @@ void TestGUI() QPushButton* removeRequestButton = receiveCoinsDialog.findChild<QPushButton*>("removeRequestButton"); removeRequestButton->click(); QCOMPARE(requestTableModel->rowCount({}), currentRowCount-1); - - bitdb.Flush(true); - bitdb.Reset(); } } diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index c1d28be0ab..ec5a66bc9f 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -9,14 +9,15 @@ #include <qt/paymentserver.h> #include <qt/transactionrecord.h> -#include <base58.h> #include <consensus/consensus.h> +#include <key_io.h> #include <validation.h> #include <script/script.h> #include <timedata.h> #include <util.h> #include <wallet/db.h> #include <wallet/wallet.h> +#include <policy/policy.h> #include <stdint.h> #include <string> @@ -239,8 +240,9 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco if (wtx.mapValue.count("comment") && !wtx.mapValue["comment"].empty()) strHTML += "<br><b>" + tr("Comment") + ":</b><br>" + GUIUtil::HtmlEscape(wtx.mapValue["comment"], true) + "<br>"; - strHTML += "<b>" + tr("Transaction ID") + ":</b> " + rec->getTxID() + "<br>"; + strHTML += "<b>" + tr("Transaction ID") + ":</b> " + rec->getTxHash() + "<br>"; strHTML += "<b>" + tr("Transaction total size") + ":</b> " + QString::number(wtx.tx->GetTotalSize()) + " bytes<br>"; + strHTML += "<b>" + tr("Transaction virtual size") + ":</b> " + QString::number(GetVirtualTransactionSize(*wtx.tx)) + " bytes<br>"; strHTML += "<b>" + tr("Output index") + ":</b> " + QString::number(rec->getOutputIndex()) + "<br>"; // Message from normal bitcoin:URI (bitcoin:123...?message=example) diff --git a/src/qt/transactiondescdialog.cpp b/src/qt/transactiondescdialog.cpp index 161fccd462..7bf4d3351c 100644 --- a/src/qt/transactiondescdialog.cpp +++ b/src/qt/transactiondescdialog.cpp @@ -14,7 +14,7 @@ TransactionDescDialog::TransactionDescDialog(const QModelIndex &idx, QWidget *pa ui(new Ui::TransactionDescDialog) { ui->setupUi(this); - setWindowTitle(tr("Details for %1").arg(idx.data(TransactionTableModel::TxIDRole).toString())); + setWindowTitle(tr("Details for %1").arg(idx.data(TransactionTableModel::TxHashRole).toString())); QString desc = idx.data(TransactionTableModel::LongDescriptionRole).toString(); ui->detailText->setHtml(desc); } diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp index 39d03fa547..a702461f7a 100644 --- a/src/qt/transactionfilterproxy.cpp +++ b/src/qt/transactionfilterproxy.cpp @@ -36,7 +36,7 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex & bool involvesWatchAddress = index.data(TransactionTableModel::WatchonlyRole).toBool(); QString address = index.data(TransactionTableModel::AddressRole).toString(); QString label = index.data(TransactionTableModel::LabelRole).toString(); - QString txid = index.data(TransactionTableModel::TxIDRole).toString(); + QString txid = index.data(TransactionTableModel::TxHashRole).toString(); qint64 amount = llabs(index.data(TransactionTableModel::AmountRole).toLongLong()); int status = index.data(TransactionTableModel::StatusRole).toInt(); diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index de3e885e8f..cc30cf747d 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -4,8 +4,8 @@ #include <qt/transactionrecord.h> -#include <base58.h> #include <consensus/consensus.h> +#include <key_io.h> #include <validation.h> #include <timedata.h> #include <wallet/wallet.h> @@ -167,10 +167,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) // Determine transaction status // Find the block the tx is in - CBlockIndex* pindex = nullptr; - BlockMap::iterator mi = mapBlockIndex.find(wtx.hashBlock); - if (mi != mapBlockIndex.end()) - pindex = (*mi).second; + const CBlockIndex* pindex = LookupBlockIndex(wtx.hashBlock); // Sort order, unrecorded transactions sort to the top status.sortKey = strprintf("%010d-%01d-%010u-%03d", @@ -254,7 +251,7 @@ bool TransactionRecord::statusUpdateNeeded() const return status.cur_num_blocks != chainActive.Height() || status.needsUpdate; } -QString TransactionRecord::getTxID() const +QString TransactionRecord::getTxHash() const { return QString::fromStdString(hash.ToString()); } diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index 29a3cd8de7..5321d05d15 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -129,7 +129,7 @@ public: bool involvesWatchAddress; /** Return the unique identifier for this transaction (part) */ - QString getTxID() const; + QString getTxHash() const; /** Return the output index of the subtransaction */ int getOutputIndex() const; diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 626d4c0bdc..84800125fe 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -615,10 +615,8 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(rec->address)); case AmountRole: return qint64(rec->credit + rec->debit); - case TxIDRole: - return rec->getTxID(); case TxHashRole: - return QString::fromStdString(rec->hash.ToString()); + return rec->getTxHash(); case TxHexRole: return priv->getTxHex(rec); case TxPlainTextRole: diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 8f58962d17..781874d160 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -56,8 +56,6 @@ public: LabelRole, /** Net amount of transaction */ AmountRole, - /** Unique identifier */ - TxIDRole, /** Transaction hash */ TxHashRole, /** Transaction data, hex-encoded */ diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 88f8f463bc..26391452da 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -263,8 +263,7 @@ void TransactionView::setModel(WalletModel *_model) void TransactionView::chooseDate(int idx) { - if(!transactionProxyModel) - return; + if (!transactionProxyModel) return; QDate current = QDate::currentDate(); dateRangeWidget->setVisible(false); switch(dateWidget->itemData(idx).toInt()) @@ -372,7 +371,7 @@ void TransactionView::exportClicked() writer.addColumn(tr("Label"), 0, TransactionTableModel::LabelRole); writer.addColumn(tr("Address"), 0, TransactionTableModel::AddressRole); writer.addColumn(BitcoinUnits::getAmountColumnTitle(model->getOptionsModel()->getDisplayUnit()), 0, TransactionTableModel::FormattedAmountRole); - writer.addColumn(tr("ID"), 0, TransactionTableModel::TxIDRole); + writer.addColumn(tr("ID"), 0, TransactionTableModel::TxHashRole); if(!writer.write()) { Q_EMIT message(tr("Exporting Failed"), tr("There was an error trying to save the transaction history to %1.").arg(filename), @@ -456,7 +455,7 @@ void TransactionView::copyAmount() void TransactionView::copyTxID() { - GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::TxIDRole); + GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::TxHashRole); } void TransactionView::copyTxHex() @@ -592,6 +591,32 @@ void TransactionView::focusTransaction(const QModelIndex &idx) transactionView->setFocus(); } +void TransactionView::focusTransaction(const uint256& txid) +{ + if (!transactionProxyModel) + return; + + const QModelIndexList results = this->model->getTransactionTableModel()->match( + this->model->getTransactionTableModel()->index(0,0), + TransactionTableModel::TxHashRole, + QString::fromStdString(txid.ToString()), -1); + + transactionView->setFocus(); + transactionView->selectionModel()->clearSelection(); + for (const QModelIndex& index : results) { + const QModelIndex targetIndex = transactionProxyModel->mapFromSource(index); + transactionView->selectionModel()->select( + targetIndex, + QItemSelectionModel::Rows | QItemSelectionModel::Select); + // Called once per destination to ensure all results are in view, unless + // transactions are not ordered by (ascending or descending) date. + transactionView->scrollTo(targetIndex); + // scrollTo() does not scroll far enough the first time when transactions + // are ordered by ascending date. + if (index == results[0]) transactionView->scrollTo(targetIndex); + } +} + // We override the virtual resizeEvent of the QWidget to adjust tables column // sizes as the tables width is proportional to the dialogs width. void TransactionView::resizeEvent(QResizeEvent* event) diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 82e929b53f..66dc5bc86b 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -7,6 +7,8 @@ #include <qt/guiutil.h> +#include <uint256.h> + #include <QWidget> #include <QKeyEvent> @@ -116,7 +118,7 @@ public Q_SLOTS: void changedSearch(); void exportClicked(); void focusTransaction(const QModelIndex&); - + void focusTransaction(const uint256& txid); }; #endif // BITCOIN_QT_TRANSACTIONVIEW_H diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 541114e5fe..39ef20c835 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -14,8 +14,8 @@ #include <qt/sendcoinsdialog.h> #include <qt/transactiontablemodel.h> -#include <base58.h> #include <chain.h> +#include <key_io.h> #include <keystore.h> #include <validation.h> #include <net.h> // for g_connman @@ -42,6 +42,7 @@ WalletModel::WalletModel(const PlatformStyle *platformStyle, CWallet *_wallet, O transactionTableModel(0), recentRequestsTableModel(0), cachedBalance(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0), + cachedWatchOnlyBalance{0}, cachedWatchUnconfBalance{0}, cachedWatchImmatureBalance{0}, cachedEncryptionStatus(Unencrypted), cachedNumBlocks(0) { @@ -274,9 +275,9 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact int nChangePosRet = -1; std::string strFailReason; - CWalletTx *newTx = transaction.getTransaction(); + CTransactionRef& newTx = transaction.getTransaction(); CReserveKey *keyChange = transaction.getPossibleKeyChange(); - bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, nChangePosRet, strFailReason, coinControl); + bool fCreated = wallet->CreateTransaction(vecSend, newTx, *keyChange, nFeeRequired, nChangePosRet, strFailReason, coinControl); transaction.setTransactionFee(nFeeRequired); if (fSubtractFeeFromAmount && fCreated) transaction.reassignAmounts(nChangePosRet); @@ -308,8 +309,8 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran { LOCK2(cs_main, wallet->cs_wallet); - CWalletTx *newTx = transaction.getTransaction(); + std::vector<std::pair<std::string, std::string>> vOrderForm; for (const SendCoinsRecipient &rcp : transaction.getRecipients()) { if (rcp.paymentRequest.IsInitialized()) @@ -320,22 +321,22 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran } // Store PaymentRequests in wtx.vOrderForm in wallet. - std::string key("PaymentRequest"); std::string value; rcp.paymentRequest.SerializeToString(&value); - newTx->vOrderForm.push_back(make_pair(key, value)); + vOrderForm.emplace_back("PaymentRequest", std::move(value)); } else if (!rcp.message.isEmpty()) // Message from normal bitcoin:URI (bitcoin:123...?message=example) - newTx->vOrderForm.push_back(make_pair("Message", rcp.message.toStdString())); + vOrderForm.emplace_back("Message", rcp.message.toStdString()); } + CTransactionRef& newTx = transaction.getTransaction(); CReserveKey *keyChange = transaction.getPossibleKeyChange(); CValidationState state; - if(!wallet->CommitTransaction(*newTx, *keyChange, g_connman.get(), state)) + if (!wallet->CommitTransaction(newTx, {} /* mapValue */, std::move(vOrderForm), {} /* fromAccount */, *keyChange, g_connman.get(), state)) return SendCoinsReturn(TransactionCommitFailed, QString::fromStdString(state.GetRejectReason())); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); - ssTx << *newTx->tx; + ssTx << newTx; transaction_array.append(&(ssTx[0]), ssTx.size()); } diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 9e13de79be..811996b98f 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -5,6 +5,11 @@ #ifndef BITCOIN_QT_WALLETMODEL_H #define BITCOIN_QT_WALLETMODEL_H +#include <amount.h> +#include <key.h> +#include <serialize.h> +#include <script/standard.h> + #include <qt/paymentrequestplus.h> #include <qt/walletmodeltransaction.h> diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp index 4b2bef2690..4df8a5687e 100644 --- a/src/qt/walletmodeltransaction.cpp +++ b/src/qt/walletmodeltransaction.cpp @@ -12,12 +12,6 @@ WalletModelTransaction::WalletModelTransaction(const QList<SendCoinsRecipient> & walletTransaction(0), fee(0) { - walletTransaction = new CWalletTx(); -} - -WalletModelTransaction::~WalletModelTransaction() -{ - delete walletTransaction; } QList<SendCoinsRecipient> WalletModelTransaction::getRecipients() const @@ -25,14 +19,14 @@ QList<SendCoinsRecipient> WalletModelTransaction::getRecipients() const return recipients; } -CWalletTx *WalletModelTransaction::getTransaction() const +CTransactionRef& WalletModelTransaction::getTransaction() { return walletTransaction; } unsigned int WalletModelTransaction::getTransactionSize() { - return (!walletTransaction ? 0 : ::GetVirtualTransactionSize(*walletTransaction->tx)); + return (!walletTransaction ? 0 : ::GetVirtualTransactionSize(*walletTransaction)); } CAmount WalletModelTransaction::getTransactionFee() const @@ -62,7 +56,7 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet) if (out.amount() <= 0) continue; if (i == nChangePosRet) i++; - subtotal += walletTransaction->tx->vout[i].nValue; + subtotal += walletTransaction->vout[i].nValue; i++; } rcp.amount = subtotal; @@ -71,7 +65,7 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet) { if (i == nChangePosRet) i++; - rcp.amount = walletTransaction->tx->vout[i].nValue; + rcp.amount = walletTransaction->vout[i].nValue; i++; } } diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h index cd531dba4b..931e960d18 100644 --- a/src/qt/walletmodeltransaction.h +++ b/src/qt/walletmodeltransaction.h @@ -20,11 +20,10 @@ class WalletModelTransaction { public: explicit WalletModelTransaction(const QList<SendCoinsRecipient> &recipients); - ~WalletModelTransaction(); QList<SendCoinsRecipient> getRecipients() const; - CWalletTx *getTransaction() const; + CTransactionRef& getTransaction(); unsigned int getTransactionSize(); void setTransactionFee(const CAmount& newFee); @@ -39,7 +38,7 @@ public: private: QList<SendCoinsRecipient> recipients; - CWalletTx *walletTransaction; + CTransactionRef walletTransaction; std::unique_ptr<CReserveKey> keyChange; CAmount fee; }; diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 7eced9289d..64497a3431 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -68,6 +68,9 @@ WalletView::WalletView(const PlatformStyle *_platformStyle, QWidget *parent): connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), transactionView, SLOT(focusTransaction(QModelIndex))); connect(overviewPage, SIGNAL(outOfSyncWarningClicked()), this, SLOT(requestedSyncWarningInfo())); + // Highlight transaction after send + connect(sendCoinsPage, SIGNAL(coinsSent(uint256)), transactionView, SLOT(focusTransaction(uint256))); + // Double-clicking on a transaction on the transaction history page shows details connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails())); @@ -91,6 +94,9 @@ void WalletView::setBitcoinGUI(BitcoinGUI *gui) // Clicking on a transaction on the overview page simply sends you to transaction history page connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), gui, SLOT(gotoHistoryPage())); + // Navigate to transaction history page after send + connect(sendCoinsPage, SIGNAL(coinsSent(uint256)), gui, SLOT(gotoHistoryPage())); + // Receive and report messages connect(this, SIGNAL(message(QString,QString,unsigned int)), gui, SLOT(message(QString,QString,unsigned int))); diff --git a/src/qt/winshutdownmonitor.cpp b/src/qt/winshutdownmonitor.cpp index e11a72899d..1e7a76efc0 100644 --- a/src/qt/winshutdownmonitor.cpp +++ b/src/qt/winshutdownmonitor.cpp @@ -56,7 +56,7 @@ bool WinShutdownMonitor::nativeEventFilter(const QByteArray &eventType, void *pM void WinShutdownMonitor::registerShutdownBlockReason(const QString& strReason, const HWND& mainWinId) { typedef BOOL (WINAPI *PSHUTDOWNBRCREATE)(HWND, LPCWSTR); - PSHUTDOWNBRCREATE shutdownBRCreate = static_cast<PSHUTDOWNBRCREATE>(GetProcAddress(GetModuleHandleA("User32.dll"), "ShutdownBlockReasonCreate")); + PSHUTDOWNBRCREATE shutdownBRCreate = (PSHUTDOWNBRCREATE)GetProcAddress(GetModuleHandleA("User32.dll"), "ShutdownBlockReasonCreate"); if (shutdownBRCreate == nullptr) { qWarning() << "registerShutdownBlockReason: GetProcAddress for ShutdownBlockReasonCreate failed"; return; diff --git a/src/rest.cpp b/src/rest.cpp index eeeb3f5141..f47b40343b 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -90,7 +90,7 @@ static enum RetFormat ParseDataFormat(std::string& param, const std::string& str static std::string AvailableDataFormatsString() { - std::string formats = ""; + std::string formats; for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++) if (strlen(rf_names[i].name) > 0) { formats.append("."); @@ -147,8 +147,7 @@ static bool rest_headers(HTTPRequest* req, headers.reserve(count); { LOCK(cs_main); - BlockMap::const_iterator it = mapBlockIndex.find(hash); - const CBlockIndex *pindex = (it != mapBlockIndex.end()) ? it->second : nullptr; + const CBlockIndex* pindex = LookupBlockIndex(hash); while (pindex != nullptr && chainActive.Contains(pindex)) { headers.push_back(pindex); if (headers.size() == (unsigned long)count) @@ -212,10 +211,11 @@ static bool rest_block(HTTPRequest* req, CBlockIndex* pblockindex = nullptr; { LOCK(cs_main); - if (mapBlockIndex.count(hash) == 0) + pblockindex = LookupBlockIndex(hash); + if (!pblockindex) { return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); + } - pblockindex = mapBlockIndex[hash]; if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)"); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 189da6ae48..2077723af5 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -372,6 +372,9 @@ std::string EntryDescriptionString() " \"wtxid\" : hash, (string) hash of serialized transaction, including witness data\n" " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n" " \"transactionid\", (string) parent transaction id\n" + " ... ]\n" + " \"spentby\" : [ (array) unconfirmed transactions spending outputs from this transaction\n" + " \"transactionid\", (string) child transaction id\n" " ... ]\n"; } @@ -406,6 +409,15 @@ void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) } info.pushKV("depends", depends); + + UniValue spent(UniValue::VARR); + const CTxMemPool::txiter &it = mempool.mapTx.find(tx.GetHash()); + const CTxMemPool::setEntries &setChildren = mempool.GetMemPoolChildren(it); + for (const CTxMemPool::txiter &childiter : setChildren) { + spent.push_back(childiter->GetTx().GetHash().ToString()); + } + + info.pushKV("spentby", spent); } UniValue mempoolToJSON(bool fVerbose) @@ -697,10 +709,10 @@ UniValue getblockheader(const JSONRPCRequest& request) if (!request.params[1].isNull()) fVerbose = request.params[1].get_bool(); - if (mapBlockIndex.count(hash) == 0) + const CBlockIndex* pblockindex = LookupBlockIndex(hash); + if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - - CBlockIndex* pblockindex = mapBlockIndex[hash]; + } if (!fVerbose) { @@ -776,12 +788,12 @@ UniValue getblock(const JSONRPCRequest& request) verbosity = request.params[1].get_bool() ? 1 : 0; } - if (mapBlockIndex.count(hash) == 0) + const CBlockIndex* pblockindex = LookupBlockIndex(hash); + if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + } CBlock block; - CBlockIndex* pblockindex = mapBlockIndex[hash]; - if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)"); @@ -846,7 +858,7 @@ static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats) stats.hashBlock = pcursor->GetBestBlock(); { LOCK(cs_main); - stats.nHeight = mapBlockIndex.find(stats.hashBlock)->second->nHeight; + stats.nHeight = LookupBlockIndex(stats.hashBlock)->nHeight; } ss << stats.hashBlock; uint256 prevkey; @@ -1029,8 +1041,7 @@ UniValue gettxout(const JSONRPCRequest& request) } } - BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); - CBlockIndex *pindex = it->second; + const CBlockIndex* pindex = LookupBlockIndex(pcoinsTip->GetBestBlock()); ret.pushKV("bestblock", pindex->GetBlockHash().GetHex()); if (coin.nHeight == MEMPOOL_HEIGHT) { ret.pushKV("confirmations", 0); @@ -1424,17 +1435,17 @@ UniValue preciousblock(const JSONRPCRequest& request) { LOCK(cs_main); - if (mapBlockIndex.count(hash) == 0) + pblockindex = LookupBlockIndex(hash); + if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - - pblockindex = mapBlockIndex[hash]; + } } CValidationState state; PreciousBlock(state, Params(), pblockindex); if (!state.IsValid()) { - throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); + throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state)); } return NullUniValue; @@ -1460,10 +1471,11 @@ UniValue invalidateblock(const JSONRPCRequest& request) { LOCK(cs_main); - if (mapBlockIndex.count(hash) == 0) + CBlockIndex* pblockindex = LookupBlockIndex(hash); + if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + } - CBlockIndex* pblockindex = mapBlockIndex[hash]; InvalidateBlock(state, Params(), pblockindex); } @@ -1472,7 +1484,7 @@ UniValue invalidateblock(const JSONRPCRequest& request) } if (!state.IsValid()) { - throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); + throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state)); } return NullUniValue; @@ -1498,10 +1510,11 @@ UniValue reconsiderblock(const JSONRPCRequest& request) { LOCK(cs_main); - if (mapBlockIndex.count(hash) == 0) + CBlockIndex* pblockindex = LookupBlockIndex(hash); + if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + } - CBlockIndex* pblockindex = mapBlockIndex[hash]; ResetBlockFailureFlags(pblockindex); } @@ -1509,7 +1522,7 @@ UniValue reconsiderblock(const JSONRPCRequest& request) ActivateBestChain(state, Params()); if (!state.IsValid()) { - throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); + throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state)); } return NullUniValue; @@ -1542,28 +1555,21 @@ UniValue getchaintxstats(const JSONRPCRequest& request) const CBlockIndex* pindex; int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month - bool havehash = !request.params[1].isNull(); - uint256 hash; - if (havehash) { - hash = uint256S(request.params[1].get_str()); - } - - { + if (request.params[1].isNull()) { LOCK(cs_main); - if (havehash) { - auto it = mapBlockIndex.find(hash); - if (it == mapBlockIndex.end()) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - } - pindex = it->second; - if (!chainActive.Contains(pindex)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Block is not in main chain"); - } - } else { - pindex = chainActive.Tip(); + pindex = chainActive.Tip(); + } else { + uint256 hash = uint256S(request.params[1].get_str()); + LOCK(cs_main); + pindex = LookupBlockIndex(hash); + if (!pindex) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + } + if (!chainActive.Contains(pindex)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Block is not in main chain"); } } - + assert(pindex != nullptr); if (request.params[0].isNull()) { diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 99c1242d8a..0eeb3f98b3 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -43,6 +43,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "listreceivedbyaddress", 0, "minconf" }, { "listreceivedbyaddress", 1, "include_empty" }, { "listreceivedbyaddress", 2, "include_watchonly" }, + { "listreceivedbyaddress", 3, "address_filter" }, { "listreceivedbyaccount", 0, "minconf" }, { "listreceivedbyaccount", 1, "include_empty" }, { "listreceivedbyaccount", 2, "include_watchonly" }, @@ -94,6 +95,9 @@ static const CRPCConvertParam vRPCConvertParams[] = { "decoderawtransaction", 1, "iswitness" }, { "signrawtransaction", 1, "prevtxs" }, { "signrawtransaction", 2, "privkeys" }, + { "signrawtransactionwithkey", 1, "privkeys" }, + { "signrawtransactionwithkey", 2, "prevtxs" }, + { "signrawtransactionwithwallet", 1, "prevtxs" }, { "sendrawtransaction", 1, "allowhighfees" }, { "combinerawtransaction", 0, "txs" }, { "fundrawtransaction", 1, "options" }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index dd74095b62..0537628763 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -3,7 +3,6 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <base58.h> #include <amount.h> #include <chain.h> #include <chainparams.h> @@ -13,6 +12,7 @@ #include <core_io.h> #include <init.h> #include <validation.h> +#include <key_io.h> #include <miner.h> #include <net.h> #include <policy/fees.h> @@ -264,11 +264,11 @@ static UniValue BIP22ValidationResult(const CValidationState& state) if (state.IsValid()) return NullUniValue; - std::string strRejectReason = state.GetRejectReason(); if (state.IsError()) - throw JSONRPCError(RPC_VERIFY_ERROR, strRejectReason); + throw JSONRPCError(RPC_VERIFY_ERROR, FormatStateMessage(state)); if (state.IsInvalid()) { + std::string strRejectReason = state.GetRejectReason(); if (strRejectReason.empty()) return "rejected"; return strRejectReason; @@ -396,9 +396,8 @@ UniValue getblocktemplate(const JSONRPCRequest& request) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); uint256 hash = block.GetHash(); - BlockMap::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) { - CBlockIndex *pindex = mi->second; + const CBlockIndex* pindex = LookupBlockIndex(hash); + if (pindex) { if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) return "duplicate"; if (pindex->nStatus & BLOCK_FAILED_MASK) @@ -727,9 +726,8 @@ UniValue submitblock(const JSONRPCRequest& request) bool fBlockPresent = false; { LOCK(cs_main); - BlockMap::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) { - CBlockIndex *pindex = mi->second; + const CBlockIndex* pindex = LookupBlockIndex(hash); + if (pindex) { if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) { return "duplicate"; } @@ -743,9 +741,9 @@ UniValue submitblock(const JSONRPCRequest& request) { LOCK(cs_main); - BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); - if (mi != mapBlockIndex.end()) { - UpdateUncommittedBlockStructures(block, mi->second, Params().GetConsensus()); + const CBlockIndex* pindex = LookupBlockIndex(block.hashPrevBlock); + if (pindex) { + UpdateUncommittedBlockStructures(block, pindex, Params().GetConsensus()); } } diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 3f89996e61..49e865a64a 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -3,12 +3,12 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <base58.h> #include <chain.h> #include <clientversion.h> #include <core_io.h> #include <crypto/ripemd160.h> #include <init.h> +#include <key_io.h> #include <validation.h> #include <httpserver.h> #include <net.h> @@ -33,175 +33,33 @@ #include <univalue.h> -#ifdef ENABLE_WALLET -class DescribeAddressVisitor : public boost::static_visitor<UniValue> -{ -public: - CWallet * const pwallet; - - explicit DescribeAddressVisitor(CWallet *_pwallet) : pwallet(_pwallet) {} - - void ProcessSubScript(const CScript& subscript, UniValue& obj, bool include_addresses = false) const - { - // Always present: script type and redeemscript - txnouttype which_type; - std::vector<std::vector<unsigned char>> solutions_data; - Solver(subscript, which_type, solutions_data); - obj.pushKV("script", GetTxnOutputType(which_type)); - obj.pushKV("hex", HexStr(subscript.begin(), subscript.end())); - - CTxDestination embedded; - UniValue a(UniValue::VARR); - if (ExtractDestination(subscript, embedded)) { - // Only when the script corresponds to an address. - UniValue subobj = boost::apply_visitor(*this, embedded); - subobj.pushKV("address", EncodeDestination(embedded)); - subobj.pushKV("scriptPubKey", HexStr(subscript.begin(), subscript.end())); - // Always report the pubkey at the top level, so that `getnewaddress()['pubkey']` always works. - if (subobj.exists("pubkey")) obj.pushKV("pubkey", subobj["pubkey"]); - obj.pushKV("embedded", std::move(subobj)); - if (include_addresses) a.push_back(EncodeDestination(embedded)); - } else if (which_type == TX_MULTISIG) { - // Also report some information on multisig scripts (which do not have a corresponding address). - // TODO: abstract out the common functionality between this logic and ExtractDestinations. - obj.pushKV("sigsrequired", solutions_data[0][0]); - UniValue pubkeys(UniValue::VARR); - for (size_t i = 1; i < solutions_data.size() - 1; ++i) { - CPubKey key(solutions_data[i].begin(), solutions_data[i].end()); - if (include_addresses) a.push_back(EncodeDestination(key.GetID())); - pubkeys.push_back(HexStr(key.begin(), key.end())); - } - obj.pushKV("pubkeys", std::move(pubkeys)); - } - - // The "addresses" field is confusing because it refers to public keys using their P2PKH address. - // For that reason, only add the 'addresses' field when needed for backward compatibility. New applications - // can use the 'embedded'->'address' field for P2SH or P2WSH wrapped addresses, and 'pubkeys' for - // inspecting multisig participants. - if (include_addresses) obj.pushKV("addresses", std::move(a)); - } - - UniValue operator()(const CNoDestination &dest) const { return UniValue(UniValue::VOBJ); } - - UniValue operator()(const CKeyID &keyID) const { - UniValue obj(UniValue::VOBJ); - CPubKey vchPubKey; - obj.pushKV("isscript", false); - obj.pushKV("iswitness", false); - if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) { - obj.pushKV("pubkey", HexStr(vchPubKey)); - obj.pushKV("iscompressed", vchPubKey.IsCompressed()); - } - return obj; - } - - UniValue operator()(const CScriptID &scriptID) const { - UniValue obj(UniValue::VOBJ); - CScript subscript; - obj.pushKV("isscript", true); - obj.pushKV("iswitness", false); - if (pwallet && pwallet->GetCScript(scriptID, subscript)) { - ProcessSubScript(subscript, obj, true); - } - return obj; - } - - UniValue operator()(const WitnessV0KeyHash& id) const - { - UniValue obj(UniValue::VOBJ); - CPubKey pubkey; - obj.pushKV("isscript", false); - obj.pushKV("iswitness", true); - obj.pushKV("witness_version", 0); - obj.pushKV("witness_program", HexStr(id.begin(), id.end())); - if (pwallet && pwallet->GetPubKey(CKeyID(id), pubkey)) { - obj.pushKV("pubkey", HexStr(pubkey)); - } - return obj; - } - - UniValue operator()(const WitnessV0ScriptHash& id) const - { - UniValue obj(UniValue::VOBJ); - CScript subscript; - obj.pushKV("isscript", true); - obj.pushKV("iswitness", true); - obj.pushKV("witness_version", 0); - obj.pushKV("witness_program", HexStr(id.begin(), id.end())); - CRIPEMD160 hasher; - uint160 hash; - hasher.Write(id.begin(), 32).Finalize(hash.begin()); - if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) { - ProcessSubScript(subscript, obj); - } - return obj; - } - - UniValue operator()(const WitnessUnknown& id) const - { - UniValue obj(UniValue::VOBJ); - CScript subscript; - obj.pushKV("iswitness", true); - obj.pushKV("witness_version", (int)id.version); - obj.pushKV("witness_program", HexStr(id.program, id.program + id.length)); - return obj; - } -}; -#endif - UniValue validateaddress(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "validateaddress \"address\"\n" "\nReturn information about the given bitcoin address.\n" + "DEPRECATION WARNING: Parts of this command have been deprecated and moved to getaddressinfo. Clients must\n" + "transition to using getaddressinfo to access this information before upgrading to v0.18. The following deprecated\n" + "fields have moved to getaddressinfo and will only be shown here with -deprecatedrpc=validateaddress: ismine, iswatchonly,\n" + "script, hex, pubkeys, sigsrequired, pubkey, addresses, embedded, iscompressed, account, timestamp, hdkeypath, kdmasterkeyid.\n" "\nArguments:\n" - "1. \"address\" (string, required) The bitcoin address to validate\n" + "1. \"address\" (string, required) The bitcoin address to validate\n" "\nResult:\n" "{\n" " \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n" " \"address\" : \"address\", (string) The bitcoin address validated\n" " \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\n" - " \"ismine\" : true|false, (boolean) If the address is yours or not\n" - " \"iswatchonly\" : true|false, (boolean) If the address is watchonly\n" - " \"isscript\" : true|false, (boolean, optional) If the address is P2SH or P2WSH. Not included for unknown witness types.\n" - " \"iswitness\" : true|false, (boolean) If the address is P2WPKH, P2WSH, or an unknown witness version\n" - " \"witness_version\" : version (number, optional) For all witness output types, gives the version number.\n" - " \"witness_program\" : \"hex\" (string, optional) For all witness output types, gives the script or key hash present in the address.\n" - " \"script\" : \"type\" (string, optional) The output script type. Only if \"isscript\" is true and the redeemscript is known. Possible types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash, witness_v0_scripthash, witness_unknown\n" - " \"hex\" : \"hex\", (string, optional) The redeemscript for the P2SH or P2WSH address\n" - " \"addresses\" (string, optional) Array of addresses associated with the known redeemscript (only if \"iswitness\" is false). This field is superseded by the \"pubkeys\" field and the address inside \"embedded\".\n" - " [\n" - " \"address\"\n" - " ,...\n" - " ]\n" - " \"pubkeys\" (string, optional) Array of pubkeys associated with the known redeemscript (only if \"script\" is \"multisig\")\n" - " [\n" - " \"pubkey\"\n" - " ,...\n" - " ]\n" - " \"sigsrequired\" : xxxxx (numeric, optional) Number of signatures required to spend multisig output (only if \"script\" is \"multisig\")\n" - " \"pubkey\" : \"publickeyhex\", (string, optional) The hex value of the raw public key, for single-key addresses (possibly embedded in P2SH or P2WSH)\n" - " \"embedded\" : {...}, (object, optional) information about the address embedded in P2SH or P2WSH, if relevant and known. It includes all validateaddress output fields for the embedded address, excluding \"isvalid\", metadata (\"timestamp\", \"hdkeypath\", \"hdmasterkeyid\") and relation to the wallet (\"ismine\", \"iswatchonly\", \"account\").\n" - " \"iscompressed\" : true|false, (boolean) If the address is compressed\n" - " \"account\" : \"account\" (string) DEPRECATED. The account associated with the address, \"\" is the default account\n" - " \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n" - " \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n" - " \"hdmasterkeyid\" : \"<hash160>\" (string, optional) The Hash160 of the HD master pubkey\n" + " \"isscript\" : true|false, (boolean) If the key is a script\n" + " \"iswitness\" : true|false, (boolean) If the address is a witness address\n" + " \"witness_version\" : version (numeric, optional) The version number of the witness program\n" + " \"witness_program\" : \"hex\" (string, optional) The hex value of the witness program\n" "}\n" "\nExamples:\n" + HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") + HelpExampleRpc("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") ); -#ifdef ENABLE_WALLET - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); - - LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr); -#else - LOCK(cs_main); -#endif - CTxDestination dest = DecodeDestination(request.params[0].get_str()); bool isValid = IsValidDestination(dest); @@ -209,45 +67,22 @@ UniValue validateaddress(const JSONRPCRequest& request) ret.pushKV("isvalid", isValid); if (isValid) { - std::string currentAddress = EncodeDestination(dest); - ret.pushKV("address", currentAddress); - - CScript scriptPubKey = GetScriptForDestination(dest); - ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())); #ifdef ENABLE_WALLET - isminetype mine = pwallet ? IsMine(*pwallet, dest) : ISMINE_NO; - ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE)); - ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY)); - UniValue detail = boost::apply_visitor(DescribeAddressVisitor(pwallet), dest); - ret.pushKVs(detail); - if (pwallet && pwallet->mapAddressBook.count(dest)) { - ret.pushKV("account", pwallet->mapAddressBook[dest].name); - } - if (pwallet) { - const CKeyMetadata* meta = nullptr; - CKeyID key_id = GetKeyForDestination(*pwallet, dest); - if (!key_id.IsNull()) { - auto it = pwallet->mapKeyMetadata.find(key_id); - if (it != pwallet->mapKeyMetadata.end()) { - meta = &it->second; - } - } - if (!meta) { - auto it = pwallet->m_script_metadata.find(CScriptID(scriptPubKey)); - if (it != pwallet->m_script_metadata.end()) { - meta = &it->second; - } - } - if (meta) { - ret.pushKV("timestamp", meta->nCreateTime); - if (!meta->hdKeypath.empty()) { - ret.pushKV("hdkeypath", meta->hdKeypath); - ret.pushKV("hdmasterkeyid", meta->hdMasterKeyID.GetHex()); - } - } + if (!::vpwallets.empty() && IsDeprecatedRPCEnabled("validateaddress")) { + ret.pushKVs(getaddressinfo(request)); } #endif + if (ret["address"].isNull()) { + std::string currentAddress = EncodeDestination(dest); + ret.pushKV("address", currentAddress); + + CScript scriptPubKey = GetScriptForDestination(dest); + ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));; + + UniValue detail = DescribeAddress(dest); + ret.pushKVs(detail); + } } return ret; } @@ -263,7 +98,7 @@ UniValue createmultisig(const JSONRPCRequest& request) "\nCreates a multi-signature address with n signature of m keys required.\n" "It returns a json object with the address and redeemScript.\n" "\nArguments:\n" - "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n" + "1. nrequired (numeric, required) The number of required signatures out of the n keys.\n" "2. \"keys\" (string, required) A json array of hex-encoded public keys\n" " [\n" " \"key\" (string) The hex-encoded public key\n" @@ -389,13 +224,10 @@ UniValue signmessagewithprivkey(const JSONRPCRequest& request) std::string strPrivkey = request.params[0].get_str(); std::string strMessage = request.params[1].get_str(); - CBitcoinSecret vchSecret; - bool fGood = vchSecret.SetString(strPrivkey); - if (!fGood) + CKey key = DecodeSecret(strPrivkey); + if (!key.IsValid()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); - CKey key = vchSecret.GetKey(); - if (!key.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range"); + } CHashWriter ss(SER_GETHASH, 0); ss << strMessageMagic; diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 7a0225ff0d..fee2b765ba 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -89,7 +89,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request) " \"pingtime\": n, (numeric) ping time (if available)\n" " \"minping\": n, (numeric) minimum observed ping time (if any at all)\n" " \"pingwait\": n, (numeric) ping wait (if non-zero)\n" - " \"version\": v, (numeric) The peer version, such as 7001\n" + " \"version\": v, (numeric) The peer version, such as 70001\n" " \"subver\": \"/Satoshi:0.8.5/\", (string) The string version\n" " \"inbound\": true|false, (boolean) Inbound (true) or Outbound (false)\n" " \"addnode\": true|false, (boolean) Whether connection was due to addnode/-connect or if it was an automatic/inbound connection\n" diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index fb70f9d596..20bfd3f355 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -3,7 +3,6 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <base58.h> #include <chain.h> #include <coins.h> #include <consensus/validation.h> @@ -12,11 +11,13 @@ #include <keystore.h> #include <validation.h> #include <validationinterface.h> +#include <key_io.h> #include <merkleblock.h> #include <net.h> #include <policy/policy.h> #include <policy/rbf.h> #include <primitives/transaction.h> +#include <rpc/rawtransaction.h> #include <rpc/safemode.h> #include <rpc/server.h> #include <script/script.h> @@ -28,7 +29,6 @@ #include <utilstrencodings.h> #ifdef ENABLE_WALLET #include <wallet/rpcwallet.h> -#include <wallet/wallet.h> #endif #include <future> @@ -48,9 +48,8 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) if (!hashBlock.IsNull()) { entry.pushKV("blockhash", hashBlock.GetHex()); - BlockMap::iterator mi = mapBlockIndex.find(hashBlock); - if (mi != mapBlockIndex.end() && (*mi).second) { - CBlockIndex* pindex = (*mi).second; + CBlockIndex* pindex = LookupBlockIndex(hashBlock); + if (pindex) { if (chainActive.Contains(pindex)) { entry.pushKV("confirmations", 1 + chainActive.Height() - pindex->nHeight); entry.pushKV("time", pindex->GetBlockTime()); @@ -160,11 +159,10 @@ UniValue getrawtransaction(const JSONRPCRequest& request) if (!request.params[2].isNull()) { uint256 blockhash = ParseHashV(request.params[2], "parameter 3"); - BlockMap::iterator it = mapBlockIndex.find(blockhash); - if (it == mapBlockIndex.end()) { + blockindex = LookupBlockIndex(blockhash); + if (!blockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block hash not found"); } - blockindex = it->second; in_active_chain = chainActive.Contains(blockindex); } @@ -238,9 +236,10 @@ UniValue gettxoutproof(const JSONRPCRequest& request) if (!request.params[1].isNull()) { hashBlock = uint256S(request.params[1].get_str()); - if (!mapBlockIndex.count(hashBlock)) + pblockindex = LookupBlockIndex(hashBlock); + if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - pblockindex = mapBlockIndex[hashBlock]; + } } else { // Loop through txids and try to find which block they're in. Exit loop once a block is found. for (const auto& tx : setTxids) { @@ -257,9 +256,10 @@ UniValue gettxoutproof(const JSONRPCRequest& request) CTransactionRef tx; if (!GetTransaction(oneTxid, tx, Params().GetConsensus(), hashBlock, false) || hashBlock.IsNull()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block"); - if (!mapBlockIndex.count(hashBlock)) + pblockindex = LookupBlockIndex(hashBlock); + if (!pblockindex) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt"); - pblockindex = mapBlockIndex[hashBlock]; + } } CBlock block; @@ -306,8 +306,10 @@ UniValue verifytxoutproof(const JSONRPCRequest& request) LOCK(cs_main); - if (!mapBlockIndex.count(merkleBlock.header.GetHash()) || !chainActive.Contains(mapBlockIndex[merkleBlock.header.GetHash()])) + const CBlockIndex* pindex = LookupBlockIndex(merkleBlock.header.GetHash()); + if (!pindex || !chainActive.Contains(pindex)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain"); + } for (const uint256& hash : vMatch) res.push_back(hash.GetHex()); @@ -316,9 +318,10 @@ UniValue verifytxoutproof(const JSONRPCRequest& request) UniValue createrawtransaction(const JSONRPCRequest& request) { - if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) + if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) { throw std::runtime_error( - "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...} ( locktime ) ( replaceable )\n" + // clang-format off + "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable )\n" "\nCreate a transaction spending the given inputs and creating new outputs.\n" "Outputs can be addresses or data.\n" "Returns hex-encoded raw transaction.\n" @@ -329,18 +332,23 @@ UniValue createrawtransaction(const JSONRPCRequest& request) "1. \"inputs\" (array, required) A json array of json objects\n" " [\n" " {\n" - " \"txid\":\"id\", (string, required) The transaction id\n" + " \"txid\":\"id\", (string, required) The transaction id\n" " \"vout\":n, (numeric, required) The output number\n" " \"sequence\":n (numeric, optional) The sequence number\n" " } \n" " ,...\n" " ]\n" - "2. \"outputs\" (object, required) a json object with outputs\n" + "2. \"outputs\" (array, required) a json array with outputs (key-value pairs)\n" + " [\n" " {\n" - " \"address\": x.xxx, (numeric or string, required) The key is the bitcoin address, the numeric value (can be string) is the " + CURRENCY_UNIT + " amount\n" - " \"data\": \"hex\" (string, required) The key is \"data\", the value is hex encoded data\n" - " ,...\n" + " \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n" + " },\n" + " {\n" + " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n" " }\n" + " ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n" + " accepted as second parameter.\n" + " ]\n" "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n" "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n" " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n" @@ -348,18 +356,29 @@ UniValue createrawtransaction(const JSONRPCRequest& request) "\"transaction\" (string) hex string of the transaction\n" "\nExamples:\n" - + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"address\\\":0.01}\"") - + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"data\\\":\\\"00010203\\\"}\"") - + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"address\\\":0.01}\"") - + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"data\\\":\\\"00010203\\\"}\"") + + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"address\\\":0.01}]\"") + + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"") + + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"address\\\":0.01}]\"") + + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"data\\\":\\\"00010203\\\"}]\"") + // clang-format on ); + } - RPCTypeCheck(request.params, {UniValue::VARR, UniValue::VOBJ, UniValue::VNUM, UniValue::VBOOL}, true); + RPCTypeCheck(request.params, { + UniValue::VARR, + UniValueType(), // ARR or OBJ, checked later + UniValue::VNUM, + UniValue::VBOOL + }, true + ); if (request.params[0].isNull() || request.params[1].isNull()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null"); UniValue inputs = request.params[0].get_array(); - UniValue sendTo = request.params[1].get_obj(); + const bool outputs_is_obj = request.params[1].isObject(); + UniValue outputs = outputs_is_obj ? + request.params[1].get_obj() : + request.params[1].get_array(); CMutableTransaction rawTx; @@ -411,11 +430,24 @@ UniValue createrawtransaction(const JSONRPCRequest& request) } std::set<CTxDestination> destinations; - std::vector<std::string> addrList = sendTo.getKeys(); - for (const std::string& name_ : addrList) { - + if (!outputs_is_obj) { + // Translate array of key-value pairs into dict + UniValue outputs_dict = UniValue(UniValue::VOBJ); + for (size_t i = 0; i < outputs.size(); ++i) { + const UniValue& output = outputs[i]; + if (!output.isObject()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair not an object as expected"); + } + if (output.size() != 1) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair must contain exactly one key"); + } + outputs_dict.pushKVs(output); + } + outputs = std::move(outputs_dict); + } + for (const std::string& name_ : outputs.getKeys()) { if (name_ == "data") { - std::vector<unsigned char> data = ParseHexV(sendTo[name_].getValStr(),"Data"); + std::vector<unsigned char> data = ParseHexV(outputs[name_].getValStr(), "Data"); CTxOut out(0, CScript() << OP_RETURN << data); rawTx.vout.push_back(out); @@ -430,7 +462,7 @@ UniValue createrawtransaction(const JSONRPCRequest& request) } CScript scriptPubKey = GetScriptForDestination(destination); - CAmount nAmount = AmountFromValue(sendTo[name_]); + CAmount nAmount = AmountFromValue(outputs[name_]); CTxOut out(nAmount, scriptPubKey); rawTx.vout.push_back(out); @@ -672,88 +704,13 @@ UniValue combinerawtransaction(const JSONRPCRequest& request) return EncodeHexTx(mergedTx); } -UniValue signrawtransaction(const JSONRPCRequest& request) +UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival, CBasicKeyStore *keystore, bool is_temp_keystore, const UniValue& hashType) { -#ifdef ENABLE_WALLET - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); -#endif - - if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) - throw std::runtime_error( - "signrawtransaction \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype )\n" - "\nSign inputs for raw transaction (serialized, hex-encoded).\n" - "The second optional argument (may be null) is an array of previous transaction outputs that\n" - "this transaction depends on but may not yet be in the block chain.\n" - "The third optional argument (may be null) is an array of base58-encoded private\n" - "keys that, if given, will be the only keys used to sign the transaction.\n" -#ifdef ENABLE_WALLET - + HelpRequiringPassphrase(pwallet) + "\n" -#endif - - "\nArguments:\n" - "1. \"hexstring\" (string, required) The transaction hex string\n" - "2. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n" - " [ (json array of json objects, or 'null' if none provided)\n" - " {\n" - " \"txid\":\"id\", (string, required) The transaction id\n" - " \"vout\":n, (numeric, required) The output number\n" - " \"scriptPubKey\": \"hex\", (string, required) script key\n" - " \"redeemScript\": \"hex\", (string, required for P2SH or P2WSH) redeem script\n" - " \"amount\": value (numeric, required) The amount spent\n" - " }\n" - " ,...\n" - " ]\n" - "3. \"privkeys\" (string, optional) A json array of base58-encoded private keys for signing\n" - " [ (json array of strings, or 'null' if none provided)\n" - " \"privatekey\" (string) private key in base58-encoding\n" - " ,...\n" - " ]\n" - "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n" - " \"ALL\"\n" - " \"NONE\"\n" - " \"SINGLE\"\n" - " \"ALL|ANYONECANPAY\"\n" - " \"NONE|ANYONECANPAY\"\n" - " \"SINGLE|ANYONECANPAY\"\n" - - "\nResult:\n" - "{\n" - " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n" - " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n" - " \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n" - " {\n" - " \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n" - " \"vout\" : n, (numeric) The index of the output to spent and used as input\n" - " \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n" - " \"sequence\" : n, (numeric) Script sequence number\n" - " \"error\" : \"text\" (string) Verification or signing error related to the input\n" - " }\n" - " ,...\n" - " ]\n" - "}\n" - - "\nExamples:\n" - + HelpExampleCli("signrawtransaction", "\"myhex\"") - + HelpExampleRpc("signrawtransaction", "\"myhex\"") - ); - - ObserveSafeMode(); -#ifdef ENABLE_WALLET - LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr); -#else - LOCK(cs_main); -#endif - RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true); - - CMutableTransaction mtx; - if (!DecodeHexTx(mtx, request.params[0].get_str(), true)) - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); - // Fetch previous transactions (inputs): CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); { - LOCK(mempool.cs); + LOCK2(cs_main, mempool.cs); CCoinsViewCache &viewChain = *pcoinsTip; CCoinsViewMemPool viewMempool(&viewChain, mempool); view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view @@ -765,36 +722,14 @@ UniValue signrawtransaction(const JSONRPCRequest& request) view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long } - bool fGivenKeys = false; - CBasicKeyStore tempKeystore; - if (!request.params[2].isNull()) { - fGivenKeys = true; - UniValue keys = request.params[2].get_array(); - for (unsigned int idx = 0; idx < keys.size(); idx++) { - UniValue k = keys[idx]; - CBitcoinSecret vchSecret; - bool fGood = vchSecret.SetString(k.get_str()); - if (!fGood) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); - CKey key = vchSecret.GetKey(); - if (!key.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range"); - tempKeystore.AddKey(key); - } - } -#ifdef ENABLE_WALLET - else if (pwallet) { - EnsureWalletIsUnlocked(pwallet); - } -#endif - // Add previous txouts given in the RPC call: - if (!request.params[1].isNull()) { - UniValue prevTxs = request.params[1].get_array(); - for (unsigned int idx = 0; idx < prevTxs.size(); idx++) { + if (!prevTxsUnival.isNull()) { + UniValue prevTxs = prevTxsUnival.get_array(); + for (unsigned int idx = 0; idx < prevTxs.size(); ++idx) { const UniValue& p = prevTxs[idx]; - if (!p.isObject()) + if (!p.isObject()) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}"); + } UniValue prevOut = p.get_obj(); @@ -808,8 +743,9 @@ UniValue signrawtransaction(const JSONRPCRequest& request) uint256 txid = ParseHashO(prevOut, "txid"); int nOut = find_value(prevOut, "vout").get_int(); - if (nOut < 0) + if (nOut < 0) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive"); + } COutPoint out(txid, nOut); std::vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey")); @@ -834,8 +770,8 @@ UniValue signrawtransaction(const JSONRPCRequest& request) } // if redeemScript given and not using the local wallet (private keys - // given), add redeemScript to the tempKeystore so it can be signed: - if (fGivenKeys && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) { + // given), add redeemScript to the keystore so it can be signed: + if (is_temp_keystore && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) { RPCTypeCheckObj(prevOut, { {"txid", UniValueType(UniValue::VSTR)}, @@ -847,20 +783,16 @@ UniValue signrawtransaction(const JSONRPCRequest& request) if (!v.isNull()) { std::vector<unsigned char> rsData(ParseHexV(v, "redeemScript")); CScript redeemScript(rsData.begin(), rsData.end()); - tempKeystore.AddCScript(redeemScript); + keystore->AddCScript(redeemScript); + // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH). + keystore->AddCScript(GetScriptForWitness(redeemScript)); } } } } -#ifdef ENABLE_WALLET - const CKeyStore& keystore = ((fGivenKeys || !pwallet) ? tempKeystore : *pwallet); -#else - const CKeyStore& keystore = tempKeystore; -#endif - int nHashType = SIGHASH_ALL; - if (!request.params[3].isNull()) { + if (!hashType.isNull()) { static std::map<std::string, int> mapSigHashValues = { {std::string("ALL"), int(SIGHASH_ALL)}, {std::string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)}, @@ -869,11 +801,12 @@ UniValue signrawtransaction(const JSONRPCRequest& request) {std::string("SINGLE"), int(SIGHASH_SINGLE)}, {std::string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)}, }; - std::string strHashType = request.params[3].get_str(); - if (mapSigHashValues.count(strHashType)) + std::string strHashType = hashType.get_str(); + if (mapSigHashValues.count(strHashType)) { nHashType = mapSigHashValues[strHashType]; - else + } else { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param"); + } } bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); @@ -897,8 +830,9 @@ UniValue signrawtransaction(const JSONRPCRequest& request) SignatureData sigdata; // Only sign SIGHASH_SINGLE if there's a corresponding output: - if (!fHashSingle || (i < mtx.vout.size())) - ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mtx, i, amount, nHashType), prevPubKey, sigdata); + if (!fHashSingle || (i < mtx.vout.size())) { + ProduceSignature(MutableTransactionSignatureCreator(keystore, &mtx, i, amount, nHashType), prevPubKey, sigdata); + } sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(mtx, i)); UpdateTransaction(mtx, i, sigdata); @@ -925,6 +859,184 @@ UniValue signrawtransaction(const JSONRPCRequest& request) return result; } +UniValue signrawtransactionwithkey(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) + throw std::runtime_error( + "signrawtransactionwithkey \"hexstring\" [\"privatekey1\",...] ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] sighashtype )\n" + "\nSign inputs for raw transaction (serialized, hex-encoded).\n" + "The second argument is an array of base58-encoded private\n" + "keys that will be the only keys used to sign the transaction.\n" + "The third optional argument (may be null) is an array of previous transaction outputs that\n" + "this transaction depends on but may not yet be in the block chain.\n" + + "\nArguments:\n" + "1. \"hexstring\" (string, required) The transaction hex string\n" + "2. \"privkeys\" (string, required) A json array of base58-encoded private keys for signing\n" + " [ (json array of strings)\n" + " \"privatekey\" (string) private key in base58-encoding\n" + " ,...\n" + " ]\n" + "3. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n" + " [ (json array of json objects, or 'null' if none provided)\n" + " {\n" + " \"txid\":\"id\", (string, required) The transaction id\n" + " \"vout\":n, (numeric, required) The output number\n" + " \"scriptPubKey\": \"hex\", (string, required) script key\n" + " \"redeemScript\": \"hex\", (string, required for P2SH or P2WSH) redeem script\n" + " \"amount\": value (numeric, required) The amount spent\n" + " }\n" + " ,...\n" + " ]\n" + "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n" + " \"ALL\"\n" + " \"NONE\"\n" + " \"SINGLE\"\n" + " \"ALL|ANYONECANPAY\"\n" + " \"NONE|ANYONECANPAY\"\n" + " \"SINGLE|ANYONECANPAY\"\n" + + "\nResult:\n" + "{\n" + " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n" + " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n" + " \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n" + " {\n" + " \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n" + " \"vout\" : n, (numeric) The index of the output to spent and used as input\n" + " \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n" + " \"sequence\" : n, (numeric) Script sequence number\n" + " \"error\" : \"text\" (string) Verification or signing error related to the input\n" + " }\n" + " ,...\n" + " ]\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("signrawtransactionwithkey", "\"myhex\"") + + HelpExampleRpc("signrawtransactionwithkey", "\"myhex\"") + ); + + RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true); + + CMutableTransaction mtx; + if (!DecodeHexTx(mtx, request.params[0].get_str(), true)) { + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + } + + CBasicKeyStore keystore; + const UniValue& keys = request.params[1].get_array(); + for (unsigned int idx = 0; idx < keys.size(); ++idx) { + UniValue k = keys[idx]; + CKey key = DecodeSecret(k.get_str()); + if (!key.IsValid()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); + } + keystore.AddKey(key); + } + + return SignTransaction(mtx, request.params[2], &keystore, true, request.params[3]); +} + +UniValue signrawtransaction(const JSONRPCRequest& request) +{ +#ifdef ENABLE_WALLET + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); +#endif + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) + throw std::runtime_error( + "signrawtransaction \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype )\n" + "\nDEPRECATED. Sign inputs for raw transaction (serialized, hex-encoded).\n" + "The second optional argument (may be null) is an array of previous transaction outputs that\n" + "this transaction depends on but may not yet be in the block chain.\n" + "The third optional argument (may be null) is an array of base58-encoded private\n" + "keys that, if given, will be the only keys used to sign the transaction.\n" +#ifdef ENABLE_WALLET + + HelpRequiringPassphrase(pwallet) + "\n" +#endif + "\nArguments:\n" + "1. \"hexstring\" (string, required) The transaction hex string\n" + "2. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n" + " [ (json array of json objects, or 'null' if none provided)\n" + " {\n" + " \"txid\":\"id\", (string, required) The transaction id\n" + " \"vout\":n, (numeric, required) The output number\n" + " \"scriptPubKey\": \"hex\", (string, required) script key\n" + " \"redeemScript\": \"hex\", (string, required for P2SH or P2WSH) redeem script\n" + " \"amount\": value (numeric, required) The amount spent\n" + " }\n" + " ,...\n" + " ]\n" + "3. \"privkeys\" (string, optional) A json array of base58-encoded private keys for signing\n" + " [ (json array of strings, or 'null' if none provided)\n" + " \"privatekey\" (string) private key in base58-encoding\n" + " ,...\n" + " ]\n" + "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n" + " \"ALL\"\n" + " \"NONE\"\n" + " \"SINGLE\"\n" + " \"ALL|ANYONECANPAY\"\n" + " \"NONE|ANYONECANPAY\"\n" + " \"SINGLE|ANYONECANPAY\"\n" + + "\nResult:\n" + "{\n" + " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n" + " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n" + " \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n" + " {\n" + " \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n" + " \"vout\" : n, (numeric) The index of the output to spent and used as input\n" + " \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n" + " \"sequence\" : n, (numeric) Script sequence number\n" + " \"error\" : \"text\" (string) Verification or signing error related to the input\n" + " }\n" + " ,...\n" + " ]\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("signrawtransaction", "\"myhex\"") + + HelpExampleRpc("signrawtransaction", "\"myhex\"") + ); + + if (!IsDeprecatedRPCEnabled("signrawtransaction")) { + throw JSONRPCError(RPC_METHOD_DEPRECATED, "signrawtransaction is deprecated and will be fully removed in v0.18. " + "To use signrawtransaction in v0.17, restart bitcoind with -deprecatedrpc=signrawtransaction.\n" + "Projects should transition to using signrawtransactionwithkey and signrawtransactionwithwallet before upgrading to v0.18"); + } + + RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true); + + // Make a JSONRPCRequest to pass on to the right signrawtransaction* command + JSONRPCRequest new_request; + new_request.id = request.id; + new_request.params.setArray(); + + // For signing with private keys + if (!request.params[2].isNull()) { + new_request.params.push_back(request.params[0]); + // Note: the prevtxs and privkeys are reversed for signrawtransactionwithkey + new_request.params.push_back(request.params[2]); + new_request.params.push_back(request.params[1]); + new_request.params.push_back(request.params[3]); + return signrawtransactionwithkey(new_request); + } + // Otherwise sign with the wallet which does not take a privkeys parameter +#ifdef ENABLE_WALLET + else { + new_request.params.push_back(request.params[0]); + new_request.params.push_back(request.params[1]); + new_request.params.push_back(request.params[3]); + return signrawtransactionwithwallet(new_request); + } +#endif + // If we have made it this far, then wallet is disabled and no private keys were given, so fail here. + throw JSONRPCError(RPC_INVALID_PARAMETER, "No private keys available."); +} + UniValue sendrawtransaction(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) @@ -981,12 +1093,12 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) if (!AcceptToMemoryPool(mempool, state, std::move(tx), &fMissingInputs, nullptr /* plTxnReplaced */, false /* bypass_limits */, nMaxRawTxFee)) { if (state.IsInvalid()) { - throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); + throw JSONRPCError(RPC_TRANSACTION_REJECTED, FormatStateMessage(state)); } else { if (fMissingInputs) { throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs"); } - throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason()); + throw JSONRPCError(RPC_TRANSACTION_ERROR, FormatStateMessage(state)); } } else { // If wallet is enabled, ensure that the wallet has been made aware @@ -1023,18 +1135,19 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) } static const CRPCCommand commands[] = -{ // category name actor (function) argNames - // --------------------- ------------------------ ----------------------- ---------- - { "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} }, - { "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable"} }, - { "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring","iswitness"} }, - { "rawtransactions", "decodescript", &decodescript, {"hexstring"} }, - { "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees"} }, - { "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} }, - { "rawtransactions", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */ - - { "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} }, - { "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} }, +{ // category name actor (function) argNames + // --------------------- ------------------------ ----------------------- ---------- + { "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} }, + { "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable"} }, + { "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring","iswitness"} }, + { "rawtransactions", "decodescript", &decodescript, {"hexstring"} }, + { "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees"} }, + { "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} }, + { "rawtransactions", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */ + { "rawtransactions", "signrawtransactionwithkey", &signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} }, + + { "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} }, + { "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} }, }; void RegisterRawTransactionRPCCommands(CRPCTable &t) diff --git a/src/rpc/rawtransaction.h b/src/rpc/rawtransaction.h new file mode 100644 index 0000000000..ec9d1f2cf0 --- /dev/null +++ b/src/rpc/rawtransaction.h @@ -0,0 +1,15 @@ +// Copyright (c) 2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_RPC_RAWTRANSACTION_H +#define BITCOIN_RPC_RAWTRANSACTION_H + +class CBasicKeyStore; +struct CMutableTransaction; +class UniValue; + +/** Sign a transaction with the given keystore and previous transactions */ +UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore *keystore, bool tempKeystore, const UniValue& hashType); + +#endif // BITCOIN_RPC_RAWTRANSACTION_H diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index e5b4f6ca77..54995ef000 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -5,9 +5,9 @@ #include <rpc/server.h> -#include <base58.h> #include <fs.h> #include <init.h> +#include <key_io.h> #include <random.h> #include <sync.h> #include <ui_interface.h> @@ -50,12 +50,11 @@ void RPCServer::OnStopped(std::function<void ()> slot) } void RPCTypeCheck(const UniValue& params, - const std::list<UniValue::VType>& typesExpected, + const std::list<UniValueType>& typesExpected, bool fAllowNull) { unsigned int i = 0; - for (UniValue::VType t : typesExpected) - { + for (const UniValueType& t : typesExpected) { if (params.size() <= i) break; @@ -67,10 +66,10 @@ void RPCTypeCheck(const UniValue& params, } } -void RPCTypeCheckArgument(const UniValue& value, UniValue::VType typeExpected) +void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected) { - if (value.type() != typeExpected) { - throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s", uvTypeName(typeExpected), uvTypeName(value.type()))); + if (!typeExpected.typeAny && value.type() != typeExpected.type) { + throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s", uvTypeName(typeExpected.type), uvTypeName(value.type()))); } } diff --git a/src/rpc/server.h b/src/rpc/server.h index 075940cb90..8b32924fbc 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -28,9 +28,9 @@ namespace RPCServer } /** Wrapper for UniValue::VType, which includes typeAny: - * Used to denote don't care type. Only used by RPCTypeCheckObj */ + * Used to denote don't care type. */ struct UniValueType { - explicit UniValueType(UniValue::VType _type) : typeAny(false), type(_type) {} + UniValueType(UniValue::VType _type) : typeAny(false), type(_type) {} UniValueType() : typeAny(true) {} bool typeAny; UniValue::VType type; @@ -69,12 +69,12 @@ bool RPCIsInWarmup(std::string *outStatus); * the right number of arguments are passed, just that any passed are the correct type. */ void RPCTypeCheck(const UniValue& params, - const std::list<UniValue::VType>& typesExpected, bool fAllowNull=false); + const std::list<UniValueType>& typesExpected, bool fAllowNull=false); /** * Type-check one argument; throws JSONRPCError if wrong type given. */ -void RPCTypeCheckArgument(const UniValue& value, UniValue::VType typeExpected); +void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected); /* Check for expected keys/value types in an Object. diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 09ded4e46e..593962e710 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -2,7 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <base58.h> +#include <key_io.h> #include <keystore.h> #include <pubkey.h> #include <rpc/protocol.h> @@ -66,3 +66,64 @@ CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey return result; } + +class DescribeAddressVisitor : public boost::static_visitor<UniValue> +{ +public: + explicit DescribeAddressVisitor() {} + + UniValue operator()(const CNoDestination& dest) const + { + return UniValue(UniValue::VOBJ); + } + + UniValue operator()(const CKeyID& keyID) const + { + UniValue obj(UniValue::VOBJ); + obj.pushKV("isscript", false); + obj.pushKV("iswitness", false); + return obj; + } + + UniValue operator()(const CScriptID& scriptID) const + { + UniValue obj(UniValue::VOBJ); + obj.pushKV("isscript", true); + obj.pushKV("iswitness", false); + return obj; + } + + UniValue operator()(const WitnessV0KeyHash& id) const + { + UniValue obj(UniValue::VOBJ); + obj.pushKV("isscript", false); + obj.pushKV("iswitness", true); + obj.pushKV("witness_version", 0); + obj.pushKV("witness_program", HexStr(id.begin(), id.end())); + return obj; + } + + UniValue operator()(const WitnessV0ScriptHash& id) const + { + UniValue obj(UniValue::VOBJ); + obj.pushKV("isscript", true); + obj.pushKV("iswitness", true); + obj.pushKV("witness_version", 0); + obj.pushKV("witness_program", HexStr(id.begin(), id.end())); + return obj; + } + + UniValue operator()(const WitnessUnknown& id) const + { + UniValue obj(UniValue::VOBJ); + obj.pushKV("iswitness", true); + obj.pushKV("witness_version", (int)id.version); + obj.pushKV("witness_program", HexStr(id.program, id.program + id.length)); + return obj; + } +}; + +UniValue DescribeAddress(const CTxDestination& dest) +{ + return boost::apply_visitor(DescribeAddressVisitor(), dest); +} diff --git a/src/rpc/util.h b/src/rpc/util.h index 568a4260ba..5380d45a83 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -5,6 +5,13 @@ #ifndef BITCOIN_RPC_UTIL_H #define BITCOIN_RPC_UTIL_H +#include <pubkey.h> +#include <script/standard.h> +#include <univalue.h> +#include <utilstrencodings.h> + +#include <boost/variant/static_visitor.hpp> + #include <string> #include <vector> @@ -16,4 +23,6 @@ CPubKey HexToPubKey(const std::string& hex_in); CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in); CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey>& pubkeys); +UniValue DescribeAddress(const CTxDestination& dest); + #endif // BITCOIN_RPC_UTIL_H diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 2cdff7ee57..927b0267ca 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -226,23 +226,25 @@ bool static CheckPubKeyEncoding(const valtype &vchPubKey, unsigned int flags, co } bool static CheckMinimalPush(const valtype& data, opcodetype opcode) { + // Excludes OP_1NEGATE, OP_1-16 since they are by definition minimal + assert(0 <= opcode && opcode <= OP_PUSHDATA4); if (data.size() == 0) { - // Could have used OP_0. + // Should have used OP_0. return opcode == OP_0; } else if (data.size() == 1 && data[0] >= 1 && data[0] <= 16) { - // Could have used OP_1 .. OP_16. - return opcode == OP_1 + (data[0] - 1); + // Should have used OP_1 .. OP_16. + return false; } else if (data.size() == 1 && data[0] == 0x81) { - // Could have used OP_1NEGATE. - return opcode == OP_1NEGATE; + // Should have used OP_1NEGATE. + return false; } else if (data.size() <= 75) { - // Could have used a direct push (opcode indicating number of bytes pushed + those bytes). + // Must have used a direct push (opcode indicating number of bytes pushed + those bytes). return opcode == data.size(); } else if (data.size() <= 255) { - // Could have used OP_PUSHDATA. + // Must have used OP_PUSHDATA. return opcode == OP_PUSHDATA1; } else if (data.size() <= 65535) { - // Could have used OP_PUSHDATA2. + // Must have used OP_PUSHDATA2. return opcode == OP_PUSHDATA2; } return true; diff --git a/src/script/interpreter.h b/src/script/interpreter.h index e12329be76..4dad6b44c5 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -104,7 +104,7 @@ enum // SCRIPT_VERIFY_MINIMALIF = (1U << 13), - // Signature(s) must be empty vector if an CHECK(MULTI)SIG operation failed + // Signature(s) must be empty vector if a CHECK(MULTI)SIG operation failed // SCRIPT_VERIFY_NULLFAIL = (1U << 14), diff --git a/src/script/ismine.h b/src/script/ismine.h index b54879cc15..c1338c3a8e 100644 --- a/src/script/ismine.h +++ b/src/script/ismine.h @@ -29,7 +29,7 @@ enum isminetype typedef uint8_t isminefilter; /* isInvalid becomes true when the script is found invalid by consensus or policy. This will terminate the recursion - * and return a ISMINE_NO immediately, as an invalid script should never be considered as "mine". This is needed as + * and return ISMINE_NO immediately, as an invalid script should never be considered as "mine". This is needed as * different SIGVERSION may have different network rules. Currently the only use of isInvalid is indicate uncompressed * keys in SIGVERSION_WITNESS_V0 script, but could also be used in similar cases in the future */ diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 838e502a0a..aaba5e5926 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -140,10 +140,9 @@ static CScript PushAll(const std::vector<valtype>& values) bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata) { - CScript script = fromPubKey; std::vector<valtype> result; txnouttype whichType; - bool solved = SignStep(creator, script, result, whichType, SIGVERSION_BASE); + bool solved = SignStep(creator, fromPubKey, result, whichType, SIGVERSION_BASE); bool P2SH = false; CScript subscript; sigdata.scriptWitness.stack.clear(); @@ -153,8 +152,8 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu // Solver returns the subscript that needs to be evaluated; // the final scriptSig is the signatures from that // and then the serialized subscript: - script = subscript = CScript(result[0].begin(), result[0].end()); - solved = solved && SignStep(creator, script, result, whichType, SIGVERSION_BASE) && whichType != TX_SCRIPTHASH; + subscript = CScript(result[0].begin(), result[0].end()); + solved = solved && SignStep(creator, subscript, result, whichType, SIGVERSION_BASE) && whichType != TX_SCRIPTHASH; P2SH = true; } diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index b338d6d366..0d8bd90119 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -52,6 +52,17 @@ public: { CAddrMan::Delete(nId); } + + // Simulates connection failure so that we can test eviction of offline nodes + void SimConnFail(CService& addr) + { + int64_t nLastSuccess = 1; + Good_(addr, true, nLastSuccess); // Set last good connection in the deep past. + + bool count_failure = false; + int64_t nLastTry = GetAdjustedTime()-61; + Attempt(addr, count_failure, nLastTry); + } }; static CNetAddr ResolveIP(const char* ip) @@ -226,7 +237,7 @@ BOOST_AUTO_TEST_CASE(addrman_new_collisions) BOOST_CHECK_EQUAL(addrman.size(), 0); for (unsigned int i = 1; i < 18; i++) { - CService addr = ResolveService("250.1.1." + boost::to_string(i)); + CService addr = ResolveService("250.1.1." + std::to_string(i)); addrman.Add(CAddress(addr, NODE_NONE), source); //Test: No collision in new table yet. @@ -252,7 +263,7 @@ BOOST_AUTO_TEST_CASE(addrman_tried_collisions) BOOST_CHECK_EQUAL(addrman.size(), 0); for (unsigned int i = 1; i < 80; i++) { - CService addr = ResolveService("250.1.1." + boost::to_string(i)); + CService addr = ResolveService("250.1.1." + std::to_string(i)); addrman.Add(CAddress(addr, NODE_NONE), source); addrman.Good(CAddress(addr, NODE_NONE)); @@ -385,7 +396,7 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr) for (unsigned int i = 1; i < (8 * 256); i++) { int octet1 = i % 256; int octet2 = i >> 8 % 256; - std::string strAddr = boost::to_string(octet1) + "." + boost::to_string(octet2) + ".1.23"; + std::string strAddr = std::to_string(octet1) + "." + std::to_string(octet2) + ".1.23"; CAddress addr = CAddress(ResolveService(strAddr), NODE_NONE); // Ensure that for all addrs in addrman, isTerrible == false. @@ -436,8 +447,8 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket) std::set<int> buckets; for (int i = 0; i < 255; i++) { CAddrInfo infoi = CAddrInfo( - CAddress(ResolveService("250.1.1." + boost::to_string(i)), NODE_NONE), - ResolveIP("250.1.1." + boost::to_string(i))); + CAddress(ResolveService("250.1.1." + std::to_string(i)), NODE_NONE), + ResolveIP("250.1.1." + std::to_string(i))); int bucket = infoi.GetTriedBucket(nKey1); buckets.insert(bucket); } @@ -448,8 +459,8 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket) buckets.clear(); for (int j = 0; j < 255; j++) { CAddrInfo infoj = CAddrInfo( - CAddress(ResolveService("250." + boost::to_string(j) + ".1.1"), NODE_NONE), - ResolveIP("250." + boost::to_string(j) + ".1.1")); + CAddress(ResolveService("250." + std::to_string(j) + ".1.1"), NODE_NONE), + ResolveIP("250." + std::to_string(j) + ".1.1")); int bucket = infoj.GetTriedBucket(nKey1); buckets.insert(bucket); } @@ -488,8 +499,8 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket) std::set<int> buckets; for (int i = 0; i < 255; i++) { CAddrInfo infoi = CAddrInfo( - CAddress(ResolveService("250.1.1." + boost::to_string(i)), NODE_NONE), - ResolveIP("250.1.1." + boost::to_string(i))); + CAddress(ResolveService("250.1.1." + std::to_string(i)), NODE_NONE), + ResolveIP("250.1.1." + std::to_string(i))); int bucket = infoi.GetNewBucket(nKey1); buckets.insert(bucket); } @@ -501,7 +512,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket) for (int j = 0; j < 4 * 255; j++) { CAddrInfo infoj = CAddrInfo(CAddress( ResolveService( - boost::to_string(250 + (j / 255)) + "." + boost::to_string(j % 256) + ".1.1"), NODE_NONE), + std::to_string(250 + (j / 255)) + "." + std::to_string(j % 256) + ".1.1"), NODE_NONE), ResolveIP("251.4.1.1")); int bucket = infoj.GetNewBucket(nKey1); buckets.insert(bucket); @@ -514,7 +525,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket) for (int p = 0; p < 255; p++) { CAddrInfo infoj = CAddrInfo( CAddress(ResolveService("250.1.1.1"), NODE_NONE), - ResolveIP("250." + boost::to_string(p) + ".1.1")); + ResolveIP("250." + std::to_string(p) + ".1.1")); int bucket = infoj.GetNewBucket(nKey1); buckets.insert(bucket); } @@ -522,4 +533,158 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket) // than 64 buckets. BOOST_CHECK(buckets.size() > 64); } + + +BOOST_AUTO_TEST_CASE(addrman_selecttriedcollision) +{ + CAddrManTest addrman; + + // Set addrman addr placement to be deterministic. + addrman.MakeDeterministic(); + + BOOST_CHECK(addrman.size() == 0); + + // Empty addrman should return blank addrman info. + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); + + // Add twenty two addresses. + CNetAddr source = ResolveIP("252.2.2.2"); + for (unsigned int i = 1; i < 23; i++) { + CService addr = ResolveService("250.1.1."+std::to_string(i)); + addrman.Add(CAddress(addr, NODE_NONE), source); + addrman.Good(addr); + + // No collisions yet. + BOOST_CHECK(addrman.size() == i); + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); + } + + // Ensure Good handles duplicates well. + for (unsigned int i = 1; i < 23; i++) { + CService addr = ResolveService("250.1.1."+std::to_string(i)); + addrman.Good(addr); + + BOOST_CHECK(addrman.size() == 22); + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); + } + +} + +BOOST_AUTO_TEST_CASE(addrman_noevict) +{ + CAddrManTest addrman; + + // Set addrman addr placement to be deterministic. + addrman.MakeDeterministic(); + + // Add twenty two addresses. + CNetAddr source = ResolveIP("252.2.2.2"); + for (unsigned int i = 1; i < 23; i++) { + CService addr = ResolveService("250.1.1."+std::to_string(i)); + addrman.Add(CAddress(addr, NODE_NONE), source); + addrman.Good(addr); + + // No collision yet. + BOOST_CHECK(addrman.size() == i); + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); + } + + // Collision between 23 and 19. + CService addr23 = ResolveService("250.1.1.23"); + addrman.Add(CAddress(addr23, NODE_NONE), source); + addrman.Good(addr23); + + BOOST_CHECK(addrman.size() == 23); + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "250.1.1.19:0"); + + // 23 should be discarded and 19 not evicted. + addrman.ResolveCollisions(); + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); + + // Lets create two collisions. + for (unsigned int i = 24; i < 33; i++) { + CService addr = ResolveService("250.1.1."+std::to_string(i)); + addrman.Add(CAddress(addr, NODE_NONE), source); + addrman.Good(addr); + + BOOST_CHECK(addrman.size() == i); + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); + } + + // Cause a collision. + CService addr33 = ResolveService("250.1.1.33"); + addrman.Add(CAddress(addr33, NODE_NONE), source); + addrman.Good(addr33); + BOOST_CHECK(addrman.size() == 33); + + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "250.1.1.27:0"); + + // Cause a second collision. + addrman.Add(CAddress(addr23, NODE_NONE), source); + addrman.Good(addr23); + BOOST_CHECK(addrman.size() == 33); + + BOOST_CHECK(addrman.SelectTriedCollision().ToString() != "[::]:0"); + addrman.ResolveCollisions(); + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); +} + +BOOST_AUTO_TEST_CASE(addrman_evictionworks) +{ + CAddrManTest addrman; + + // Set addrman addr placement to be deterministic. + addrman.MakeDeterministic(); + + BOOST_CHECK(addrman.size() == 0); + + // Empty addrman should return blank addrman info. + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); + + // Add twenty two addresses. + CNetAddr source = ResolveIP("252.2.2.2"); + for (unsigned int i = 1; i < 23; i++) { + CService addr = ResolveService("250.1.1."+std::to_string(i)); + addrman.Add(CAddress(addr, NODE_NONE), source); + addrman.Good(addr); + + // No collision yet. + BOOST_CHECK(addrman.size() == i); + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); + } + + // Collision between 23 and 19. + CService addr = ResolveService("250.1.1.23"); + addrman.Add(CAddress(addr, NODE_NONE), source); + addrman.Good(addr); + + BOOST_CHECK(addrman.size() == 23); + CAddrInfo info = addrman.SelectTriedCollision(); + BOOST_CHECK(info.ToString() == "250.1.1.19:0"); + + // Ensure test of address fails, so that it is evicted. + addrman.SimConnFail(info); + + // Should swap 23 for 19. + addrman.ResolveCollisions(); + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); + + // If 23 was swapped for 19, then this should cause no collisions. + addrman.Add(CAddress(addr, NODE_NONE), source); + addrman.Good(addr); + + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); + + // If we insert 19 is should collide with 23. + CService addr19 = ResolveService("250.1.1.19"); + addrman.Add(CAddress(addr19, NODE_NONE), source); + addrman.Good(addr19); + + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "250.1.1.23:0"); + + addrman.ResolveCollisions(); + BOOST_CHECK(addrman.SelectTriedCollision().ToString() == "[::]:0"); +} + + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/base32_tests.cpp b/src/test/base32_tests.cpp index b9ac62a437..1210c7a7ee 100644 --- a/src/test/base32_tests.cpp +++ b/src/test/base32_tests.cpp @@ -16,9 +16,9 @@ BOOST_AUTO_TEST_CASE(base32_testvectors) for (unsigned int i=0; i<sizeof(vstrIn)/sizeof(vstrIn[0]); i++) { std::string strEnc = EncodeBase32(vstrIn[i]); - BOOST_CHECK(strEnc == vstrOut[i]); + BOOST_CHECK_EQUAL(strEnc, vstrOut[i]); std::string strDec = DecodeBase32(vstrOut[i]); - BOOST_CHECK(strDec == vstrIn[i]); + BOOST_CHECK_EQUAL(strDec, vstrIn[i]); } } diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index a2d4f82695..f90d4f90cb 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -2,17 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <base58.h> - #include <test/data/base58_encode_decode.json.h> -#include <test/data/base58_keys_invalid.json.h> -#include <test/data/base58_keys_valid.json.h> -#include <key.h> -#include <script/script.h> +#include <base58.h> #include <test/test_bitcoin.h> -#include <uint256.h> -#include <util.h> #include <utilstrencodings.h> #include <univalue.h> @@ -73,135 +66,4 @@ BOOST_AUTO_TEST_CASE(base58_DecodeBase58) BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); } -// Goal: check that parsed keys match test payload -BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) -{ - UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid))); - CBitcoinSecret secret; - CTxDestination destination; - SelectParams(CBaseChainParams::MAIN); - - for (unsigned int idx = 0; idx < tests.size(); idx++) { - UniValue test = tests[idx]; - std::string strTest = test.write(); - if (test.size() < 3) { // Allow for extra stuff (useful for comments) - BOOST_ERROR("Bad test: " << strTest); - continue; - } - std::string exp_base58string = test[0].get_str(); - std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str()); - const UniValue &metadata = test[2].get_obj(); - bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); - SelectParams(find_value(metadata, "chain").get_str()); - bool try_case_flip = find_value(metadata, "tryCaseFlip").isNull() ? false : find_value(metadata, "tryCaseFlip").get_bool(); - if (isPrivkey) { - bool isCompressed = find_value(metadata, "isCompressed").get_bool(); - // Must be valid private key - BOOST_CHECK_MESSAGE(secret.SetString(exp_base58string), "!SetString:"+ strTest); - BOOST_CHECK_MESSAGE(secret.IsValid(), "!IsValid:" + strTest); - CKey privkey = secret.GetKey(); - BOOST_CHECK_MESSAGE(privkey.IsCompressed() == isCompressed, "compressed mismatch:" + strTest); - BOOST_CHECK_MESSAGE(privkey.size() == exp_payload.size() && std::equal(privkey.begin(), privkey.end(), exp_payload.begin()), "key mismatch:" + strTest); - - // Private key must be invalid public key - destination = DecodeDestination(exp_base58string); - BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest); - } else { - // Must be valid public key - destination = DecodeDestination(exp_base58string); - CScript script = GetScriptForDestination(destination); - BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest); - BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload)); - - // Try flipped case version - for (char& c : exp_base58string) { - if (c >= 'a' && c <= 'z') { - c = (c - 'a') + 'A'; - } else if (c >= 'A' && c <= 'Z') { - c = (c - 'A') + 'a'; - } - } - destination = DecodeDestination(exp_base58string); - BOOST_CHECK_MESSAGE(IsValidDestination(destination) == try_case_flip, "!IsValid case flipped:" + strTest); - if (IsValidDestination(destination)) { - script = GetScriptForDestination(destination); - BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload)); - } - - // Public key must be invalid private key - secret.SetString(exp_base58string); - BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid pubkey as privkey:" + strTest); - } - } -} - -// Goal: check that generated keys match test vectors -BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) -{ - UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid))); - - for (unsigned int idx = 0; idx < tests.size(); idx++) { - UniValue test = tests[idx]; - std::string strTest = test.write(); - if (test.size() < 3) // Allow for extra stuff (useful for comments) - { - BOOST_ERROR("Bad test: " << strTest); - continue; - } - std::string exp_base58string = test[0].get_str(); - std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str()); - const UniValue &metadata = test[2].get_obj(); - bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); - SelectParams(find_value(metadata, "chain").get_str()); - if (isPrivkey) { - bool isCompressed = find_value(metadata, "isCompressed").get_bool(); - CKey key; - key.Set(exp_payload.begin(), exp_payload.end(), isCompressed); - assert(key.IsValid()); - CBitcoinSecret secret; - secret.SetKey(key); - BOOST_CHECK_MESSAGE(secret.ToString() == exp_base58string, "result mismatch: " + strTest); - } else { - CTxDestination dest; - CScript exp_script(exp_payload.begin(), exp_payload.end()); - ExtractDestination(exp_script, dest); - std::string address = EncodeDestination(dest); - - BOOST_CHECK_EQUAL(address, exp_base58string); - } - } - - SelectParams(CBaseChainParams::MAIN); -} - - -// Goal: check that base58 parsing code is robust against a variety of corrupted data -BOOST_AUTO_TEST_CASE(base58_keys_invalid) -{ - UniValue tests = read_json(std::string(json_tests::base58_keys_invalid, json_tests::base58_keys_invalid + sizeof(json_tests::base58_keys_invalid))); // Negative testcases - CBitcoinSecret secret; - CTxDestination destination; - - for (unsigned int idx = 0; idx < tests.size(); idx++) { - UniValue test = tests[idx]; - std::string strTest = test.write(); - if (test.size() < 1) // Allow for extra stuff (useful for comments) - { - BOOST_ERROR("Bad test: " << strTest); - continue; - } - std::string exp_base58string = test[0].get_str(); - - // must be invalid as public and as private key - for (auto chain : { CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::REGTEST }) { - SelectParams(chain); - destination = DecodeDestination(exp_base58string); - BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey in mainnet:" + strTest); - secret.SetString(exp_base58string); - BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey in mainnet:" + strTest); - } - } -} - - BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/base64_tests.cpp b/src/test/base64_tests.cpp index b31f73f3b5..f785cede81 100644 --- a/src/test/base64_tests.cpp +++ b/src/test/base64_tests.cpp @@ -16,9 +16,9 @@ BOOST_AUTO_TEST_CASE(base64_testvectors) for (unsigned int i=0; i<sizeof(vstrIn)/sizeof(vstrIn[0]); i++) { std::string strEnc = EncodeBase64(vstrIn[i]); - BOOST_CHECK(strEnc == vstrOut[i]); + BOOST_CHECK_EQUAL(strEnc, vstrOut[i]); std::string strDec = DecodeBase64(strEnc); - BOOST_CHECK(strDec == vstrIn[i]); + BOOST_CHECK_EQUAL(strDec, vstrIn[i]); } } diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp index 438ddc177d..3c9ff1877d 100644 --- a/src/test/bip32_tests.cpp +++ b/src/test/bip32_tests.cpp @@ -4,8 +4,8 @@ #include <boost/test/unit_test.hpp> -#include <base58.h> #include <key.h> +#include <key_io.h> #include <uint256.h> #include <util.h> #include <utilstrencodings.h> @@ -99,20 +99,12 @@ void RunTest(const TestVector &test) { pubkey.Encode(data); // Test private key - CBitcoinExtKey b58key; b58key.SetKey(key); - BOOST_CHECK(b58key.ToString() == derive.prv); - - CBitcoinExtKey b58keyDecodeCheck(derive.prv); - CExtKey checkKey = b58keyDecodeCheck.GetKey(); - assert(checkKey == key); //ensure a base58 decoded key also matches + BOOST_CHECK(EncodeExtKey(key) == derive.prv); + BOOST_CHECK(DecodeExtKey(derive.prv) == key); //ensure a base58 decoded key also matches // Test public key - CBitcoinExtPubKey b58pubkey; b58pubkey.SetKey(pubkey); - BOOST_CHECK(b58pubkey.ToString() == derive.pub); - - CBitcoinExtPubKey b58PubkeyDecodeCheck(derive.pub); - CExtPubKey checkPubKey = b58PubkeyDecodeCheck.GetKey(); - assert(checkPubKey == pubkey); //ensure a base58 decoded pubkey also matches + BOOST_CHECK(EncodeExtPubKey(pubkey) == derive.pub); + BOOST_CHECK(DecodeExtPubKey(derive.pub) == pubkey); //ensure a base58 decoded pubkey also matches // Derive new keys CExtKey keyNew; diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index af5533b109..73c8eb5168 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -4,9 +4,9 @@ #include <bloom.h> -#include <base58.h> #include <clientversion.h> #include <key.h> +#include <key_io.h> #include <merkleblock.h> #include <primitives/block.h> #include <random.h> @@ -85,10 +85,7 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize_with_tweak) BOOST_AUTO_TEST_CASE(bloom_create_insert_key) { std::string strSecret = std::string("5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C"); - CBitcoinSecret vchSecret; - BOOST_CHECK(vchSecret.SetString(strSecret)); - - CKey key = vchSecret.GetKey(); + CKey key = DecodeSecret(strSecret); CPubKey pubkey = key.GetPubKey(); std::vector<unsigned char> vchPubKey(pubkey.begin(), pubkey.end()); diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp index 42f9dd0600..8e0ec5243b 100644 --- a/src/test/checkqueue_tests.cpp +++ b/src/test/checkqueue_tests.cpp @@ -406,11 +406,11 @@ BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks) boost::thread_group tg; std::mutex m; std::condition_variable cv; + bool has_lock{false}; + bool has_tried{false}; + bool done{false}; + bool done_ack{false}; { - bool has_lock {false}; - bool has_tried {false}; - bool done {false}; - bool done_ack {false}; std::unique_lock<std::mutex> l(m); tg.create_thread([&]{ CCheckQueueControl<FakeCheck> control(queue.get()); diff --git a/src/test/data/base58_keys_invalid.json b/src/test/data/key_io_invalid.json index 2056c7491c..2056c7491c 100644 --- a/src/test/data/base58_keys_invalid.json +++ b/src/test/data/key_io_invalid.json diff --git a/src/test/data/base58_keys_valid.json b/src/test/data/key_io_valid.json index 8418a6002d..8418a6002d 100644 --- a/src/test/data/base58_keys_valid.json +++ b/src/test/data/key_io_valid.json diff --git a/src/test/data/script_tests.json b/src/test/data/script_tests.json index 63f43c0fc6..ccefe52246 100644 --- a/src/test/data/script_tests.json +++ b/src/test/data/script_tests.json @@ -168,6 +168,18 @@ ["1 0 BOOLOR", "NOP", "P2SH,STRICTENC", "OK"], ["0 1 BOOLOR", "NOP", "P2SH,STRICTENC", "OK"], ["0 0 BOOLOR", "NOT", "P2SH,STRICTENC", "OK"], +["0x01 0x80", "DUP BOOLOR", "P2SH,STRICTENC", "EVAL_FALSE", "negative-0 negative-0 BOOLOR"], +["0x01 0x00", "DUP BOOLOR", "P2SH,STRICTENC", "EVAL_FALSE", " non-minimal-0 non-minimal-0 BOOLOR"], +["0x01 0x81", "DUP BOOLOR", "P2SH,STRICTENC", "OK", "-1 -1 BOOLOR"], +["0x01 0x80", "DUP BOOLAND", "P2SH,STRICTENC", "EVAL_FALSE", "negative-0 negative-0 BOOLAND"], +["0x01 0x00", "DUP BOOLAND", "P2SH,STRICTENC", "EVAL_FALSE", " non-minimal-0 non-minimal-0 BOOLAND"], +["0x01 0x81", "DUP BOOLAND", "P2SH,STRICTENC", "OK", "-1 -1 BOOLAND"], +["0x01 0x00", "NOT", "P2SH,STRICTENC", "OK", "non-minimal-0 NOT"], +["0x01 0x80", "NOT", "P2SH,STRICTENC", "OK", "negative-0 NOT"], +["0x01 0x81", "NOT", "P2SH,STRICTENC", "EVAL_FALSE", "negative 1 NOT"], +["0x01 0x80 0", "NUMEQUAL", "P2SH", "OK", "-0 0 NUMEQUAL"], +["0x01 0x00 0", "NUMEQUAL", "P2SH", "OK", "non-minimal-0 0 NUMEQUAL"], +["0x02 0x00 0x00 0", "NUMEQUAL", "P2SH", "OK", "non-minimal-0 0 NUMEQUAL"], ["16 17 BOOLOR", "NOP", "P2SH,STRICTENC", "OK"], ["11 10 1 ADD", "NUMEQUAL", "P2SH,STRICTENC", "OK"], ["11 10 1 ADD", "NUMEQUALVERIFY 1", "P2SH,STRICTENC", "OK"], diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json index 09442b7f9f..f8a1347c31 100644 --- a/src/test/data/tx_invalid.json +++ b/src/test/data/tx_invalid.json @@ -92,11 +92,11 @@ [[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a010047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"], -["As above, but using a OP_1"], +["As above, but using an OP_1"], [[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000495147304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"], -["As above, but using a OP_1NEGATE"], +["As above, but using an OP_1NEGATE"], [[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000494f47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"], diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json index a845083636..0bcecc58fe 100644 --- a/src/test/data/tx_valid.json +++ b/src/test/data/tx_valid.json @@ -23,11 +23,11 @@ [[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a01ff47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"], -["As above, but using a OP_1"], +["As above, but using an OP_1"], [[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000495147304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"], -["As above, but using a OP_1NEGATE"], +["As above, but using an OP_1NEGATE"], [[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000494f47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"], @@ -516,5 +516,9 @@ [[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7500, "0x00 0x20 0x9b66c15b4e0b4eb49fa877982cafded24859fe5b0e2dbfbe4f0df1de7743fd52", 200000]], "010000000001019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a6628964c1d000000ffffffff0101000000000000000007004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960101022102966f109c54e85d3aee8321301136cedeb9fc710fdef58a9de8a73942f8e567c021034ffc99dd9a79dd3cb31e2ab3e0b09e0e67db41ac068c625cd1f491576016c84e9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596017500000000", "P2SH,WITNESS"], +["Test long outputs, which are streamed using length-prefixed bitcoin strings. This might be surprising."], +[[["1111111111111111111111111111111111111111111111111111111111111111", 0, "0x00 0x14 0x751e76e8199196d454941c45d1b3a323f1433bd6", 5000000]], +"0100000000010111111111111111111111111111111111111111111111111111111111111111110000000000ffffffff0130244c0000000000fd02014cdc1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111175210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac02483045022100c1a4a6581996a7fdfea77d58d537955a5655c1d619b6f3ab6874f28bb2e19708022056402db6fede03caae045a3be616a1a2d0919a475ed4be828dc9ff21f24063aa01210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179800000000", "P2SH,WITNESS"], + ["Make diffs cleaner by leaving a comment here without comma at the end"] ] diff --git a/src/test/key_io_tests.cpp b/src/test/key_io_tests.cpp new file mode 100644 index 0000000000..1ac1e0015b --- /dev/null +++ b/src/test/key_io_tests.cpp @@ -0,0 +1,149 @@ +// Copyright (c) 2011-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <test/data/key_io_invalid.json.h> +#include <test/data/key_io_valid.json.h> + +#include <key.h> +#include <key_io.h> +#include <script/script.h> +#include <utilstrencodings.h> +#include <test/test_bitcoin.h> + +#include <boost/test/unit_test.hpp> + +#include <univalue.h> + +extern UniValue read_json(const std::string& jsondata); + +BOOST_FIXTURE_TEST_SUITE(key_io_tests, BasicTestingSetup) + +// Goal: check that parsed keys match test payload +BOOST_AUTO_TEST_CASE(key_io_valid_parse) +{ + UniValue tests = read_json(std::string(json_tests::key_io_valid, json_tests::key_io_valid + sizeof(json_tests::key_io_valid))); + CKey privkey; + CTxDestination destination; + SelectParams(CBaseChainParams::MAIN); + + for (unsigned int idx = 0; idx < tests.size(); idx++) { + UniValue test = tests[idx]; + std::string strTest = test.write(); + if (test.size() < 3) { // Allow for extra stuff (useful for comments) + BOOST_ERROR("Bad test: " << strTest); + continue; + } + std::string exp_base58string = test[0].get_str(); + std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str()); + const UniValue &metadata = test[2].get_obj(); + bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); + SelectParams(find_value(metadata, "chain").get_str()); + bool try_case_flip = find_value(metadata, "tryCaseFlip").isNull() ? false : find_value(metadata, "tryCaseFlip").get_bool(); + if (isPrivkey) { + bool isCompressed = find_value(metadata, "isCompressed").get_bool(); + // Must be valid private key + privkey = DecodeSecret(exp_base58string); + BOOST_CHECK_MESSAGE(privkey.IsValid(), "!IsValid:" + strTest); + BOOST_CHECK_MESSAGE(privkey.IsCompressed() == isCompressed, "compressed mismatch:" + strTest); + BOOST_CHECK_MESSAGE(privkey.size() == exp_payload.size() && std::equal(privkey.begin(), privkey.end(), exp_payload.begin()), "key mismatch:" + strTest); + + // Private key must be invalid public key + destination = DecodeDestination(exp_base58string); + BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest); + } else { + // Must be valid public key + destination = DecodeDestination(exp_base58string); + CScript script = GetScriptForDestination(destination); + BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest); + BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload)); + + // Try flipped case version + for (char& c : exp_base58string) { + if (c >= 'a' && c <= 'z') { + c = (c - 'a') + 'A'; + } else if (c >= 'A' && c <= 'Z') { + c = (c - 'A') + 'a'; + } + } + destination = DecodeDestination(exp_base58string); + BOOST_CHECK_MESSAGE(IsValidDestination(destination) == try_case_flip, "!IsValid case flipped:" + strTest); + if (IsValidDestination(destination)) { + script = GetScriptForDestination(destination); + BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload)); + } + + // Public key must be invalid private key + privkey = DecodeSecret(exp_base58string); + BOOST_CHECK_MESSAGE(!privkey.IsValid(), "IsValid pubkey as privkey:" + strTest); + } + } +} + +// Goal: check that generated keys match test vectors +BOOST_AUTO_TEST_CASE(key_io_valid_gen) +{ + UniValue tests = read_json(std::string(json_tests::key_io_valid, json_tests::key_io_valid + sizeof(json_tests::key_io_valid))); + + for (unsigned int idx = 0; idx < tests.size(); idx++) { + UniValue test = tests[idx]; + std::string strTest = test.write(); + if (test.size() < 3) // Allow for extra stuff (useful for comments) + { + BOOST_ERROR("Bad test: " << strTest); + continue; + } + std::string exp_base58string = test[0].get_str(); + std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str()); + const UniValue &metadata = test[2].get_obj(); + bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); + SelectParams(find_value(metadata, "chain").get_str()); + if (isPrivkey) { + bool isCompressed = find_value(metadata, "isCompressed").get_bool(); + CKey key; + key.Set(exp_payload.begin(), exp_payload.end(), isCompressed); + assert(key.IsValid()); + BOOST_CHECK_MESSAGE(EncodeSecret(key) == exp_base58string, "result mismatch: " + strTest); + } else { + CTxDestination dest; + CScript exp_script(exp_payload.begin(), exp_payload.end()); + ExtractDestination(exp_script, dest); + std::string address = EncodeDestination(dest); + + BOOST_CHECK_EQUAL(address, exp_base58string); + } + } + + SelectParams(CBaseChainParams::MAIN); +} + + +// Goal: check that base58 parsing code is robust against a variety of corrupted data +BOOST_AUTO_TEST_CASE(key_io_invalid) +{ + UniValue tests = read_json(std::string(json_tests::key_io_invalid, json_tests::key_io_invalid + sizeof(json_tests::key_io_invalid))); // Negative testcases + CKey privkey; + CTxDestination destination; + + for (unsigned int idx = 0; idx < tests.size(); idx++) { + UniValue test = tests[idx]; + std::string strTest = test.write(); + if (test.size() < 1) // Allow for extra stuff (useful for comments) + { + BOOST_ERROR("Bad test: " << strTest); + continue; + } + std::string exp_base58string = test[0].get_str(); + + // must be invalid as public and as private key + for (auto chain : { CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::REGTEST }) { + SelectParams(chain); + destination = DecodeDestination(exp_base58string); + BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey in mainnet:" + strTest); + privkey = DecodeSecret(exp_base58string); + BOOST_CHECK_MESSAGE(!privkey.IsValid(), "IsValid privkey in mainnet:" + strTest); + } + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index 55ee1ecf6b..64c57f0705 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -4,7 +4,7 @@ #include <key.h> -#include <base58.h> +#include <key_io.h> #include <script/script.h> #include <uint256.h> #include <util.h> @@ -32,21 +32,16 @@ BOOST_FIXTURE_TEST_SUITE(key_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(key_test1) { - CBitcoinSecret bsecret1, bsecret2, bsecret1C, bsecret2C, baddress1; - BOOST_CHECK( bsecret1.SetString (strSecret1)); - BOOST_CHECK( bsecret2.SetString (strSecret2)); - BOOST_CHECK( bsecret1C.SetString(strSecret1C)); - BOOST_CHECK( bsecret2C.SetString(strSecret2C)); - BOOST_CHECK(!baddress1.SetString(strAddressBad)); - - CKey key1 = bsecret1.GetKey(); - BOOST_CHECK(key1.IsCompressed() == false); - CKey key2 = bsecret2.GetKey(); - BOOST_CHECK(key2.IsCompressed() == false); - CKey key1C = bsecret1C.GetKey(); - BOOST_CHECK(key1C.IsCompressed() == true); - CKey key2C = bsecret2C.GetKey(); - BOOST_CHECK(key2C.IsCompressed() == true); + CKey key1 = DecodeSecret(strSecret1); + BOOST_CHECK(key1.IsValid() && !key1.IsCompressed()); + CKey key2 = DecodeSecret(strSecret2); + BOOST_CHECK(key2.IsValid() && !key2.IsCompressed()); + CKey key1C = DecodeSecret(strSecret1C); + BOOST_CHECK(key1C.IsValid() && key1C.IsCompressed()); + CKey key2C = DecodeSecret(strSecret2C); + BOOST_CHECK(key2C.IsValid() && key2C.IsCompressed()); + CKey bad_key = DecodeSecret(strAddressBad); + BOOST_CHECK(!bad_key.IsValid()); CPubKey pubkey1 = key1. GetPubKey(); CPubKey pubkey2 = key2. GetPubKey(); diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index ca57f58905..e03234060d 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -171,7 +171,7 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test) ipv4Addr.s_addr = 0xa0b0c001; CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK); - std::string pszDest = ""; + std::string pszDest; bool fInboundIn = false; // Test that fFeeler is false by default. diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp index db9162c0db..01c3a6cedd 100644 --- a/src/test/prevector_tests.cpp +++ b/src/test/prevector_tests.cpp @@ -206,7 +206,7 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt) test.erase(InsecureRandRange(test.size())); } if (InsecureRandBits(3) == 2) { - int new_size = std::max<int>(0, std::min<int>(30, test.size() + (InsecureRandRange(5)) - 2)); + int new_size = std::max(0, std::min(30, (int)test.size() + (int)InsecureRandRange(5) - 2)); test.resize(new_size); } if (InsecureRandBits(3) == 3) { diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index ed86413b2f..8d9f80ada0 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -5,8 +5,8 @@ #include <rpc/server.h> #include <rpc/client.h> -#include <base58.h> #include <core_io.h> +#include <key_io.h> #include <netbase.h> #include <test/test_bitcoin.h> @@ -52,7 +52,6 @@ BOOST_AUTO_TEST_CASE(rpc_rawparams) BOOST_CHECK_THROW(CallRPC("createrawtransaction"), std::runtime_error); BOOST_CHECK_THROW(CallRPC("createrawtransaction null null"), std::runtime_error); BOOST_CHECK_THROW(CallRPC("createrawtransaction not_array"), std::runtime_error); - BOOST_CHECK_THROW(CallRPC("createrawtransaction [] []"), std::runtime_error); BOOST_CHECK_THROW(CallRPC("createrawtransaction {} {}"), std::runtime_error); BOOST_CHECK_NO_THROW(CallRPC("createrawtransaction [] {}")); BOOST_CHECK_THROW(CallRPC("createrawtransaction [] {} extra"), std::runtime_error); @@ -69,14 +68,6 @@ BOOST_AUTO_TEST_CASE(rpc_rawparams) BOOST_CHECK_NO_THROW(r = CallRPC(std::string("decoderawtransaction ")+rawtx+" false")); BOOST_CHECK_THROW(r = CallRPC(std::string("decoderawtransaction ")+rawtx+" false extra"), std::runtime_error); - BOOST_CHECK_THROW(CallRPC("signrawtransaction"), std::runtime_error); - BOOST_CHECK_THROW(CallRPC("signrawtransaction null"), std::runtime_error); - BOOST_CHECK_THROW(CallRPC("signrawtransaction ff00"), std::runtime_error); - BOOST_CHECK_NO_THROW(CallRPC(std::string("signrawtransaction ")+rawtx)); - BOOST_CHECK_NO_THROW(CallRPC(std::string("signrawtransaction ")+rawtx+" null null NONE|ANYONECANPAY")); - BOOST_CHECK_NO_THROW(CallRPC(std::string("signrawtransaction ")+rawtx+" [] [] NONE|ANYONECANPAY")); - BOOST_CHECK_THROW(CallRPC(std::string("signrawtransaction ")+rawtx+" null null badenum"), std::runtime_error); - // Only check failure cases for sendrawtransaction, there's no network to send to... BOOST_CHECK_THROW(CallRPC("sendrawtransaction"), std::runtime_error); BOOST_CHECK_THROW(CallRPC("sendrawtransaction null"), std::runtime_error); @@ -119,9 +110,9 @@ BOOST_AUTO_TEST_CASE(rpc_rawsign) std::string notsigned = r.get_str(); std::string privkey1 = "\"KzsXybp9jX64P5ekX1KUxRQ79Jht9uzW7LorgwE65i5rWACL6LQe\""; std::string privkey2 = "\"Kyhdf5LuKTRx4ge69ybABsiUAWjVRK4XGxAKk2FQLp2HjGMy87Z4\""; - r = CallRPC(std::string("signrawtransaction ")+notsigned+" "+prevout+" "+"[]"); + r = CallRPC(std::string("signrawtransactionwithkey ")+notsigned+" [] "+prevout); BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == false); - r = CallRPC(std::string("signrawtransaction ")+notsigned+" "+prevout+" "+"["+privkey1+","+privkey2+"]"); + r = CallRPC(std::string("signrawtransactionwithkey ")+notsigned+" ["+privkey1+","+privkey2+"] "+prevout); BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == true); } diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp index 760f933abc..179df7dd38 100644 --- a/src/test/scheduler_tests.cpp +++ b/src/test/scheduler_tests.cpp @@ -56,8 +56,8 @@ BOOST_AUTO_TEST_CASE(manythreads) int counter[10] = { 0 }; FastRandomContext rng(42); auto zeroToNine = [](FastRandomContext& rc) -> int { return rc.randrange(10); }; // [0, 9] - auto randomMsec = [](FastRandomContext& rc) -> int { return -11 + rc.randrange(1012); }; // [-11, 1000] - auto randomDelta = [](FastRandomContext& rc) -> int { return -1000 + rc.randrange(2001); }; // [-1000, 1000] + auto randomMsec = [](FastRandomContext& rc) -> int { return -11 + (int)rc.randrange(1012); }; // [-11, 1000] + auto randomDelta = [](FastRandomContext& rc) -> int { return -1000 + (int)rc.randrange(2001); }; // [-1000, 1000] boost::chrono::system_clock::time_point start = boost::chrono::system_clock::now(); boost::chrono::system_clock::time_point now = start; diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index bdd44489f4..95c4825b84 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -28,6 +28,9 @@ void CConnmanTest::AddNode(CNode& node) void CConnmanTest::ClearNodes() { LOCK(g_connman->cs_vNodes); + for (CNode* node : g_connman->vNodes) { + delete node; + } g_connman->vNodes.clear(); } diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 55d60d95e9..84b61bea86 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -13,6 +13,11 @@ #include <stdint.h> #include <vector> +#ifndef WIN32 +#include <signal.h> +#include <sys/types.h> +#include <sys/wait.h> +#endif #include <boost/test/unit_test.hpp> @@ -77,6 +82,20 @@ BOOST_AUTO_TEST_CASE(util_HexStr) "04 67 8a fd b0"); BOOST_CHECK_EQUAL( + HexStr(ParseHex_expected + sizeof(ParseHex_expected), + ParseHex_expected + sizeof(ParseHex_expected)), + ""); + + BOOST_CHECK_EQUAL( + HexStr(ParseHex_expected + sizeof(ParseHex_expected), + ParseHex_expected + sizeof(ParseHex_expected), true), + ""); + + BOOST_CHECK_EQUAL( + HexStr(ParseHex_expected, ParseHex_expected), + ""); + + BOOST_CHECK_EQUAL( HexStr(ParseHex_expected, ParseHex_expected, true), ""); @@ -85,6 +104,58 @@ BOOST_AUTO_TEST_CASE(util_HexStr) BOOST_CHECK_EQUAL( HexStr(ParseHex_vec, true), "04 67 8a fd b0"); + + BOOST_CHECK_EQUAL( + HexStr(ParseHex_vec.rbegin(), ParseHex_vec.rend()), + "b0fd8a6704" + ); + + BOOST_CHECK_EQUAL( + HexStr(ParseHex_vec.rbegin(), ParseHex_vec.rend(), true), + "b0 fd 8a 67 04" + ); + + BOOST_CHECK_EQUAL( + HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected), + std::reverse_iterator<const uint8_t *>(ParseHex_expected)), + "" + ); + + BOOST_CHECK_EQUAL( + HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected), + std::reverse_iterator<const uint8_t *>(ParseHex_expected), true), + "" + ); + + BOOST_CHECK_EQUAL( + HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 1), + std::reverse_iterator<const uint8_t *>(ParseHex_expected)), + "04" + ); + + BOOST_CHECK_EQUAL( + HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 1), + std::reverse_iterator<const uint8_t *>(ParseHex_expected), true), + "04" + ); + + BOOST_CHECK_EQUAL( + HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 5), + std::reverse_iterator<const uint8_t *>(ParseHex_expected)), + "b0fd8a6704" + ); + + BOOST_CHECK_EQUAL( + HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 5), + std::reverse_iterator<const uint8_t *>(ParseHex_expected), true), + "b0 fd 8a 67 04" + ); + + BOOST_CHECK_EQUAL( + HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 65), + std::reverse_iterator<const uint8_t *>(ParseHex_expected)), + "5f1df16b2b704c8a578d0bbaf74d385cde12c11ee50455f3c438ef4c3fbcf649b6de611feae06279a60939e028a8d65c10b73071a6f16719274855feb0fd8a6704" + ); } @@ -93,10 +164,27 @@ BOOST_AUTO_TEST_CASE(util_DateTimeStrFormat) BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 0), "1970-01-01 00:00:00"); BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 0x7FFFFFFF), "2038-01-19 03:14:07"); BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 1317425777), "2011-09-30 23:36:17"); + BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", 1317425777), "2011-09-30T23:36:17Z"); + BOOST_CHECK_EQUAL(DateTimeStrFormat("%H:%M:%SZ", 1317425777), "23:36:17Z"); BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M", 1317425777), "2011-09-30 23:36"); BOOST_CHECK_EQUAL(DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", 1317425777), "Fri, 30 Sep 2011 23:36:17 +0000"); } +BOOST_AUTO_TEST_CASE(util_FormatISO8601DateTime) +{ + BOOST_CHECK_EQUAL(FormatISO8601DateTime(1317425777), "2011-09-30T23:36:17Z"); +} + +BOOST_AUTO_TEST_CASE(util_FormatISO8601Date) +{ + BOOST_CHECK_EQUAL(FormatISO8601Date(1317425777), "2011-09-30"); +} + +BOOST_AUTO_TEST_CASE(util_FormatISO8601Time) +{ + BOOST_CHECK_EQUAL(FormatISO8601Time(1317425777), "23:36:17Z"); +} + class TestArgsManager : public ArgsManager { public: @@ -603,4 +691,130 @@ BOOST_AUTO_TEST_CASE(test_ParseFixedPoint) BOOST_CHECK(!ParseFixedPoint("1.", 8, &amount)); } +static void TestOtherThread(fs::path dirname, std::string lockname, bool *result) +{ + *result = LockDirectory(dirname, lockname); +} + +#ifndef WIN32 // Cannot do this test on WIN32 due to lack of fork() +static constexpr char LockCommand = 'L'; +static constexpr char UnlockCommand = 'U'; +static constexpr char ExitCommand = 'X'; + +static void TestOtherProcess(fs::path dirname, std::string lockname, int fd) +{ + char ch; + int rv; + while (true) { + rv = read(fd, &ch, 1); // Wait for command + assert(rv == 1); + switch(ch) { + case LockCommand: + ch = LockDirectory(dirname, lockname); + rv = write(fd, &ch, 1); + assert(rv == 1); + break; + case UnlockCommand: + ReleaseDirectoryLocks(); + ch = true; // Always succeeds + rv = write(fd, &ch, 1); + break; + case ExitCommand: + close(fd); + exit(0); + default: + assert(0); + } + } +} +#endif + +BOOST_AUTO_TEST_CASE(test_LockDirectory) +{ + fs::path dirname = fs::temp_directory_path() / fs::unique_path(); + const std::string lockname = ".lock"; +#ifndef WIN32 + // Revert SIGCHLD to default, otherwise boost.test will catch and fail on + // it: there is BOOST_TEST_IGNORE_SIGCHLD but that only works when defined + // at build-time of the boost library + void (*old_handler)(int) = signal(SIGCHLD, SIG_DFL); + + // Fork another process for testing before creating the lock, so that we + // won't fork while holding the lock (which might be undefined, and is not + // relevant as test case as that is avoided with -daemonize). + int fd[2]; + BOOST_CHECK_EQUAL(socketpair(AF_UNIX, SOCK_STREAM, 0, fd), 0); + pid_t pid = fork(); + if (!pid) { + BOOST_CHECK_EQUAL(close(fd[1]), 0); // Child: close parent end + TestOtherProcess(dirname, lockname, fd[0]); + } + BOOST_CHECK_EQUAL(close(fd[0]), 0); // Parent: close child end +#endif + // Lock on non-existent directory should fail + BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname), false); + + fs::create_directories(dirname); + + // Probing lock on new directory should succeed + BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true); + + // Persistent lock on new directory should succeed + BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname), true); + + // Another lock on the directory from the same thread should succeed + BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname), true); + + // Another lock on the directory from a different thread within the same process should succeed + bool threadresult; + std::thread thr(TestOtherThread, dirname, lockname, &threadresult); + thr.join(); + BOOST_CHECK_EQUAL(threadresult, true); +#ifndef WIN32 + // Try to acquire lock in child process while we're holding it, this should fail. + char ch; + BOOST_CHECK_EQUAL(write(fd[1], &LockCommand, 1), 1); + BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1); + BOOST_CHECK_EQUAL((bool)ch, false); + + // Give up our lock + ReleaseDirectoryLocks(); + // Probing lock from our side now should succeed, but not hold on to the lock. + BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true); + + // Try to acquire the lock in the child process, this should be successful. + BOOST_CHECK_EQUAL(write(fd[1], &LockCommand, 1), 1); + BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1); + BOOST_CHECK_EQUAL((bool)ch, true); + + // When we try to probe the lock now, it should fail. + BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), false); + + // Unlock the lock in the child process + BOOST_CHECK_EQUAL(write(fd[1], &UnlockCommand, 1), 1); + BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1); + BOOST_CHECK_EQUAL((bool)ch, true); + + // When we try to probe the lock now, it should succeed. + BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true); + + // Re-lock the lock in the child process, then wait for it to exit, check + // successful return. After that, we check that exiting the process + // has released the lock as we would expect by probing it. + int processstatus; + BOOST_CHECK_EQUAL(write(fd[1], &LockCommand, 1), 1); + BOOST_CHECK_EQUAL(write(fd[1], &ExitCommand, 1), 1); + BOOST_CHECK_EQUAL(waitpid(pid, &processstatus, 0), pid); + BOOST_CHECK_EQUAL(processstatus, 0); + BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true); + + // Restore SIGCHLD + signal(SIGCHLD, old_handler); + BOOST_CHECK_EQUAL(close(fd[1]), 0); // Close our side of the socketpair +#endif + // Clean up + ReleaseDirectoryLocks(); + fs::remove_all(dirname); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index d875008ef2..717d1cf7e5 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -731,7 +731,7 @@ void TorController::reconnect_cb(evutil_socket_t fd, short what, void *arg) /****** Thread ********/ static struct event_base *gBase; -static boost::thread torControlThread; +static std::thread torControlThread; static void TorControlThread() { @@ -740,7 +740,7 @@ static void TorControlThread() event_base_dispatch(gBase); } -void StartTorControl(boost::thread_group& threadGroup, CScheduler& scheduler) +void StartTorControl() { assert(!gBase); #ifdef WIN32 @@ -754,7 +754,7 @@ void StartTorControl(boost::thread_group& threadGroup, CScheduler& scheduler) return; } - torControlThread = boost::thread(boost::bind(&TraceThread<void (*)()>, "torcontrol", &TorControlThread)); + torControlThread = std::thread(std::bind(&TraceThread<void (*)()>, "torcontrol", &TorControlThread)); } void InterruptTorControl() diff --git a/src/torcontrol.h b/src/torcontrol.h index 20514f7bbf..2be6701fa5 100644 --- a/src/torcontrol.h +++ b/src/torcontrol.h @@ -13,7 +13,7 @@ extern const std::string DEFAULT_TOR_CONTROL; static const bool DEFAULT_LISTEN_ONION = true; -void StartTorControl(boost::thread_group& threadGroup, CScheduler& scheduler); +void StartTorControl(); void InterruptTorControl(); void StopTorControl(); diff --git a/src/util.cpp b/src/util.cpp index 6738bbc6e4..62cdce3012 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -79,6 +79,7 @@ #include <openssl/crypto.h> #include <openssl/rand.h> #include <openssl/conf.h> +#include <thread> // Application startup time (used for uptime calculation) const int64_t nStartupTime = GetTime(); @@ -314,12 +315,14 @@ static std::string LogTimestampStr(const std::string &str, std::atomic_bool *fSt if (*fStartedNewLine) { int64_t nTimeMicros = GetTimeMicros(); - strStamped = DateTimeStrFormat("%Y-%m-%d %H:%M:%S", nTimeMicros/1000000); - if (fLogTimeMicros) - strStamped += strprintf(".%06d", nTimeMicros%1000000); + strStamped = FormatISO8601DateTime(nTimeMicros/1000000); + if (fLogTimeMicros) { + strStamped.pop_back(); + strStamped += strprintf(".%06dZ", nTimeMicros%1000000); + } int64_t mocktime = GetMockTime(); if (mocktime) { - strStamped += " (mocktime: " + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", mocktime) + ")"; + strStamped += " (mocktime: " + FormatISO8601DateTime(mocktime) + ")"; } strStamped += ' ' + str; } else @@ -373,20 +376,37 @@ int LogPrintStr(const std::string &str) return ret; } +/** A map that contains all the currently held directory locks. After + * successful locking, these will be held here until the global destructor + * cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks + * is called. + */ +static std::map<std::string, std::unique_ptr<boost::interprocess::file_lock>> dir_locks; +/** Mutex to protect dir_locks. */ +static std::mutex cs_dir_locks; + bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only) { + std::lock_guard<std::mutex> ulock(cs_dir_locks); fs::path pathLockFile = directory / lockfile_name; - FILE* file = fsbridge::fopen(pathLockFile, "a"); // empty lock file; created if it doesn't exist. + + // If a lock for this directory already exists in the map, don't try to re-lock it + if (dir_locks.count(pathLockFile.string())) { + return true; + } + + // Create empty lock file if it doesn't exist. + FILE* file = fsbridge::fopen(pathLockFile, "a"); if (file) fclose(file); try { - static std::map<std::string, boost::interprocess::file_lock> locks; - boost::interprocess::file_lock& lock = locks.emplace(pathLockFile.string(), pathLockFile.string().c_str()).first->second; - if (!lock.try_lock()) { + auto lock = MakeUnique<boost::interprocess::file_lock>(pathLockFile.string().c_str()); + if (!lock->try_lock()) { return false; } - if (probe_only) { - lock.unlock(); + if (!probe_only) { + // Lock successful and we're not just probing, put it into the map + dir_locks.emplace(pathLockFile.string(), std::move(lock)); } } catch (const boost::interprocess::interprocess_exception& e) { return error("Error while attempting to lock directory %s: %s", directory.string(), e.what()); @@ -394,6 +414,12 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b return true; } +void ReleaseDirectoryLocks() +{ + std::lock_guard<std::mutex> ulock(cs_dir_locks); + dir_locks.clear(); +} + /** Interpret string as boolean, for argument parsing */ static bool InterpretBool(const std::string& strValue) { @@ -904,11 +930,7 @@ bool SetupNetworking() int GetNumCores() { -#if BOOST_VERSION >= 105600 - return boost::thread::physical_concurrency(); -#else // Must fall back to hardware_concurrency, which unfortunately counts virtual cores - return boost::thread::hardware_concurrency(); -#endif + return std::thread::hardware_concurrency(); } std::string CopyrightHolders(const std::string& strPrefix) diff --git a/src/util.h b/src/util.h index 05138a9bfe..e4170d8aa2 100644 --- a/src/util.h +++ b/src/util.h @@ -174,6 +174,12 @@ int RaiseFileDescriptorLimit(int nMinFD); void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length); bool RenameOver(fs::path src, fs::path dest); bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only=false); + +/** Release all directory locks. This is used for unit testing only, at runtime + * the global destructor will take care of the locks. + */ +void ReleaseDirectoryLocks(); + bool TryCreateDirectories(const fs::path& p); fs::path GetDefaultDataDir(); const fs::path &GetDataDir(bool fNetSpecific = true); @@ -306,9 +312,8 @@ std::string HelpMessageGroup(const std::string& message); std::string HelpMessageOpt(const std::string& option, const std::string& message); /** - * Return the number of physical cores available on the current system. - * @note This does not count virtual cores, such as those provided by HyperThreading - * when boost is newer than 1.56. + * Return the number of cores available on the current system. + * @note This does count virtual cores, such as those provided by HyperThreading. */ int GetNumCores(); diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index 52158e9804..d1025fc7bf 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -127,46 +127,11 @@ std::string EncodeBase64(const unsigned char* pch, size_t len) { static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - std::string strRet = ""; - strRet.reserve((len+2)/3*4); - - int mode=0, left=0; - const unsigned char *pchEnd = pch+len; - - while (pch<pchEnd) - { - int enc = *(pch++); - switch (mode) - { - case 0: // we have no bits - strRet += pbase64[enc >> 2]; - left = (enc & 3) << 4; - mode = 1; - break; - - case 1: // we have two bits - strRet += pbase64[left | (enc >> 4)]; - left = (enc & 15) << 2; - mode = 2; - break; - - case 2: // we have four bits - strRet += pbase64[left | (enc >> 6)]; - strRet += pbase64[enc & 63]; - mode = 0; - break; - } - } - - if (mode) - { - strRet += pbase64[left]; - strRet += '='; - if (mode == 1) - strRet += '='; - } - - return strRet; + std::string str; + str.reserve(((len + 2) / 3) * 4); + ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, pch, pch + len); + while (str.size() % 4) str += '='; + return str; } std::string EncodeBase64(const std::string& str) @@ -193,68 +158,32 @@ std::vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid) -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; - if (pfInvalid) - *pfInvalid = false; - - std::vector<unsigned char> vchRet; - vchRet.reserve(strlen(p)*3/4); - - int mode = 0; - int left = 0; - - while (1) - { - int dec = decode64_table[(unsigned char)*p]; - if (dec == -1) break; - p++; - switch (mode) - { - case 0: // we have no bits and get 6 - left = dec; - mode = 1; - break; - - case 1: // we have 6 bits and keep 4 - vchRet.push_back((left<<2) | (dec>>4)); - left = dec & 15; - mode = 2; - break; - - case 2: // we have 4 bits and get 6, we keep 2 - vchRet.push_back((left<<4) | (dec>>2)); - left = dec & 3; - mode = 3; - break; - - case 3: // we have 2 bits and get 6 - vchRet.push_back((left<<6) | dec); - mode = 0; - break; - } + const char* e = p; + std::vector<uint8_t> val; + val.reserve(strlen(p)); + while (*p != 0) { + int x = decode64_table[(unsigned char)*p]; + if (x == -1) break; + val.push_back(x); + ++p; } - if (pfInvalid) - switch (mode) - { - case 0: // 4n base64 characters processed: ok - break; - - case 1: // 4n+1 base64 character processed: impossible - *pfInvalid = true; - break; - - case 2: // 4n+2 base64 characters processed: require '==' - if (left || p[0] != '=' || p[1] != '=' || decode64_table[(unsigned char)p[2]] != -1) - *pfInvalid = true; - break; - - case 3: // 4n+3 base64 characters processed: require '=' - if (left || p[0] != '=' || decode64_table[(unsigned char)p[1]] != -1) - *pfInvalid = true; - break; + std::vector<unsigned char> ret; + ret.reserve((val.size() * 3) / 4); + bool valid = ConvertBits<6, 8, false>([&](unsigned char c) { ret.push_back(c); }, val.begin(), val.end()); + + const char* q = p; + while (valid && *p != 0) { + if (*p != '=') { + valid = false; + break; } + ++p; + } + valid = valid && (p - e) % 4 == 0 && p - q < 4; + if (pfInvalid) *pfInvalid = !valid; - return vchRet; + return ret; } std::string DecodeBase64(const std::string& str) @@ -267,59 +196,11 @@ std::string EncodeBase32(const unsigned char* pch, size_t len) { static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567"; - std::string strRet=""; - strRet.reserve((len+4)/5*8); - - int mode=0, left=0; - const unsigned char *pchEnd = pch+len; - - while (pch<pchEnd) - { - int enc = *(pch++); - switch (mode) - { - case 0: // we have no bits - strRet += pbase32[enc >> 3]; - left = (enc & 7) << 2; - mode = 1; - break; - - case 1: // we have three bits - strRet += pbase32[left | (enc >> 6)]; - strRet += pbase32[(enc >> 1) & 31]; - left = (enc & 1) << 4; - mode = 2; - break; - - case 2: // we have one bit - strRet += pbase32[left | (enc >> 4)]; - left = (enc & 15) << 1; - mode = 3; - break; - - case 3: // we have four bits - strRet += pbase32[left | (enc >> 7)]; - strRet += pbase32[(enc >> 2) & 31]; - left = (enc & 3) << 3; - mode = 4; - break; - - case 4: // we have two bits - strRet += pbase32[left | (enc >> 5)]; - strRet += pbase32[enc & 31]; - mode = 0; - } - } - - static const int nPadding[5] = {0, 6, 4, 3, 1}; - if (mode) - { - strRet += pbase32[left]; - for (int n=0; n<nPadding[mode]; n++) - strRet += '='; - } - - return strRet; + std::string str; + str.reserve(((len + 4) / 5) * 8); + ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, pch, pch + len); + while (str.size() % 8) str += '='; + return str; } std::string EncodeBase32(const std::string& str) @@ -346,102 +227,32 @@ std::vector<unsigned char> DecodeBase32(const char* p, bool* pfInvalid) -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; - if (pfInvalid) - *pfInvalid = false; - - std::vector<unsigned char> vchRet; - vchRet.reserve((strlen(p))*5/8); - - int mode = 0; - int left = 0; - - while (1) - { - int dec = decode32_table[(unsigned char)*p]; - if (dec == -1) break; - p++; - switch (mode) - { - case 0: // we have no bits and get 5 - left = dec; - mode = 1; - break; - - case 1: // we have 5 bits and keep 2 - vchRet.push_back((left<<3) | (dec>>2)); - left = dec & 3; - mode = 2; - break; - - case 2: // we have 2 bits and keep 7 - left = left << 5 | dec; - mode = 3; - break; - - case 3: // we have 7 bits and keep 4 - vchRet.push_back((left<<1) | (dec>>4)); - left = dec & 15; - mode = 4; - break; - - case 4: // we have 4 bits, and keep 1 - vchRet.push_back((left<<4) | (dec>>1)); - left = dec & 1; - mode = 5; - break; - - case 5: // we have 1 bit, and keep 6 - left = left << 5 | dec; - mode = 6; - break; - - case 6: // we have 6 bits, and keep 3 - vchRet.push_back((left<<2) | (dec>>3)); - left = dec & 7; - mode = 7; - break; - - case 7: // we have 3 bits, and keep 0 - vchRet.push_back((left<<5) | dec); - mode = 0; - break; - } + const char* e = p; + std::vector<uint8_t> val; + val.reserve(strlen(p)); + while (*p != 0) { + int x = decode32_table[(unsigned char)*p]; + if (x == -1) break; + val.push_back(x); + ++p; } - if (pfInvalid) - switch (mode) - { - case 0: // 8n base32 characters processed: ok - break; - - case 1: // 8n+1 base32 characters processed: impossible - case 3: // +3 - case 6: // +6 - *pfInvalid = true; - break; - - case 2: // 8n+2 base32 characters processed: require '======' - if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || p[4] != '=' || p[5] != '=' || decode32_table[(unsigned char)p[6]] != -1) - *pfInvalid = true; - break; - - case 4: // 8n+4 base32 characters processed: require '====' - if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || decode32_table[(unsigned char)p[4]] != -1) - *pfInvalid = true; - break; - - case 5: // 8n+5 base32 characters processed: require '===' - if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || decode32_table[(unsigned char)p[3]] != -1) - *pfInvalid = true; - break; - - case 7: // 8n+7 base32 characters processed: require '=' - if (left || p[0] != '=' || decode32_table[(unsigned char)p[1]] != -1) - *pfInvalid = true; - break; + std::vector<unsigned char> ret; + ret.reserve((val.size() * 5) / 8); + bool valid = ConvertBits<5, 8, false>([&](unsigned char c) { ret.push_back(c); }, val.begin(), val.end()); + + const char* q = p; + while (valid && *p != 0) { + if (*p != '=') { + valid = false; + break; } + ++p; + } + valid = valid && (p - e) % 8 == 0 && p - q < 8; + if (pfInvalid) *pfInvalid = !valid; - return vchRet; + return ret; } std::string DecodeBase32(const std::string& str) diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h index 994e6abbad..1c9cca90b2 100644 --- a/src/utilstrencodings.h +++ b/src/utilstrencodings.h @@ -151,7 +151,7 @@ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out); /** Convert from one power-of-2 number base to another. */ template<int frombits, int tobits, bool pad, typename O, typename I> -bool ConvertBits(O& out, I it, I end) { +bool ConvertBits(const O& outfn, I it, I end) { size_t acc = 0; size_t bits = 0; constexpr size_t maxv = (1 << tobits) - 1; @@ -161,12 +161,12 @@ bool ConvertBits(O& out, I it, I end) { bits += frombits; while (bits >= tobits) { bits -= tobits; - out.push_back((acc >> bits) & maxv); + outfn((acc >> bits) & maxv); } ++it; } if (pad) { - if (bits) out.push_back((acc << (tobits - bits)) & maxv); + if (bits) outfn((acc << (tobits - bits)) & maxv); } else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) { return false; } diff --git a/src/utiltime.cpp b/src/utiltime.cpp index e908173135..8a861039b3 100644 --- a/src/utiltime.cpp +++ b/src/utiltime.cpp @@ -85,3 +85,15 @@ std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime) ss << boost::posix_time::from_time_t(nTime); return ss.str(); } + +std::string FormatISO8601DateTime(int64_t nTime) { + return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime); +} + +std::string FormatISO8601Date(int64_t nTime) { + return DateTimeStrFormat("%Y-%m-%d", nTime); +} + +std::string FormatISO8601Time(int64_t nTime) { + return DateTimeStrFormat("%H:%M:%SZ", nTime); +} diff --git a/src/utiltime.h b/src/utiltime.h index 56cc31da67..807c52ffaf 100644 --- a/src/utiltime.h +++ b/src/utiltime.h @@ -27,6 +27,14 @@ void SetMockTime(int64_t nMockTimeIn); int64_t GetMockTime(); void MilliSleep(int64_t n); +/** + * ISO 8601 formatting is preferred. Use the FormatISO8601{DateTime,Date,Time} + * helper functions if possible. + */ std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime); +std::string FormatISO8601DateTime(int64_t nTime); +std::string FormatISO8601Date(int64_t nTime); +std::string FormatISO8601Time(int64_t nTime); + #endif // BITCOIN_UTILTIME_H diff --git a/src/validation.cpp b/src/validation.cpp index 978aaf7d06..f49dc5a155 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -260,12 +260,12 @@ namespace { CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) { + AssertLockHeld(cs_main); + // Find the first block the caller has in the main chain for (const uint256& hash : locator.vHave) { - BlockMap::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) - { - CBlockIndex* pindex = (*mi).second; + CBlockIndex* pindex = LookupBlockIndex(hash); + if (pindex) { if (chain.Contains(pindex)) return pindex; if (pindex->GetAncestor(chain.Height()) == chain.Tip()) { @@ -712,7 +712,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool CAmount mempoolRejectFee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); if (!bypass_limits && mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) { - return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee)); + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nModifiedFees, mempoolRejectFee)); } // No transactions are allowed below minRelayTxFee except from disconnected blocks @@ -1182,7 +1182,8 @@ static void AlertNotify(const std::string& strMessage) safeStatus = singleQuote+safeStatus+singleQuote; boost::replace_all(strCmd, "%s", safeStatus); - boost::thread t(runCommand, strCmd); // thread runs free + std::thread t(runCommand, strCmd); + t.detach(); // thread runs free } static void CheckForkWarningConditions() @@ -1266,13 +1267,12 @@ void static InvalidChainFound(CBlockIndex* pindexNew) LogPrintf("%s: invalid block=%s height=%d log2_work=%.8g date=%s\n", __func__, pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, - log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", - pindexNew->GetBlockTime())); + log(pindexNew->nChainWork.getdouble())/log(2.0), FormatISO8601DateTime(pindexNew->GetBlockTime())); CBlockIndex *tip = chainActive.Tip(); assert (tip); LogPrintf("%s: current best=%s height=%d log2_work=%.8g date=%s\n", __func__, tip->GetBlockHash().ToString(), chainActive.Height(), log(tip->nChainWork.getdouble())/log(2.0), - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", tip->GetBlockTime())); + FormatISO8601DateTime(tip->GetBlockTime())); CheckForkWarningConditions(); } @@ -1316,7 +1316,7 @@ bool CScriptCheck::operator()() { int GetSpendHeight(const CCoinsViewCache& inputs) { LOCK(cs_main); - CBlockIndex* pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second; + CBlockIndex* pindexPrev = LookupBlockIndex(inputs.GetBestBlock()); return pindexPrev->nHeight + 1; } @@ -1772,9 +1772,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl { AssertLockHeld(cs_main); assert(pindex); - // pindex->phashBlock can be null if called by CreateNewBlock/TestBlockValidity - assert((pindex->phashBlock == nullptr) || - (*pindex->phashBlock == block.GetHash())); + assert(*pindex->phashBlock == block.GetHash()); int64_t nTimeStart = GetTimeMicros(); // Check it again in case a previous version let a bad block in @@ -1848,8 +1846,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl // Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the // two in the chain that violate it. This prevents exploiting the issue against nodes during their // initial block download. - bool fEnforceBIP30 = (!pindex->phashBlock) || // Enforce on CreateNewBlock invocations which don't have a hash. - !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256S("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) || + bool fEnforceBIP30 = !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256S("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) || (pindex->nHeight==91880 && pindex->GetBlockHash() == uint256S("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721"))); // Once BIP34 activated it was not possible to create new duplicate coinbases and thus other than starting @@ -1858,12 +1855,65 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl // before the first had been spent. Since those coinbases are sufficiently buried its no longer possible to create further // duplicate transactions descending from the known pairs either. // If we're on the known chain at height greater than where BIP34 activated, we can save the db accesses needed for the BIP30 check. + + // BIP34 requires that a block at height X (block X) has its coinbase + // scriptSig start with a CScriptNum of X (indicated height X). The above + // logic of no longer requiring BIP30 once BIP34 activates is flawed in the + // case that there is a block X before the BIP34 height of 227,931 which has + // an indicated height Y where Y is greater than X. The coinbase for block + // X would also be a valid coinbase for block Y, which could be a BIP30 + // violation. An exhaustive search of all mainnet coinbases before the + // BIP34 height which have an indicated height greater than the block height + // reveals many occurrences. The 3 lowest indicated heights found are + // 209,921, 490,897, and 1,983,702 and thus coinbases for blocks at these 3 + // heights would be the first opportunity for BIP30 to be violated. + + // The search reveals a great many blocks which have an indicated height + // greater than 1,983,702, so we simply remove the optimization to skip + // BIP30 checking for blocks at height 1,983,702 or higher. Before we reach + // that block in another 25 years or so, we should take advantage of a + // future consensus change to do a new and improved version of BIP34 that + // will actually prevent ever creating any duplicate coinbases in the + // future. + static constexpr int BIP34_IMPLIES_BIP30_LIMIT = 1983702; + + // There is no potential to create a duplicate coinbase at block 209,921 + // because this is still before the BIP34 height and so explicit BIP30 + // checking is still active. + + // The final case is block 176,684 which has an indicated height of + // 490,897. Unfortunately, this issue was not discovered until about 2 weeks + // before block 490,897 so there was not much opportunity to address this + // case other than to carefully analyze it and determine it would not be a + // problem. Block 490,897 was, in fact, mined with a different coinbase than + // block 176,684, but it is important to note that even if it hadn't been or + // is remined on an alternate fork with a duplicate coinbase, we would still + // not run into a BIP30 violation. This is because the coinbase for 176,684 + // is spent in block 185,956 in transaction + // d4f7fbbf92f4a3014a230b2dc70b8058d02eb36ac06b4a0736d9d60eaa9e8781. This + // spending transaction can't be duplicated because it also spends coinbase + // 0328dd85c331237f18e781d692c92de57649529bd5edf1d01036daea32ffde29. This + // coinbase has an indicated height of over 4.2 billion, and wouldn't be + // duplicatable until that height, and it's currently impossible to create a + // chain that long. Nevertheless we may wish to consider a future soft fork + // which retroactively prevents block 490,897 from creating a duplicate + // coinbase. The two historical BIP30 violations often provide a confusing + // edge case when manipulating the UTXO and it would be simpler not to have + // another edge case to deal with. + + // testnet3 has no blocks before the BIP34 height with indicated heights + // post BIP34 before approximately height 486,000,000 and presumably will + // be reset before it reaches block 1,983,702 and starts doing unnecessary + // BIP30 checking again. assert(pindex->pprev); CBlockIndex *pindexBIP34height = pindex->pprev->GetAncestor(chainparams.GetConsensus().BIP34Height); //Only continue to enforce if we're below BIP34 activation height or the block hash at that height doesn't correspond. fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || !(pindexBIP34height->GetBlockHash() == chainparams.GetConsensus().BIP34Hash)); - if (fEnforceBIP30) { + // TODO: Remove BIP30 checking from block height 1,983,702 on, once we have a + // consensus change that ensures coinbases at those heights can not + // duplicate earlier coinbases. + if (fEnforceBIP30 || pindex->nHeight >= BIP34_IMPLIES_BIP30_LIMIT) { for (const auto& tx : block.vtx) { for (size_t o = 0; o < tx->vout.size(); o++) { if (view.HaveCoin(COutPoint(tx->GetHash(), o))) { @@ -2086,7 +2136,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState & nLastWrite = nNow; } // Flush best chain related state. This can only be done if the blocks / block index write was also done. - if (fDoFullFlush) { + if (fDoFullFlush && !pcoinsTip->GetBestBlock().IsNull()) { // Typical Coin structures on disk are around 48 bytes in size. // Pushing a new one to the database can cause it to be written // twice (once in the log, and once in the tables). This is already @@ -2178,7 +2228,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)", __func__, pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion, log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx, - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexNew->GetBlockTime()), + FormatISO8601DateTime(pindexNew->GetBlockTime()), GuessVerificationProgress(chainParams.TxData(), pindexNew), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); if (!warningMessages.empty()) LogPrintf(" warning='%s'", boost::algorithm::join(warningMessages, ", ")); @@ -2776,6 +2826,8 @@ bool ResetBlockFailureFlags(CBlockIndex *pindex) { CBlockIndex* CChainState::AddToBlockIndex(const CBlockHeader& block) { + AssertLockHeld(cs_main); + // Check for duplicate uint256 hash = block.GetHash(); BlockMap::iterator it = mapBlockIndex.find(hash); @@ -3222,7 +3274,6 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState& BlockMap::iterator miSelf = mapBlockIndex.find(hash); CBlockIndex *pindex = nullptr; if (hash != chainparams.GetConsensus().hashGenesisBlock) { - if (miSelf != mapBlockIndex.end()) { // Block header is already known. pindex = miSelf->second; @@ -3433,9 +3484,11 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, AssertLockHeld(cs_main); assert(pindexPrev && pindexPrev == chainActive.Tip()); CCoinsViewCache viewNew(pcoinsTip.get()); + uint256 block_hash(block.GetHash()); CBlockIndex indexDummy(block); indexDummy.pprev = pindexPrev; indexDummy.nHeight = pindexPrev->nHeight + 1; + indexDummy.phashBlock = &block_hash; // NOTE: CheckBlockHeader is called by CheckBlock if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime())) @@ -3654,6 +3707,8 @@ fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix) CBlockIndex * CChainState::InsertBlockIndex(const uint256& hash) { + AssertLockHeld(cs_main); + if (hash.IsNull()) return nullptr; @@ -3781,6 +3836,8 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) bool LoadChainTip(const CChainParams& chainparams) { + AssertLockHeld(cs_main); + if (chainActive.Tip() && chainActive.Tip()->GetBlockHash() == pcoinsTip->GetBestBlock()) return true; if (pcoinsTip->GetBestBlock().IsNull() && mapBlockIndex.size() == 1) { @@ -3794,16 +3851,17 @@ bool LoadChainTip(const CChainParams& chainparams) } // Load pointer to end of best chain - BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); - if (it == mapBlockIndex.end()) + CBlockIndex* pindex = LookupBlockIndex(pcoinsTip->GetBestBlock()); + if (!pindex) { return false; - chainActive.SetTip(it->second); + } + chainActive.SetTip(pindex); g_chainstate.PruneBlockIndexCandidates(); LogPrintf("Loaded best chain: hashBestChain=%s height=%d date=%s progress=%f\n", chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), + FormatISO8601DateTime(chainActive.Tip()->GetBlockTime()), GuessVerificationProgress(chainparams.TxData(), chainActive.Tip())); return true; } @@ -4244,26 +4302,31 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB blkdat >> block; nRewind = blkdat.GetPos(); - // detect out of order blocks, and store them for later uint256 hash = block.GetHash(); - if (hash != chainparams.GetConsensus().hashGenesisBlock && mapBlockIndex.find(block.hashPrevBlock) == mapBlockIndex.end()) { - LogPrint(BCLog::REINDEX, "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(), - block.hashPrevBlock.ToString()); - if (dbp) - mapBlocksUnknownParent.insert(std::make_pair(block.hashPrevBlock, *dbp)); - continue; - } - - // process in case the block isn't known yet - if (mapBlockIndex.count(hash) == 0 || (mapBlockIndex[hash]->nStatus & BLOCK_HAVE_DATA) == 0) { + { LOCK(cs_main); - CValidationState state; - if (g_chainstate.AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr)) - nLoaded++; - if (state.IsError()) - break; - } else if (hash != chainparams.GetConsensus().hashGenesisBlock && mapBlockIndex[hash]->nHeight % 1000 == 0) { - LogPrint(BCLog::REINDEX, "Block Import: already had block %s at height %d\n", hash.ToString(), mapBlockIndex[hash]->nHeight); + // detect out of order blocks, and store them for later + if (hash != chainparams.GetConsensus().hashGenesisBlock && !LookupBlockIndex(block.hashPrevBlock)) { + LogPrint(BCLog::REINDEX, "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(), + block.hashPrevBlock.ToString()); + if (dbp) + mapBlocksUnknownParent.insert(std::make_pair(block.hashPrevBlock, *dbp)); + continue; + } + + // process in case the block isn't known yet + CBlockIndex* pindex = LookupBlockIndex(hash); + if (!pindex || (pindex->nStatus & BLOCK_HAVE_DATA) == 0) { + CValidationState state; + if (g_chainstate.AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr)) { + nLoaded++; + } + if (state.IsError()) { + break; + } + } else if (hash != chainparams.GetConsensus().hashGenesisBlock && pindex->nHeight % 1000 == 0) { + LogPrint(BCLog::REINDEX, "Block Import: already had block %s at height %d\n", hash.ToString(), pindex->nHeight); + } } // Activate the genesis block so normal node progress can continue @@ -4501,7 +4564,7 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams) std::string CBlockFileInfo::ToString() const { - return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast)); + return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, FormatISO8601Date(nTimeFirst), FormatISO8601Date(nTimeLast)); } CBlockFileInfo* GetBlockFileInfo(size_t n) @@ -4657,6 +4720,7 @@ bool DumpMempool(void) } //! Guess how far we are in the verification process at the given block index +//! require cs_main if pindex has not been validated yet (because nChainTx might be unset) double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pindex) { if (pindex == nullptr) return 0.0; diff --git a/src/validation.h b/src/validation.h index 99cbfdf1ee..e780f453b2 100644 --- a/src/validation.h +++ b/src/validation.h @@ -428,6 +428,13 @@ public: /** Replay blocks that aren't fully applied to the database. */ bool ReplayBlocks(const CChainParams& params, CCoinsView* view); +inline CBlockIndex* LookupBlockIndex(const uint256& hash) +{ + AssertLockHeld(cs_main); + BlockMap::const_iterator it = mapBlockIndex.find(hash); + return it == mapBlockIndex.end() ? nullptr : it->second; +} + /** Find the last common block between the parameter chain and a locator. */ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator); diff --git a/src/validationinterface.h b/src/validationinterface.h index 56ea698a2e..63097166af 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -56,6 +56,11 @@ void SyncWithValidationInterfaceQueue(); class CValidationInterface { protected: /** + * Protected destructor so that instances can only be deleted by derived classes. + * If that restriction is no longer desired, this should be made public and virtual. + */ + ~CValidationInterface() = default; + /** * Notifies listeners of updated block chain tip * * Called on a background thread. diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 23c6279128..ebe7b48da0 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -52,20 +52,55 @@ void CheckUniqueFileid(const CDBEnv& env, const std::string& filename, Db& db) } } } + +CCriticalSection cs_db; +std::map<std::string, CDBEnv> g_dbenvs; //!< Map from directory name to open db environment. } // namespace +CDBEnv* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename) +{ + fs::path env_directory; + if (fs::is_regular_file(wallet_path)) { + // Special case for backwards compatibility: if wallet path points to an + // existing file, treat it as the path to a BDB data file in a parent + // directory that also contains BDB log files. + env_directory = wallet_path.parent_path(); + database_filename = wallet_path.filename().string(); + } else { + // Normal case: Interpret wallet path as a directory path containing + // data and log files. + env_directory = wallet_path; + database_filename = "wallet.dat"; + } + LOCK(cs_db); + // Note: An ununsed temporary CDBEnv object may be created inside the + // emplace function if the key already exists. This is a little inefficient, + // but not a big concern since the map will be changed in the future to hold + // pointers instead of objects, anyway. + return &g_dbenvs.emplace(std::piecewise_construct, std::forward_as_tuple(env_directory.string()), std::forward_as_tuple(env_directory)).first->second; +} + // // CDB // -CDBEnv bitdb; - -void CDBEnv::EnvShutdown() +void CDBEnv::Close() { if (!fDbEnvInit) return; fDbEnvInit = false; + + for (auto& db : mapDb) { + auto count = mapFileUseCount.find(db.first); + assert(count == mapFileUseCount.end() || count->second == 0); + if (db.second) { + db.second->close(0); + delete db.second; + db.second = nullptr; + } + } + int ret = dbenv->close(0); if (ret != 0) LogPrintf("CDBEnv::EnvShutdown: Error %d shutting down database environment: %s\n", ret, DbEnv::strerror(ret)); @@ -80,29 +115,25 @@ void CDBEnv::Reset() fMockDb = false; } -CDBEnv::CDBEnv() +CDBEnv::CDBEnv(const fs::path& dir_path) : strPath(dir_path.string()) { Reset(); } CDBEnv::~CDBEnv() { - EnvShutdown(); + Close(); } -void CDBEnv::Close() -{ - EnvShutdown(); -} - -bool CDBEnv::Open(const fs::path& pathIn, bool retry) +bool CDBEnv::Open(bool retry) { if (fDbEnvInit) return true; boost::this_thread::interruption_point(); - strPath = pathIn.string(); + fs::path pathIn = strPath; + TryCreateDirectories(pathIn); if (!LockDirectory(pathIn, ".walletlock")) { LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance of bitcoin may be using it.\n", strPath); return false; @@ -150,7 +181,7 @@ bool CDBEnv::Open(const fs::path& pathIn, bool retry) // failure is ok (well, not really, but it's not worse than what we started with) } // try opening it again one more time - if (!Open(pathIn, false)) { + if (!Open(false /* retry */)) { // if it still fails, it probably means we can't even create the database env return false; } @@ -209,12 +240,15 @@ CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, recoverFunc_type return RECOVER_FAIL; // Try to recover: - bool fRecovered = (*recoverFunc)(strFile, out_backup_filename); + bool fRecovered = (*recoverFunc)(fs::path(strPath) / strFile, out_backup_filename); return (fRecovered ? RECOVER_OK : RECOVER_FAIL); } -bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename) +bool CDB::Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename) { + std::string filename; + CDBEnv* env = GetWalletEnv(file_path, filename); + // Recovery procedure: // move wallet file to walletfilename.timestamp.bak // Call Salvage with fAggressive=true to @@ -225,7 +259,7 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco int64_t now = GetTime(); newFilename = strprintf("%s.%d.bak", filename, now); - int result = bitdb.dbenv->dbrename(nullptr, filename.c_str(), nullptr, + int result = env->dbenv->dbrename(nullptr, filename.c_str(), nullptr, newFilename.c_str(), DB_AUTO_COMMIT); if (result == 0) LogPrintf("Renamed %s to %s\n", filename, newFilename); @@ -236,7 +270,7 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco } std::vector<CDBEnv::KeyValPair> salvagedData; - bool fSuccess = bitdb.Salvage(newFilename, true, salvagedData); + bool fSuccess = env->Salvage(newFilename, true, salvagedData); if (salvagedData.empty()) { LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename); @@ -244,7 +278,7 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco } LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size()); - std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(bitdb.dbenv.get(), 0); + std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0); int ret = pdbCopy->open(nullptr, // Txn pointer filename.c_str(), // Filename "main", // Logical db name @@ -257,7 +291,7 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco return false; } - DbTxn* ptxn = bitdb.TxnBegin(); + DbTxn* ptxn = env->TxnBegin(); for (CDBEnv::KeyValPair& row : salvagedData) { if (recoverKVcallback) @@ -279,8 +313,12 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco return fSuccess; } -bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& walletDir, std::string& errorStr) +bool CDB::VerifyEnvironment(const fs::path& file_path, std::string& errorStr) { + std::string walletFile; + CDBEnv* env = GetWalletEnv(file_path, walletFile); + fs::path walletDir = env->Directory(); + LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0)); LogPrintf("Using wallet %s\n", walletFile); @@ -291,7 +329,7 @@ bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& walle return false; } - if (!bitdb.Open(walletDir, true)) { + if (!env->Open(true /* retry */)) { errorStr = strprintf(_("Error initializing wallet database environment %s!"), walletDir); return false; } @@ -299,12 +337,16 @@ bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& walle return true; } -bool CDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& walletDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc) +bool CDB::VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc) { + std::string walletFile; + CDBEnv* env = GetWalletEnv(file_path, walletFile); + fs::path walletDir = env->Directory(); + if (fs::exists(walletDir / walletFile)) { std::string backup_filename; - CDBEnv::VerifyResult r = bitdb.Verify(walletFile, recoverFunc, backup_filename); + CDBEnv::VerifyResult r = env->Verify(walletFile, recoverFunc, backup_filename); if (r == CDBEnv::RECOVER_OK) { warningStr = strprintf(_("Warning: Wallet file corrupt, data salvaged!" @@ -414,8 +456,8 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb nFlags |= DB_CREATE; { - LOCK(env->cs_db); - if (!env->Open(GetWalletDir())) + LOCK(cs_db); + if (!env->Open(false /* retry */)) throw std::runtime_error("CDB: Failed to open database environment."); pdb = env->mapDb[strFilename]; @@ -442,7 +484,25 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb if (ret != 0) { throw std::runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFilename)); } - CheckUniqueFileid(*env, strFilename, *pdb_temp); + + // Call CheckUniqueFileid on the containing BDB environment to + // avoid BDB data consistency bugs that happen when different data + // files in the same environment have the same fileid. + // + // Also call CheckUniqueFileid on all the other g_dbenvs to prevent + // bitcoin from opening the same data file through another + // environment when the file is referenced through equivalent but + // not obviously identical symlinked or hard linked or bind mounted + // paths. In the future a more relaxed check for equal inode and + // device ids could be done instead, which would allow opening + // different backup copies of a wallet at the same time. Maybe even + // more ideally, an exclusive lock for accessing the database could + // be implemented, so no equality checks are needed at all. (Newer + // versions of BDB have an set_lk_exclusive method for this + // purpose, but the older version we use does not.) + for (auto& env : g_dbenvs) { + CheckUniqueFileid(env.second, strFilename, *pdb_temp); + } pdb = pdb_temp.release(); env->mapDb[strFilename] = pdb; @@ -490,7 +550,7 @@ void CDB::Close() Flush(); { - LOCK(env->cs_db); + LOCK(cs_db); --env->mapFileUseCount[strFile]; } } @@ -518,7 +578,7 @@ bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip) const std::string& strFile = dbw.strFile; while (true) { { - LOCK(env->cs_db); + LOCK(cs_db); if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0) { // Flush log data to the dat file env->CloseDb(strFile); @@ -646,7 +706,7 @@ bool CDB::PeriodicFlush(CWalletDBWrapper& dbw) bool ret = false; CDBEnv *env = dbw.env; const std::string& strFile = dbw.strFile; - TRY_LOCK(bitdb.cs_db,lockDb); + TRY_LOCK(cs_db, lockDb); if (lockDb) { // Don't do this if any databases are in use @@ -694,7 +754,7 @@ bool CWalletDBWrapper::Backup(const std::string& strDest) while (true) { { - LOCK(env->cs_db); + LOCK(cs_db); if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0) { // Flush log data to the dat file diff --git a/src/wallet/db.h b/src/wallet/db.h index 787135e400..b1ce451534 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -11,6 +11,7 @@ #include <serialize.h> #include <streams.h> #include <sync.h> +#include <util.h> #include <version.h> #include <atomic> @@ -32,20 +33,19 @@ private: // shutdown problems/crashes caused by a static initialized internal pointer. std::string strPath; - void EnvShutdown(); - public: - mutable CCriticalSection cs_db; std::unique_ptr<DbEnv> dbenv; std::map<std::string, int> mapFileUseCount; std::map<std::string, Db*> mapDb; - CDBEnv(); + CDBEnv(const fs::path& env_directory); ~CDBEnv(); void Reset(); void MakeMock(); bool IsMock() const { return fMockDb; } + bool IsInitialized() const { return fDbEnvInit; } + fs::path Directory() const { return strPath; } /** * Verify that database file strFile is OK. If it is not, @@ -56,7 +56,7 @@ public: enum VerifyResult { VERIFY_OK, RECOVER_OK, RECOVER_FAIL }; - typedef bool (*recoverFunc_type)(const std::string& strFile, std::string& out_backup_filename); + typedef bool (*recoverFunc_type)(const fs::path& file_path, std::string& out_backup_filename); VerifyResult Verify(const std::string& strFile, recoverFunc_type recoverFunc, std::string& out_backup_filename); /** * Salvage data from a file that Verify says is bad. @@ -68,7 +68,7 @@ public: typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair; bool Salvage(const std::string& strFile, bool fAggressive, std::vector<KeyValPair>& vResult); - bool Open(const fs::path& path, bool retry = 0); + bool Open(bool retry); void Close(); void Flush(bool fShutdown); void CheckpointLSN(const std::string& strFile); @@ -85,7 +85,8 @@ public: } }; -extern CDBEnv bitdb; +/** Get CDBEnv and database filename given a wallet path. */ +CDBEnv* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename); /** An instance of this class represents one database. * For BerkeleyDB this is just a (env, strFile) tuple. @@ -100,9 +101,33 @@ public: } /** Create DB handle to real database */ - CWalletDBWrapper(CDBEnv *env_in, const std::string &strFile_in) : - nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(env_in), strFile(strFile_in) + CWalletDBWrapper(const fs::path& wallet_path, bool mock = false) : + nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0) { + env = GetWalletEnv(wallet_path, strFile); + if (mock) { + env->Close(); + env->Reset(); + env->MakeMock(); + } + } + + /** Return object for accessing database at specified path. */ + static std::unique_ptr<CWalletDBWrapper> Create(const fs::path& path) + { + return MakeUnique<CWalletDBWrapper>(path); + } + + /** Return object for accessing dummy database with no read/write capabilities. */ + static std::unique_ptr<CWalletDBWrapper> CreateDummy() + { + return MakeUnique<CWalletDBWrapper>(); + } + + /** Return object for accessing temporary in-memory database. */ + static std::unique_ptr<CWalletDBWrapper> CreateMock() + { + return MakeUnique<CWalletDBWrapper>("", true /* mock */); } /** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero @@ -113,10 +138,6 @@ public: */ bool Backup(const std::string& strDest); - /** Get a name for this database, for debugging etc. - */ - std::string GetName() const { return strFile; } - /** Make sure all changes are flushed to disk. */ void Flush(bool shutdown); @@ -161,15 +182,15 @@ public: void Flush(); void Close(); - static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename); + static bool Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename); /* flush the wallet passively (TRY_LOCK) ideal to be called periodically */ static bool PeriodicFlush(CWalletDBWrapper& dbw); /* verifies the database environment */ - static bool VerifyEnvironment(const std::string& walletFile, const fs::path& walletDir, std::string& errorStr); + static bool VerifyEnvironment(const fs::path& file_path, std::string& errorStr); /* verifies the database file */ - static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& walletDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc); + static bool VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc); public: template <typename K, typename T> @@ -329,7 +350,7 @@ public: { if (!pdb || activeTxn) return false; - DbTxn* ptxn = bitdb.TxnBegin(); + DbTxn* ptxn = env->TxnBegin(); if (!ptxn) return false; activeTxn = ptxn; diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 9bfcab54a5..0c6d782e38 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -65,16 +65,39 @@ static feebumper::Result PreconditionChecks(const CWallet* wallet, const CWallet errors.push_back("Transaction has been mined, or is conflicted with a mined transaction"); return feebumper::Result::WALLET_ERROR; } + + if (!SignalsOptInRBF(*wtx.tx)) { + errors.push_back("Transaction is not BIP 125 replaceable"); + return feebumper::Result::WALLET_ERROR; + } + + if (wtx.mapValue.count("replaced_by_txid")) { + errors.push_back(strprintf("Cannot bump transaction %s which was already bumped by transaction %s", wtx.GetHash().ToString(), wtx.mapValue.at("replaced_by_txid"))); + return feebumper::Result::WALLET_ERROR; + } + + // check that original tx consists entirely of our inputs + // if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee) + if (!wallet->IsAllFromMe(*wtx.tx, ISMINE_SPENDABLE)) { + errors.push_back("Transaction contains inputs that don't belong to this wallet"); + return feebumper::Result::WALLET_ERROR; + } + + return feebumper::Result::OK; } namespace feebumper { -bool TransactionCanBeBumped(CWallet* wallet, const uint256& txid) +bool TransactionCanBeBumped(const CWallet* wallet, const uint256& txid) { LOCK2(cs_main, wallet->cs_wallet); const CWalletTx* wtx = wallet->GetWalletTx(txid); - return wtx && SignalsOptInRBF(*wtx->tx) && !wtx->mapValue.count("replaced_by_txid"); + if (wtx == nullptr) return false; + + std::vector<std::string> errors_dummy; + feebumper::Result res = PreconditionChecks(wallet, *wtx, errors_dummy); + return res == feebumper::Result::OK; } Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoinControl& coin_control, CAmount total_fee, std::vector<std::string>& errors, @@ -94,23 +117,6 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin return result; } - if (!SignalsOptInRBF(*wtx.tx)) { - errors.push_back("Transaction is not BIP 125 replaceable"); - return Result::WALLET_ERROR; - } - - if (wtx.mapValue.count("replaced_by_txid")) { - errors.push_back(strprintf("Cannot bump transaction %s which was already bumped by transaction %s", txid.ToString(), wtx.mapValue.at("replaced_by_txid"))); - return Result::WALLET_ERROR; - } - - // check that original tx consists entirely of our inputs - // if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee) - if (!wallet->IsAllFromMe(*wtx.tx, ISMINE_SPENDABLE)) { - errors.push_back("Transaction contains inputs that don't belong to this wallet"); - return Result::WALLET_ERROR; - } - // figure out which output was change // if there was no change output or multiple change outputs, fail int nOutput = -1; @@ -228,6 +234,7 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin } } + return Result::OK; } @@ -255,23 +262,20 @@ Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransacti return result; } - CWalletTx wtxBumped(wallet, MakeTransactionRef(std::move(mtx))); // commit/broadcast the tx + CTransactionRef tx = MakeTransactionRef(std::move(mtx)); + mapValue_t mapValue = oldWtx.mapValue; + mapValue["replaces_txid"] = oldWtx.GetHash().ToString(); + CReserveKey reservekey(wallet); - wtxBumped.mapValue = oldWtx.mapValue; - wtxBumped.mapValue["replaces_txid"] = oldWtx.GetHash().ToString(); - wtxBumped.vOrderForm = oldWtx.vOrderForm; - wtxBumped.strFromAccount = oldWtx.strFromAccount; - wtxBumped.fTimeReceivedIsTxTime = true; - wtxBumped.fFromMe = true; CValidationState state; - if (!wallet->CommitTransaction(wtxBumped, reservekey, g_connman.get(), state)) { + if (!wallet->CommitTransaction(tx, std::move(mapValue), oldWtx.vOrderForm, oldWtx.strFromAccount, reservekey, g_connman.get(), state)) { // NOTE: CommitTransaction never returns false, so this should never happen. - errors.push_back(strprintf("The transaction was rejected: %s", state.GetRejectReason())); + errors.push_back(strprintf("The transaction was rejected: %s", FormatStateMessage(state))); return Result::WALLET_ERROR; } - bumped_txid = wtxBumped.GetHash(); + bumped_txid = tx->GetHash(); if (state.IsInvalid()) { // This can happen if the mempool rejected the transaction. Report // what happened in the "errors" response. @@ -279,7 +283,7 @@ Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransacti } // mark the original tx as bumped - if (!wallet->MarkReplaced(oldWtx.GetHash(), wtxBumped.GetHash())) { + if (!wallet->MarkReplaced(oldWtx.GetHash(), bumped_txid)) { // TODO: see if JSON-RPC has a standard way of returning a response // along with an exception. It would be good to return information about // wtxBumped to the caller even if marking the original transaction @@ -290,4 +294,3 @@ Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransacti } } // namespace feebumper - diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h index 8eec30440c..7e36a9766b 100644 --- a/src/wallet/feebumper.h +++ b/src/wallet/feebumper.h @@ -26,7 +26,7 @@ enum class Result }; //! Return whether transaction can be bumped. -bool TransactionCanBeBumped(CWallet* wallet, const uint256& txid); +bool TransactionCanBeBumped(const CWallet* wallet, const uint256& txid); //! Create bumpfee transaction. Result CreateTransaction(const CWallet* wallet, diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp index 73985dcf25..385fdc963a 100644 --- a/src/wallet/fees.cpp +++ b/src/wallet/fees.cpp @@ -53,6 +53,9 @@ CAmount GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, c // if we don't have enough data for estimateSmartFee, then use fallbackFee fee_needed = CWallet::fallbackFee.GetFee(nTxBytes); if (feeCalc) feeCalc->reason = FeeReason::FALLBACK; + + // directly return if fallback fee is disabled (feerate 0 == disabled) + if (CWallet::fallbackFee.GetFee(1000) == 0) return fee_needed; } // Obey mempool min fee when using smart fee estimation CAmount min_mempool_fee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nTxBytes); diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 74036f4f0f..e028cf4210 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -5,6 +5,7 @@ #include <wallet/init.h> +#include <chainparams.h> #include <net.h> #include <util.h> #include <utilmoneystr.h> @@ -34,7 +35,7 @@ std::string GetWalletHelpString(bool showDebug) strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); strUsage += HelpMessageOpt("-txconfirmtarget=<n>", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET)); strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup")); - strUsage += HelpMessageOpt("-wallet=<file>", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); + strUsage += HelpMessageOpt("-wallet=<path>", _("Specify wallet database path. Can be specified multiple times to load multiple wallets. Path is interpreted relative to <walletdir> if it is not absolute, and will be created if it does not exist (as a directory containing a wallet.dat file and log files). For backwards compatibility this will also accept names of existing data files in <walletdir>.)")); strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST)); strUsage += HelpMessageOpt("-walletdir=<dir>", _("Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)")); strUsage += HelpMessageOpt("-walletnotify=<cmd>", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)")); @@ -65,7 +66,7 @@ bool WalletParameterInteraction() return true; } - gArgs.SoftSetArg("-wallet", DEFAULT_WALLET_DAT); + gArgs.SoftSetArg("-wallet", ""); const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1; if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) { @@ -123,6 +124,8 @@ bool WalletParameterInteraction() _("This is the minimum transaction fee you pay on every transaction.")); CWallet::minTxFee = CFeeRate(n); } + + g_wallet_allow_fallback_fee = Params().IsFallbackFeeEnabled(); if (gArgs.IsArgSet("-fallbackfee")) { CAmount nFeePerK = 0; @@ -132,6 +135,7 @@ bool WalletParameterInteraction() InitWarning(AmountHighWarn("-fallbackfee") + " " + _("This is the transaction fee you may pay when fee estimates are not available.")); CWallet::fallbackFee = CFeeRate(nFeePerK); + g_wallet_allow_fallback_fee = nFeePerK != 0; //disable fallback fee in case value was set to 0, enable if non-null value } if (gArgs.IsArgSet("-discardfee")) { @@ -226,18 +230,22 @@ bool VerifyWallets() std::set<fs::path> wallet_paths; for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { - if (boost::filesystem::path(walletFile).filename() != walletFile) { - return InitError(strprintf(_("Error loading wallet %s. -wallet parameter must only specify a filename (not a path)."), walletFile)); - } - - if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) { - return InitError(strprintf(_("Error loading wallet %s. Invalid characters in -wallet filename."), walletFile)); - } - + // Do some checking on wallet path. It should be either a: + // + // 1. Path where a directory can be created. + // 2. Path to an existing directory. + // 3. Path to a symlink to a directory. + // 4. For backwards compatibility, the name of a data file in -walletdir. fs::path wallet_path = fs::absolute(walletFile, GetWalletDir()); - - if (fs::exists(wallet_path) && (!fs::is_regular_file(wallet_path) || fs::is_symlink(wallet_path))) { - return InitError(strprintf(_("Error loading wallet %s. -wallet filename must be a regular file."), walletFile)); + fs::file_type path_type = fs::symlink_status(wallet_path).type(); + if (!(path_type == fs::file_not_found || path_type == fs::directory_file || + (path_type == fs::symlink_file && fs::is_directory(wallet_path)) || + (path_type == fs::regular_file && fs::path(walletFile).filename() == walletFile))) { + return InitError(strprintf( + _("Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and " + "database/log.?????????? files can be stored, a location where such a directory could be created, " + "or (for backwards compatibility) the name of an existing data file in -walletdir (%s)"), + walletFile, GetWalletDir())); } if (!wallet_paths.insert(wallet_path).second) { @@ -245,21 +253,21 @@ bool VerifyWallets() } std::string strError; - if (!CWalletDB::VerifyEnvironment(walletFile, GetWalletDir().string(), strError)) { + if (!CWalletDB::VerifyEnvironment(wallet_path, strError)) { return InitError(strError); } if (gArgs.GetBoolArg("-salvagewallet", false)) { // Recover readable keypairs: - CWallet dummyWallet; + CWallet dummyWallet("dummy", CWalletDBWrapper::CreateDummy()); std::string backup_filename; - if (!CWalletDB::Recover(walletFile, (void *)&dummyWallet, CWalletDB::RecoverKeysOnlyFilter, backup_filename)) { + if (!CWalletDB::Recover(wallet_path, (void *)&dummyWallet, CWalletDB::RecoverKeysOnlyFilter, backup_filename)) { return false; } } std::string strWarning; - bool dbV = CWalletDB::VerifyDatabaseFile(walletFile, GetWalletDir().string(), strWarning, strError); + bool dbV = CWalletDB::VerifyDatabaseFile(wallet_path, strWarning, strError); if (!strWarning.empty()) { InitWarning(strWarning); } @@ -280,7 +288,7 @@ bool OpenWallets() } for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { - CWallet * const pwallet = CWallet::CreateWalletFromFile(walletFile); + CWallet * const pwallet = CWallet::CreateWalletFromFile(walletFile, fs::absolute(walletFile, GetWalletDir())); if (!pwallet) { return false; } diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 741ea25340..01125dd618 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <base58.h> #include <chain.h> +#include <key_io.h> #include <rpc/safemode.h> #include <rpc/server.h> #include <wallet/init.h> @@ -28,10 +28,6 @@ #include <univalue.h> -std::string static EncodeDumpTime(int64_t nTime) { - return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime); -} - int64_t static DecodeDumpTime(const std::string &str) { static const boost::posix_time::ptime epoch = boost::posix_time::from_time_t(0); static const std::locale loc(std::locale::classic(), @@ -147,13 +143,8 @@ UniValue importprivkey(const JSONRPCRequest& request) throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); } - CBitcoinSecret vchSecret; - bool fGood = vchSecret.SetString(strSecret); - - if (!fGood) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding"); - - CKey key = vchSecret.GetKey(); - if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range"); + CKey key = DecodeSecret(strSecret); + if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding"); CPubKey pubkey = key.GetPubKey(); assert(key.VerifyPubKey(pubkey)); @@ -279,7 +270,7 @@ UniValue importaddress(const JSONRPCRequest& request) ); - std::string strLabel = ""; + std::string strLabel; if (!request.params[1].isNull()) strLabel = request.params[1].get_str(); @@ -359,9 +350,10 @@ UniValue importprunedfunds(const JSONRPCRequest& request) if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) == merkleBlock.header.hashMerkleRoot) { LOCK(cs_main); - - if (!mapBlockIndex.count(merkleBlock.header.GetHash()) || !chainActive.Contains(mapBlockIndex[merkleBlock.header.GetHash()])) + const CBlockIndex* pindex = LookupBlockIndex(merkleBlock.header.GetHash()); + if (!pindex || !chainActive.Contains(pindex)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain"); + } std::vector<uint256>::const_iterator it; if ((it = std::find(vMatch.begin(), vMatch.end(), hashTx))==vMatch.end()) { @@ -452,7 +444,7 @@ UniValue importpubkey(const JSONRPCRequest& request) ); - std::string strLabel = ""; + std::string strLabel; if (!request.params[1].isNull()) strLabel = request.params[1].get_str(); @@ -554,9 +546,8 @@ UniValue importwallet(const JSONRPCRequest& request) boost::split(vstr, line, boost::is_any_of(" ")); if (vstr.size() < 2) continue; - CBitcoinSecret vchSecret; - if (vchSecret.SetString(vstr[0])) { - CKey key = vchSecret.GetKey(); + CKey key = DecodeSecret(vstr[0]); + if (key.IsValid()) { CPubKey pubkey = key.GetPubKey(); assert(key.VerifyPubKey(pubkey)); CKeyID keyid = pubkey.GetID(); @@ -659,7 +650,7 @@ UniValue dumpprivkey(const JSONRPCRequest& request) if (!pwallet->GetKey(keyid, vchSecret)) { throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known"); } - return CBitcoinSecret(vchSecret).ToString(); + return EncodeSecret(vchSecret); } @@ -728,9 +719,9 @@ UniValue dumpwallet(const JSONRPCRequest& request) // produce output file << strprintf("# Wallet dump created by Bitcoin %s\n", CLIENT_BUILD); - file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime())); + file << strprintf("# * Created on %s\n", FormatISO8601DateTime(GetTime())); file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString()); - file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->GetBlockTime())); + file << strprintf("# mined on %s\n", FormatISO8601DateTime(chainActive.Tip()->GetBlockTime())); file << "\n"; // add the base58check encoded extended master if the wallet uses HD @@ -742,20 +733,17 @@ UniValue dumpwallet(const JSONRPCRequest& request) CExtKey masterKey; masterKey.SetMaster(key.begin(), key.size()); - CBitcoinExtKey b58extkey; - b58extkey.SetKey(masterKey); - - file << "# extended private masterkey: " << b58extkey.ToString() << "\n\n"; + file << "# extended private masterkey: " << EncodeExtKey(masterKey) << "\n\n"; } } for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) { const CKeyID &keyid = it->second; - std::string strTime = EncodeDumpTime(it->first); + std::string strTime = FormatISO8601DateTime(it->first); std::string strAddr; std::string strLabel; CKey key; if (pwallet->GetKey(keyid, key)) { - file << strprintf("%s %s ", CBitcoinSecret(key).ToString(), strTime); + file << strprintf("%s %s ", EncodeSecret(key), strTime); if (GetWalletAddressesForKey(pwallet, keyid, strAddr, strLabel)) { file << strprintf("label=%s", strLabel); } else if (keyid == masterKeyID) { @@ -778,7 +766,7 @@ UniValue dumpwallet(const JSONRPCRequest& request) // get birth times for scripts with metadata auto it = pwallet->m_script_metadata.find(scriptid); if (it != pwallet->m_script_metadata.end()) { - create_time = EncodeDumpTime(it->second.nCreateTime); + create_time = FormatISO8601DateTime(it->second.nCreateTime); } if(pwallet->GetCScript(scriptid, script)) { file << strprintf("%s %s script=1", HexStr(script.begin(), script.end()), create_time); @@ -911,17 +899,10 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6 for (size_t i = 0; i < keys.size(); i++) { const std::string& privkey = keys[i].get_str(); - CBitcoinSecret vchSecret; - bool fGood = vchSecret.SetString(privkey); - - if (!fGood) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding"); - } - - CKey key = vchSecret.GetKey(); + CKey key = DecodeSecret(privkey); if (!key.IsValid()) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding"); } CPubKey pubkey = key.GetPubKey(); @@ -1018,16 +999,10 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6 const std::string& strPrivkey = keys[0].get_str(); // Checks. - CBitcoinSecret vchSecret; - bool fGood = vchSecret.SetString(strPrivkey); + CKey key = DecodeSecret(strPrivkey); - if (!fGood) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding"); - } - - CKey key = vchSecret.GetKey(); if (!key.IsValid()) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding"); } CPubKey pubKey = key.GetPubKey(); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index b466cf1a81..7ad9efff70 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -4,18 +4,19 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <amount.h> -#include <base58.h> #include <chain.h> #include <consensus/validation.h> #include <core_io.h> #include <httpserver.h> #include <validation.h> +#include <key_io.h> #include <net.h> #include <policy/feerate.h> #include <policy/fees.h> #include <policy/policy.h> #include <policy/rbf.h> #include <rpc/mining.h> +#include <rpc/rawtransaction.h> #include <rpc/safemode.h> #include <rpc/server.h> #include <rpc/util.h> @@ -25,6 +26,7 @@ #include <utilmoneystr.h> #include <wallet/coincontrol.h> #include <wallet/feebumper.h> +#include <wallet/rpcwallet.h> #include <wallet/wallet.h> #include <wallet/walletdb.h> #include <wallet/walletutil.h> @@ -93,7 +95,7 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) { entry.pushKV("blockhash", wtx.hashBlock.GetHex()); entry.pushKV("blockindex", wtx.nIndex); - entry.pushKV("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime()); + entry.pushKV("blocktime", LookupBlockIndex(wtx.hashBlock)->GetBlockTime()); } else { entry.pushKV("trusted", wtx.IsTrusted()); } @@ -402,7 +404,7 @@ UniValue getaddressesbyaccount(const JSONRPCRequest& request) return ret; } -static void SendMoney(CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew, const CCoinControl& coin_control) +static CTransactionRef SendMoney(CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, const CCoinControl& coin_control, mapValue_t mapValue, std::string fromAccount) { CAmount curBalance = pwallet->GetBalance(); @@ -428,16 +430,18 @@ static void SendMoney(CWallet * const pwallet, const CTxDestination &address, CA int nChangePosRet = -1; CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount}; vecSend.push_back(recipient); - if (!pwallet->CreateTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError, coin_control)) { + CTransactionRef tx; + if (!pwallet->CreateTransaction(vecSend, tx, reservekey, nFeeRequired, nChangePosRet, strError, coin_control)) { if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); throw JSONRPCError(RPC_WALLET_ERROR, strError); } CValidationState state; - if (!pwallet->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) { - strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); + if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, std::move(fromAccount), reservekey, g_connman.get(), state)) { + strError = strprintf("Error: The transaction was rejected! Reason given: %s", FormatStateMessage(state)); throw JSONRPCError(RPC_WALLET_ERROR, strError); } + return tx; } UniValue sendtoaddress(const JSONRPCRequest& request) @@ -496,11 +500,11 @@ UniValue sendtoaddress(const JSONRPCRequest& request) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); // Wallet comments - CWalletTx wtx; + mapValue_t mapValue; if (!request.params[2].isNull() && !request.params[2].get_str().empty()) - wtx.mapValue["comment"] = request.params[2].get_str(); + mapValue["comment"] = request.params[2].get_str(); if (!request.params[3].isNull() && !request.params[3].get_str().empty()) - wtx.mapValue["to"] = request.params[3].get_str(); + mapValue["to"] = request.params[3].get_str(); bool fSubtractFeeFromAmount = false; if (!request.params[4].isNull()) { @@ -525,9 +529,8 @@ UniValue sendtoaddress(const JSONRPCRequest& request) EnsureWalletIsUnlocked(pwallet); - SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, wtx, coin_control); - - return wtx.GetHash().GetHex(); + CTransactionRef tx = SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, coin_control, std::move(mapValue), {} /* fromAccount */); + return tx->GetHash().GetHex(); } UniValue listaddressgroupings(const JSONRPCRequest& request) @@ -993,12 +996,11 @@ UniValue sendfrom(const JSONRPCRequest& request) if (!request.params[3].isNull()) nMinDepth = request.params[3].get_int(); - CWalletTx wtx; - wtx.strFromAccount = strAccount; + mapValue_t mapValue; if (!request.params[4].isNull() && !request.params[4].get_str().empty()) - wtx.mapValue["comment"] = request.params[4].get_str(); + mapValue["comment"] = request.params[4].get_str(); if (!request.params[5].isNull() && !request.params[5].get_str().empty()) - wtx.mapValue["to"] = request.params[5].get_str(); + mapValue["to"] = request.params[5].get_str(); EnsureWalletIsUnlocked(pwallet); @@ -1008,9 +1010,8 @@ UniValue sendfrom(const JSONRPCRequest& request) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); CCoinControl no_coin_control; // This is a deprecated API - SendMoney(pwallet, dest, nAmount, false, wtx, no_coin_control); - - return wtx.GetHash().GetHex(); + CTransactionRef tx = SendMoney(pwallet, dest, nAmount, false, no_coin_control, std::move(mapValue), std::move(strAccount)); + return tx->GetHash().GetHex(); } @@ -1081,10 +1082,9 @@ UniValue sendmany(const JSONRPCRequest& request) if (!request.params[2].isNull()) nMinDepth = request.params[2].get_int(); - CWalletTx wtx; - wtx.strFromAccount = strAccount; + mapValue_t mapValue; if (!request.params[3].isNull() && !request.params[3].get_str().empty()) - wtx.mapValue["comment"] = request.params[3].get_str(); + mapValue["comment"] = request.params[3].get_str(); UniValue subtractFeeFromAmount(UniValue::VARR); if (!request.params[4].isNull()) @@ -1150,16 +1150,17 @@ UniValue sendmany(const JSONRPCRequest& request) CAmount nFeeRequired = 0; int nChangePosRet = -1; std::string strFailReason; - bool fCreated = pwallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason, coin_control); + CTransactionRef tx; + bool fCreated = pwallet->CreateTransaction(vecSend, tx, keyChange, nFeeRequired, nChangePosRet, strFailReason, coin_control); if (!fCreated) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); CValidationState state; - if (!pwallet->CommitTransaction(wtx, keyChange, g_connman.get(), state)) { - strFailReason = strprintf("Transaction commit failed:: %s", state.GetRejectReason()); + if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, std::move(strAccount), keyChange, g_connman.get(), state)) { + strFailReason = strprintf("Transaction commit failed:: %s", FormatStateMessage(state)); throw JSONRPCError(RPC_WALLET_ERROR, strFailReason); } - return wtx.GetHash().GetHex(); + return tx->GetHash().GetHex(); } UniValue addmultisigaddress(const JSONRPCRequest& request) @@ -1401,6 +1402,16 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA if(params[2].get_bool()) filter = filter | ISMINE_WATCH_ONLY; + bool has_filtered_address = false; + CTxDestination filtered_address = CNoDestination(); + if (!fByAccounts && params.size() > 3) { + if (!IsValidDestinationString(params[3].get_str())) { + throw JSONRPCError(RPC_WALLET_ERROR, "address_filter parameter was invalid"); + } + filtered_address = DecodeDestination(params[3].get_str()); + has_filtered_address = true; + } + // Tally std::map<CTxDestination, tallyitem> mapTally; for (const std::pair<uint256, CWalletTx>& pairWtx : pwallet->mapWallet) { @@ -1419,6 +1430,10 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA if (!ExtractDestination(txout.scriptPubKey, address)) continue; + if (has_filtered_address && !(filtered_address == address)) { + continue; + } + isminefilter mine = IsMine(*pwallet, address); if(!(mine & filter)) continue; @@ -1435,10 +1450,24 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA // Reply UniValue ret(UniValue::VARR); std::map<std::string, tallyitem> mapAccountTally; - for (const std::pair<CTxDestination, CAddressBookData>& item : pwallet->mapAddressBook) { - const CTxDestination& dest = item.first; - const std::string& strAccount = item.second.name; - std::map<CTxDestination, tallyitem>::iterator it = mapTally.find(dest); + + // Create mapAddressBook iterator + // If we aren't filtering, go from begin() to end() + auto start = pwallet->mapAddressBook.begin(); + auto end = pwallet->mapAddressBook.end(); + // If we are filtering, find() the applicable entry + if (has_filtered_address) { + start = pwallet->mapAddressBook.find(filtered_address); + if (start != end) { + end = std::next(start); + } + } + + for (auto item_it = start; item_it != end; ++item_it) + { + const CTxDestination& address = item_it->first; + const std::string& strAccount = item_it->second.name; + auto it = mapTally.find(address); if (it == mapTally.end() && !fIncludeEmpty) continue; @@ -1464,7 +1493,7 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA UniValue obj(UniValue::VOBJ); if(fIsWatchonly) obj.pushKV("involvesWatchonly", true); - obj.pushKV("address", EncodeDestination(dest)); + obj.pushKV("address", EncodeDestination(address)); obj.pushKV("account", strAccount); obj.pushKV("amount", ValueFromAmount(nAmount)); obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)); @@ -1509,15 +1538,15 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request) return NullUniValue; } - if (request.fHelp || request.params.size() > 3) + if (request.fHelp || request.params.size() > 4) throw std::runtime_error( - "listreceivedbyaddress ( minconf include_empty include_watchonly)\n" + "listreceivedbyaddress ( minconf include_empty include_watchonly address_filter )\n" "\nList balances by receiving address.\n" "\nArguments:\n" "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" "2. include_empty (bool, optional, default=false) Whether to include addresses that haven't received any payments.\n" "3. include_watchonly (bool, optional, default=false) Whether to include watch-only addresses (see 'importaddress').\n" - + "4. address_filter (string, optional) If present, only return information on this address.\n" "\nResult:\n" "[\n" " {\n" @@ -1539,6 +1568,7 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request) + HelpExampleCli("listreceivedbyaddress", "") + HelpExampleCli("listreceivedbyaddress", "6 true") + HelpExampleRpc("listreceivedbyaddress", "6, true, true") + + HelpExampleRpc("listreceivedbyaddress", "6, true, true, \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\"") ); ObserveSafeMode(); @@ -2012,11 +2042,10 @@ UniValue listsinceblock(const JSONRPCRequest& request) uint256 blockId; blockId.SetHex(request.params[0].get_str()); - BlockMap::iterator it = mapBlockIndex.find(blockId); - if (it == mapBlockIndex.end()) { + paltindex = pindex = LookupBlockIndex(blockId); + if (!pindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } - paltindex = pindex = it->second; if (chainActive[pindex->nHeight] != pindex) { // the block being asked for is a part of a deactivated chain; // we don't want to depend on its perceived height in the block @@ -3129,7 +3158,6 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) {"change_type", UniValueType(UniValue::VSTR)}, {"includeWatching", UniValueType(UniValue::VBOOL)}, {"lockUnspents", UniValueType(UniValue::VBOOL)}, - {"reserveChangeKey", UniValueType(UniValue::VBOOL)}, // DEPRECATED (and ignored), should be removed in 0.16 or so. {"feeRate", UniValueType()}, // will be checked below {"subtractFeeFromOutputs", UniValueType(UniValue::VARR)}, {"replaceable", UniValueType(UniValue::VBOOL)}, @@ -3236,6 +3264,75 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) return result; } +UniValue signrawtransactionwithwallet(const JSONRPCRequest& request) +{ + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { + return NullUniValue; + } + + if (request.fHelp || request.params.size() < 1 || request.params.size() > 3) + throw std::runtime_error( + "signrawtransactionwithwallet \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] sighashtype )\n" + "\nSign inputs for raw transaction (serialized, hex-encoded).\n" + "The second optional argument (may be null) is an array of previous transaction outputs that\n" + "this transaction depends on but may not yet be in the block chain.\n" + + HelpRequiringPassphrase(pwallet) + "\n" + + "\nArguments:\n" + "1. \"hexstring\" (string, required) The transaction hex string\n" + "2. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n" + " [ (json array of json objects, or 'null' if none provided)\n" + " {\n" + " \"txid\":\"id\", (string, required) The transaction id\n" + " \"vout\":n, (numeric, required) The output number\n" + " \"scriptPubKey\": \"hex\", (string, required) script key\n" + " \"redeemScript\": \"hex\", (string, required for P2SH or P2WSH) redeem script\n" + " \"amount\": value (numeric, required) The amount spent\n" + " }\n" + " ,...\n" + " ]\n" + "3. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n" + " \"ALL\"\n" + " \"NONE\"\n" + " \"SINGLE\"\n" + " \"ALL|ANYONECANPAY\"\n" + " \"NONE|ANYONECANPAY\"\n" + " \"SINGLE|ANYONECANPAY\"\n" + + "\nResult:\n" + "{\n" + " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n" + " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n" + " \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n" + " {\n" + " \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n" + " \"vout\" : n, (numeric) The index of the output to spent and used as input\n" + " \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n" + " \"sequence\" : n, (numeric) Script sequence number\n" + " \"error\" : \"text\" (string) Verification or signing error related to the input\n" + " }\n" + " ,...\n" + " ]\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") + + HelpExampleRpc("signrawtransactionwithwallet", "\"myhex\"") + ); + + RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VSTR}, true); + + CMutableTransaction mtx; + if (!DecodeHexTx(mtx, request.params[0].get_str(), true)) { + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + } + + // Sign the transaction + LOCK2(cs_main, pwallet->cs_wallet); + return SignTransaction(mtx, request.params[1], pwallet, false, request.params[2]); +} + UniValue bumpfee(const JSONRPCRequest& request) { CWallet * const pwallet = GetWalletForJSONRPCRequest(request); @@ -3515,6 +3612,209 @@ UniValue rescanblockchain(const JSONRPCRequest& request) return response; } +class DescribeWalletAddressVisitor : public boost::static_visitor<UniValue> +{ +public: + CWallet * const pwallet; + + void ProcessSubScript(const CScript& subscript, UniValue& obj, bool include_addresses = false) const + { + // Always present: script type and redeemscript + txnouttype which_type; + std::vector<std::vector<unsigned char>> solutions_data; + Solver(subscript, which_type, solutions_data); + obj.pushKV("script", GetTxnOutputType(which_type)); + obj.pushKV("hex", HexStr(subscript.begin(), subscript.end())); + + CTxDestination embedded; + UniValue a(UniValue::VARR); + if (ExtractDestination(subscript, embedded)) { + // Only when the script corresponds to an address. + UniValue subobj(UniValue::VOBJ); + UniValue detail = DescribeAddress(embedded); + subobj.pushKVs(detail); + UniValue wallet_detail = boost::apply_visitor(*this, embedded); + subobj.pushKVs(wallet_detail); + subobj.pushKV("address", EncodeDestination(embedded)); + subobj.pushKV("scriptPubKey", HexStr(subscript.begin(), subscript.end())); + // Always report the pubkey at the top level, so that `getnewaddress()['pubkey']` always works. + if (subobj.exists("pubkey")) obj.pushKV("pubkey", subobj["pubkey"]); + obj.pushKV("embedded", std::move(subobj)); + if (include_addresses) a.push_back(EncodeDestination(embedded)); + } else if (which_type == TX_MULTISIG) { + // Also report some information on multisig scripts (which do not have a corresponding address). + // TODO: abstract out the common functionality between this logic and ExtractDestinations. + obj.pushKV("sigsrequired", solutions_data[0][0]); + UniValue pubkeys(UniValue::VARR); + for (size_t i = 1; i < solutions_data.size() - 1; ++i) { + CPubKey key(solutions_data[i].begin(), solutions_data[i].end()); + if (include_addresses) a.push_back(EncodeDestination(key.GetID())); + pubkeys.push_back(HexStr(key.begin(), key.end())); + } + obj.pushKV("pubkeys", std::move(pubkeys)); + } + + // The "addresses" field is confusing because it refers to public keys using their P2PKH address. + // For that reason, only add the 'addresses' field when needed for backward compatibility. New applications + // can use the 'embedded'->'address' field for P2SH or P2WSH wrapped addresses, and 'pubkeys' for + // inspecting multisig participants. + if (include_addresses) obj.pushKV("addresses", std::move(a)); + } + + explicit DescribeWalletAddressVisitor(CWallet* _pwallet) : pwallet(_pwallet) {} + + UniValue operator()(const CNoDestination& dest) const { return UniValue(UniValue::VOBJ); } + + UniValue operator()(const CKeyID& keyID) const + { + UniValue obj(UniValue::VOBJ); + CPubKey vchPubKey; + if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) { + obj.pushKV("pubkey", HexStr(vchPubKey)); + obj.pushKV("iscompressed", vchPubKey.IsCompressed()); + } + return obj; + } + + UniValue operator()(const CScriptID& scriptID) const + { + UniValue obj(UniValue::VOBJ); + CScript subscript; + if (pwallet && pwallet->GetCScript(scriptID, subscript)) { + ProcessSubScript(subscript, obj, IsDeprecatedRPCEnabled("validateaddress")); + } + return obj; + } + + UniValue operator()(const WitnessV0KeyHash& id) const + { + UniValue obj(UniValue::VOBJ); + CPubKey pubkey; + if (pwallet && pwallet->GetPubKey(CKeyID(id), pubkey)) { + obj.pushKV("pubkey", HexStr(pubkey)); + } + return obj; + } + + UniValue operator()(const WitnessV0ScriptHash& id) const + { + UniValue obj(UniValue::VOBJ); + CScript subscript; + CRIPEMD160 hasher; + uint160 hash; + hasher.Write(id.begin(), 32).Finalize(hash.begin()); + if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) { + ProcessSubScript(subscript, obj); + } + return obj; + } + + UniValue operator()(const WitnessUnknown& id) const { return UniValue(UniValue::VOBJ); } +}; + +UniValue DescribeWalletAddress(CWallet* pwallet, const CTxDestination& dest) +{ + UniValue ret(UniValue::VOBJ); + UniValue detail = DescribeAddress(dest); + ret.pushKVs(detail); + ret.pushKVs(boost::apply_visitor(DescribeWalletAddressVisitor(pwallet), dest)); + return ret; +} + +UniValue getaddressinfo(const JSONRPCRequest& request) +{ + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { + return NullUniValue; + } + + if (request.fHelp || request.params.size() != 1) { + throw std::runtime_error( + "getaddressinfo \"address\"\n" + "\nReturn information about the given bitcoin address. Some information requires the address\n" + "to be in the wallet.\n" + "\nArguments:\n" + "1. \"address\" (string, required) The bitcoin address to get the information of.\n" + "\nResult:\n" + "{\n" + " \"address\" : \"address\", (string) The bitcoin address validated\n" + " \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\n" + " \"ismine\" : true|false, (boolean) If the address is yours or not\n" + " \"iswatchonly\" : true|false, (boolean) If the address is watchonly\n" + " \"isscript\" : true|false, (boolean) If the key is a script\n" + " \"iswitness\" : true|false, (boolean) If the address is a witness address\n" + " \"witness_version\" : version (numeric, optional) The version number of the witness program\n" + " \"witness_program\" : \"hex\" (string, optional) The hex value of the witness program\n" + " \"script\" : \"type\" (string, optional) The output script type. Only if \"isscript\" is true and the redeemscript is known. Possible types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash, witness_v0_scripthash, witness_unknown\n" + " \"hex\" : \"hex\", (string, optional) The redeemscript for the p2sh address\n" + " \"pubkeys\" (string, optional) Array of pubkeys associated with the known redeemscript (only if \"script\" is \"multisig\")\n" + " [\n" + " \"pubkey\"\n" + " ,...\n" + " ]\n" + " \"sigsrequired\" : xxxxx (numeric, optional) Number of signatures required to spend multisig output (only if \"script\" is \"multisig\")\n" + " \"pubkey\" : \"publickeyhex\", (string, optional) The hex value of the raw public key, for single-key addresses (possibly embedded in P2SH or P2WSH)\n" + " \"embedded\" : {...}, (object, optional) Information about the address embedded in P2SH or P2WSH, if relevant and known. It includes all getaddressinfo output fields for the embedded address, excluding metadata (\"timestamp\", \"hdkeypath\", \"hdmasterkeyid\") and relation to the wallet (\"ismine\", \"iswatchonly\", \"account\").\n" + " \"iscompressed\" : true|false, (boolean) If the address is compressed\n" + " \"account\" : \"account\" (string) The account associated with the address, \"\" is the default account\n" + " \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n" + " \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n" + " \"hdmasterkeyid\" : \"<hash160>\" (string, optional) The Hash160 of the HD master pubkey\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getaddressinfo", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") + + HelpExampleRpc("getaddressinfo", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") + ); + } + + LOCK(pwallet->cs_wallet); + + UniValue ret(UniValue::VOBJ); + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + + // Make sure the destination is valid + if (!IsValidDestination(dest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + } + + std::string currentAddress = EncodeDestination(dest); + ret.pushKV("address", currentAddress); + + CScript scriptPubKey = GetScriptForDestination(dest); + ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())); + + isminetype mine = IsMine(*pwallet, dest); + ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE)); + ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY)); + UniValue detail = DescribeWalletAddress(pwallet, dest); + ret.pushKVs(detail); + if (pwallet->mapAddressBook.count(dest)) { + ret.pushKV("account", pwallet->mapAddressBook[dest].name); + } + const CKeyMetadata* meta = nullptr; + CKeyID key_id = GetKeyForDestination(*pwallet, dest); + if (!key_id.IsNull()) { + auto it = pwallet->mapKeyMetadata.find(key_id); + if (it != pwallet->mapKeyMetadata.end()) { + meta = &it->second; + } + } + if (!meta) { + auto it = pwallet->m_script_metadata.find(CScriptID(scriptPubKey)); + if (it != pwallet->m_script_metadata.end()) { + meta = &it->second; + } + } + if (meta) { + ret.pushKV("timestamp", meta->nCreateTime); + if (!meta->hdKeypath.empty()) { + ret.pushKV("hdkeypath", meta->hdKeypath); + ret.pushKV("hdmasterkeyid", meta->hdMasterKeyID.GetHex()); + } + } + return ret; +} + extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp extern UniValue importprivkey(const JSONRPCRequest& request); @@ -3528,61 +3828,63 @@ extern UniValue importmulti(const JSONRPCRequest& request); extern UniValue rescanblockchain(const JSONRPCRequest& request); static const CRPCCommand commands[] = -{ // category name actor (function) argNames - // --------------------- ------------------------ ----------------------- ---------- - { "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options","iswitness"} }, - { "hidden", "resendwallettransactions", &resendwallettransactions, {} }, - { "wallet", "abandontransaction", &abandontransaction, {"txid"} }, - { "wallet", "abortrescan", &abortrescan, {} }, - { "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","account","address_type"} }, - { "hidden", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} }, - { "wallet", "backupwallet", &backupwallet, {"destination"} }, - { "wallet", "bumpfee", &bumpfee, {"txid", "options"} }, - { "wallet", "dumpprivkey", &dumpprivkey, {"address"} }, - { "wallet", "dumpwallet", &dumpwallet, {"filename"} }, - { "wallet", "encryptwallet", &encryptwallet, {"passphrase"} }, - { "wallet", "getaccountaddress", &getaccountaddress, {"account"} }, - { "wallet", "getaccount", &getaccount, {"address"} }, - { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, {"account"} }, - { "wallet", "getbalance", &getbalance, {"account","minconf","include_watchonly"} }, - { "wallet", "getnewaddress", &getnewaddress, {"account","address_type"} }, - { "wallet", "getrawchangeaddress", &getrawchangeaddress, {"address_type"} }, - { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, {"account","minconf"} }, - { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} }, - { "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly"} }, - { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} }, - { "wallet", "getwalletinfo", &getwalletinfo, {} }, - { "wallet", "importmulti", &importmulti, {"requests","options"} }, - { "wallet", "importprivkey", &importprivkey, {"privkey","label","rescan"} }, - { "wallet", "importwallet", &importwallet, {"filename"} }, - { "wallet", "importaddress", &importaddress, {"address","label","rescan","p2sh"} }, - { "wallet", "importprunedfunds", &importprunedfunds, {"rawtransaction","txoutproof"} }, - { "wallet", "importpubkey", &importpubkey, {"pubkey","label","rescan"} }, - { "wallet", "keypoolrefill", &keypoolrefill, {"newsize"} }, - { "wallet", "listaccounts", &listaccounts, {"minconf","include_watchonly"} }, - { "wallet", "listaddressgroupings", &listaddressgroupings, {} }, - { "wallet", "listlockunspent", &listlockunspent, {} }, - { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, {"minconf","include_empty","include_watchonly"} }, - { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, {"minconf","include_empty","include_watchonly"} }, - { "wallet", "listsinceblock", &listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} }, - { "wallet", "listtransactions", &listtransactions, {"account","count","skip","include_watchonly"} }, - { "wallet", "listunspent", &listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} }, - { "wallet", "listwallets", &listwallets, {} }, - { "wallet", "lockunspent", &lockunspent, {"unlock","transactions"} }, - { "wallet", "move", &movecmd, {"fromaccount","toaccount","amount","minconf","comment"} }, - { "wallet", "sendfrom", &sendfrom, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} }, - { "wallet", "sendmany", &sendmany, {"fromaccount","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} }, - { "wallet", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode"} }, - { "wallet", "setaccount", &setaccount, {"address","account"} }, - { "wallet", "settxfee", &settxfee, {"amount"} }, - { "wallet", "signmessage", &signmessage, {"address","message"} }, - { "wallet", "walletlock", &walletlock, {} }, - { "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} }, - { "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} }, - { "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} }, - { "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} }, - - { "generating", "generate", &generate, {"nblocks","maxtries"} }, +{ // category name actor (function) argNames + // --------------------- ------------------------ ----------------------- ---------- + { "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options","iswitness"} }, + { "hidden", "resendwallettransactions", &resendwallettransactions, {} }, + { "wallet", "abandontransaction", &abandontransaction, {"txid"} }, + { "wallet", "abortrescan", &abortrescan, {} }, + { "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","account","address_type"} }, + { "hidden", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} }, + { "wallet", "backupwallet", &backupwallet, {"destination"} }, + { "wallet", "bumpfee", &bumpfee, {"txid", "options"} }, + { "wallet", "dumpprivkey", &dumpprivkey, {"address"} }, + { "wallet", "dumpwallet", &dumpwallet, {"filename"} }, + { "wallet", "encryptwallet", &encryptwallet, {"passphrase"} }, + { "wallet", "getaccountaddress", &getaccountaddress, {"account"} }, + { "wallet", "getaccount", &getaccount, {"address"} }, + { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, {"account"} }, + { "wallet", "getaddressinfo", &getaddressinfo, {"address"} }, + { "wallet", "getbalance", &getbalance, {"account","minconf","include_watchonly"} }, + { "wallet", "getnewaddress", &getnewaddress, {"account","address_type"} }, + { "wallet", "getrawchangeaddress", &getrawchangeaddress, {"address_type"} }, + { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, {"account","minconf"} }, + { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} }, + { "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly"} }, + { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} }, + { "wallet", "getwalletinfo", &getwalletinfo, {} }, + { "wallet", "importmulti", &importmulti, {"requests","options"} }, + { "wallet", "importprivkey", &importprivkey, {"privkey","label","rescan"} }, + { "wallet", "importwallet", &importwallet, {"filename"} }, + { "wallet", "importaddress", &importaddress, {"address","label","rescan","p2sh"} }, + { "wallet", "importprunedfunds", &importprunedfunds, {"rawtransaction","txoutproof"} }, + { "wallet", "importpubkey", &importpubkey, {"pubkey","label","rescan"} }, + { "wallet", "keypoolrefill", &keypoolrefill, {"newsize"} }, + { "wallet", "listaccounts", &listaccounts, {"minconf","include_watchonly"} }, + { "wallet", "listaddressgroupings", &listaddressgroupings, {} }, + { "wallet", "listlockunspent", &listlockunspent, {} }, + { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, {"minconf","include_empty","include_watchonly"} }, + { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, {"minconf","include_empty","include_watchonly","address_filter"} }, + { "wallet", "listsinceblock", &listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} }, + { "wallet", "listtransactions", &listtransactions, {"account","count","skip","include_watchonly"} }, + { "wallet", "listunspent", &listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} }, + { "wallet", "listwallets", &listwallets, {} }, + { "wallet", "lockunspent", &lockunspent, {"unlock","transactions"} }, + { "wallet", "move", &movecmd, {"fromaccount","toaccount","amount","minconf","comment"} }, + { "wallet", "sendfrom", &sendfrom, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} }, + { "wallet", "sendmany", &sendmany, {"fromaccount","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} }, + { "wallet", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode"} }, + { "wallet", "setaccount", &setaccount, {"address","account"} }, + { "wallet", "settxfee", &settxfee, {"amount"} }, + { "wallet", "signmessage", &signmessage, {"address","message"} }, + { "wallet", "signrawtransactionwithwallet", &signrawtransactionwithwallet, {"hexstring","prevtxs","sighashtype"} }, + { "wallet", "walletlock", &walletlock, {} }, + { "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} }, + { "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} }, + { "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} }, + { "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} }, + + { "generating", "generate", &generate, {"nblocks","maxtries"} }, }; void RegisterWalletRPCCommands(CRPCTable &t) diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h index 77f7b42b23..84f161abb5 100644 --- a/src/wallet/rpcwallet.h +++ b/src/wallet/rpcwallet.h @@ -10,6 +10,7 @@ class CRPCTable; class CWallet; class JSONRPCRequest; +class UniValue; void RegisterWalletRPCCommands(CRPCTable &t); @@ -25,4 +26,6 @@ std::string HelpRequiringPassphrase(CWallet *); void EnsureWalletIsUnlocked(CWallet *); bool EnsureWalletIsAvailable(CWallet *, bool avoidException); +UniValue getaddressinfo(const JSONRPCRequest& request); +UniValue signrawtransactionwithwallet(const JSONRPCRequest& request); #endif //BITCOIN_WALLET_RPCWALLET_H diff --git a/src/wallet/test/accounting_tests.cpp b/src/wallet/test/accounting_tests.cpp index cafd69d075..aae328d81f 100644 --- a/src/wallet/test/accounting_tests.cpp +++ b/src/wallet/test/accounting_tests.cpp @@ -13,13 +13,13 @@ BOOST_FIXTURE_TEST_SUITE(accounting_tests, WalletTestingSetup) static void -GetResults(CWallet *wallet, std::map<CAmount, CAccountingEntry>& results) +GetResults(CWallet& wallet, std::map<CAmount, CAccountingEntry>& results) { std::list<CAccountingEntry> aes; results.clear(); - BOOST_CHECK(wallet->ReorderTransactions() == DB_LOAD_OK); - wallet->ListAccountCreditDebit("", aes); + BOOST_CHECK(wallet.ReorderTransactions() == DB_LOAD_OK); + wallet.ListAccountCreditDebit("", aes); for (CAccountingEntry& ae : aes) { results[ae.nOrderPos] = ae; @@ -29,32 +29,32 @@ GetResults(CWallet *wallet, std::map<CAmount, CAccountingEntry>& results) BOOST_AUTO_TEST_CASE(acc_orderupgrade) { std::vector<CWalletTx*> vpwtx; - CWalletTx wtx; + CWalletTx wtx(nullptr /* pwallet */, MakeTransactionRef()); CAccountingEntry ae; std::map<CAmount, CAccountingEntry> results; - LOCK(pwalletMain->cs_wallet); + LOCK(m_wallet.cs_wallet); ae.strAccount = ""; ae.nCreditDebit = 1; ae.nTime = 1333333333; ae.strOtherAccount = "b"; ae.strComment = ""; - pwalletMain->AddAccountingEntry(ae); + m_wallet.AddAccountingEntry(ae); wtx.mapValue["comment"] = "z"; - pwalletMain->AddToWallet(wtx); - vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); + m_wallet.AddToWallet(wtx); + vpwtx.push_back(&m_wallet.mapWallet.at(wtx.GetHash())); vpwtx[0]->nTimeReceived = (unsigned int)1333333335; vpwtx[0]->nOrderPos = -1; ae.nTime = 1333333336; ae.strOtherAccount = "c"; - pwalletMain->AddAccountingEntry(ae); + m_wallet.AddAccountingEntry(ae); - GetResults(pwalletMain.get(), results); + GetResults(m_wallet, results); - BOOST_CHECK(pwalletMain->nOrderPosNext == 3); + BOOST_CHECK(m_wallet.nOrderPosNext == 3); BOOST_CHECK(2 == results.size()); BOOST_CHECK(results[0].nTime == 1333333333); BOOST_CHECK(results[0].strComment.empty()); @@ -65,13 +65,13 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) ae.nTime = 1333333330; ae.strOtherAccount = "d"; - ae.nOrderPos = pwalletMain->IncOrderPosNext(); - pwalletMain->AddAccountingEntry(ae); + ae.nOrderPos = m_wallet.IncOrderPosNext(); + m_wallet.AddAccountingEntry(ae); - GetResults(pwalletMain.get(), results); + GetResults(m_wallet, results); BOOST_CHECK(results.size() == 3); - BOOST_CHECK(pwalletMain->nOrderPosNext == 4); + BOOST_CHECK(m_wallet.nOrderPosNext == 4); BOOST_CHECK(results[0].nTime == 1333333333); BOOST_CHECK(1 == vpwtx[0]->nOrderPos); BOOST_CHECK(results[2].nTime == 1333333336); @@ -82,28 +82,28 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) wtx.mapValue["comment"] = "y"; { CMutableTransaction tx(*wtx.tx); - --tx.nLockTime; // Just to change the hash :) + ++tx.nLockTime; // Just to change the hash :) wtx.SetTx(MakeTransactionRef(std::move(tx))); } - pwalletMain->AddToWallet(wtx); - vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); + m_wallet.AddToWallet(wtx); + vpwtx.push_back(&m_wallet.mapWallet.at(wtx.GetHash())); vpwtx[1]->nTimeReceived = (unsigned int)1333333336; wtx.mapValue["comment"] = "x"; { CMutableTransaction tx(*wtx.tx); - --tx.nLockTime; // Just to change the hash :) + ++tx.nLockTime; // Just to change the hash :) wtx.SetTx(MakeTransactionRef(std::move(tx))); } - pwalletMain->AddToWallet(wtx); - vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]); + m_wallet.AddToWallet(wtx); + vpwtx.push_back(&m_wallet.mapWallet.at(wtx.GetHash())); vpwtx[2]->nTimeReceived = (unsigned int)1333333329; vpwtx[2]->nOrderPos = -1; - GetResults(pwalletMain.get(), results); + GetResults(m_wallet, results); BOOST_CHECK(results.size() == 3); - BOOST_CHECK(pwalletMain->nOrderPosNext == 6); + BOOST_CHECK(m_wallet.nOrderPosNext == 6); BOOST_CHECK(0 == vpwtx[2]->nOrderPos); BOOST_CHECK(results[1].nTime == 1333333333); BOOST_CHECK(2 == vpwtx[0]->nOrderPos); @@ -116,12 +116,12 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade) ae.nTime = 1333333334; ae.strOtherAccount = "e"; ae.nOrderPos = -1; - pwalletMain->AddAccountingEntry(ae); + m_wallet.AddAccountingEntry(ae); - GetResults(pwalletMain.get(), results); + GetResults(m_wallet, results); BOOST_CHECK(results.size() == 4); - BOOST_CHECK(pwalletMain->nOrderPosNext == 7); + BOOST_CHECK(m_wallet.nOrderPosNext == 7); BOOST_CHECK(0 == vpwtx[2]->nOrderPos); BOOST_CHECK(results[1].nTime == 1333333333); BOOST_CHECK(2 == vpwtx[0]->nOrderPos); diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp index 7797f85f07..77ccd0b8d8 100644 --- a/src/wallet/test/wallet_test_fixture.cpp +++ b/src/wallet/test/wallet_test_fixture.cpp @@ -6,27 +6,21 @@ #include <rpc/server.h> #include <wallet/db.h> +#include <wallet/wallet.h> WalletTestingSetup::WalletTestingSetup(const std::string& chainName): - TestingSetup(chainName) + TestingSetup(chainName), m_wallet("mock", CWalletDBWrapper::CreateMock()) { - bitdb.MakeMock(); - bool fFirstRun; g_address_type = OUTPUT_TYPE_DEFAULT; g_change_type = OUTPUT_TYPE_DEFAULT; - std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat")); - pwalletMain = MakeUnique<CWallet>(std::move(dbw)); - pwalletMain->LoadWallet(fFirstRun); - RegisterValidationInterface(pwalletMain.get()); + m_wallet.LoadWallet(fFirstRun); + RegisterValidationInterface(&m_wallet); RegisterWalletRPCCommands(tableRPC); } WalletTestingSetup::~WalletTestingSetup() { - UnregisterValidationInterface(pwalletMain.get()); - - bitdb.Flush(true); - bitdb.Reset(); + UnregisterValidationInterface(&m_wallet); } diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h index c03aec7f87..663836a955 100644 --- a/src/wallet/test/wallet_test_fixture.h +++ b/src/wallet/test/wallet_test_fixture.h @@ -15,7 +15,7 @@ struct WalletTestingSetup: public TestingSetup { explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN); ~WalletTestingSetup(); - std::unique_ptr<CWallet> pwalletMain; + CWallet m_wallet; }; #endif diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 161372784b..3373b51f2a 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -36,7 +36,7 @@ typedef std::set<CInputCoin> CoinSet; BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup) -static const CWallet testWallet; +static const CWallet testWallet("dummy", CWalletDBWrapper::CreateDummy()); static std::vector<COutput> vCoins; static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0) @@ -382,7 +382,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) // Verify ScanForWalletTransactions picks up transactions in both the old // and new block files. { - CWallet wallet; + CWallet wallet("dummy", CWalletDBWrapper::CreateDummy()); AddKey(wallet, coinbaseKey); WalletRescanReserver reserver(&wallet); reserver.reserve(); @@ -397,7 +397,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) // Verify ScanForWalletTransactions only picks transactions in the new block // file. { - CWallet wallet; + CWallet wallet("dummy", CWalletDBWrapper::CreateDummy()); AddKey(wallet, coinbaseKey); WalletRescanReserver reserver(&wallet); reserver.reserve(); @@ -409,7 +409,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) // before the missing block, and success for a key whose creation time is // after. { - CWallet wallet; + CWallet wallet("dummy", CWalletDBWrapper::CreateDummy()); vpwallets.insert(vpwallets.begin(), &wallet); UniValue keys; keys.setArray(); @@ -451,6 +451,9 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) // than or equal to key birthday. BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) { + g_address_type = OUTPUT_TYPE_DEFAULT; + g_change_type = OUTPUT_TYPE_DEFAULT; + // Create two blocks with same timestamp to verify that importwallet rescan // will pick up both blocks, not just the first. const int64_t BLOCK_TIME = chainActive.Tip()->GetBlockTimeMax() + 5; @@ -468,7 +471,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) // Import key into wallet and call dumpwallet to create backup file. { - CWallet wallet; + CWallet wallet("dummy", CWalletDBWrapper::CreateDummy()); LOCK(wallet.cs_wallet); wallet.mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME; wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); @@ -483,7 +486,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) // Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME // were scanned, and no prior blocks were scanned. { - CWallet wallet; + CWallet wallet("dummy", CWalletDBWrapper::CreateDummy()); JSONRPCRequest request; request.params.setArray(); @@ -513,7 +516,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) // debit functions. BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup) { - CWallet wallet; + CWallet wallet("dummy", CWalletDBWrapper::CreateDummy()); CWalletTx wtx(&wallet, MakeTransactionRef(coinbaseTxns.back())); LOCK2(cs_main, wallet.cs_wallet); wtx.hashBlock = chainActive.Tip()->GetBlockHash(); @@ -550,7 +553,10 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64 if (block) { wtx.SetMerkleBranch(block, 0); } - wallet.AddToWallet(wtx); + { + LOCK(cs_main); + wallet.AddToWallet(wtx); + } LOCK(wallet.cs_wallet); return wallet.mapWallet.at(wtx.GetHash()).nTimeSmart; } @@ -559,27 +565,25 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64 // expanded to cover more corner cases of smart time logic. BOOST_AUTO_TEST_CASE(ComputeTimeSmart) { - CWallet wallet; - // New transaction should use clock time if lower than block time. - BOOST_CHECK_EQUAL(AddTx(wallet, 1, 100, 120), 100); + BOOST_CHECK_EQUAL(AddTx(m_wallet, 1, 100, 120), 100); // Test that updating existing transaction does not change smart time. - BOOST_CHECK_EQUAL(AddTx(wallet, 1, 200, 220), 100); + BOOST_CHECK_EQUAL(AddTx(m_wallet, 1, 200, 220), 100); // New transaction should use clock time if there's no block time. - BOOST_CHECK_EQUAL(AddTx(wallet, 2, 300, 0), 300); + BOOST_CHECK_EQUAL(AddTx(m_wallet, 2, 300, 0), 300); // New transaction should use block time if lower than clock time. - BOOST_CHECK_EQUAL(AddTx(wallet, 3, 420, 400), 400); + BOOST_CHECK_EQUAL(AddTx(m_wallet, 3, 420, 400), 400); // New transaction should use latest entry time if higher than // min(block time, clock time). - BOOST_CHECK_EQUAL(AddTx(wallet, 4, 500, 390), 400); + BOOST_CHECK_EQUAL(AddTx(m_wallet, 4, 500, 390), 400); // If there are future entries, new transaction should use time of the // newest entry that is no more than 300 seconds ahead of the clock time. - BOOST_CHECK_EQUAL(AddTx(wallet, 5, 50, 600), 300); + BOOST_CHECK_EQUAL(AddTx(m_wallet, 5, 50, 600), 300); // Reset mock time for other tests. SetMockTime(0); @@ -588,12 +592,12 @@ BOOST_AUTO_TEST_CASE(ComputeTimeSmart) BOOST_AUTO_TEST_CASE(LoadReceiveRequests) { CTxDestination dest = CKeyID(); - LOCK(pwalletMain->cs_wallet); - pwalletMain->AddDestData(dest, "misc", "val_misc"); - pwalletMain->AddDestData(dest, "rr0", "val_rr0"); - pwalletMain->AddDestData(dest, "rr1", "val_rr1"); + LOCK(m_wallet.cs_wallet); + m_wallet.AddDestData(dest, "misc", "val_misc"); + m_wallet.AddDestData(dest, "rr0", "val_rr0"); + m_wallet.AddDestData(dest, "rr1", "val_rr1"); - auto values = pwalletMain->GetDestValues("rr"); + auto values = m_wallet.GetDestValues("rr"); BOOST_CHECK_EQUAL(values.size(), 2); BOOST_CHECK_EQUAL(values[0], "val_rr0"); BOOST_CHECK_EQUAL(values[1], "val_rr1"); @@ -605,10 +609,9 @@ public: ListCoinsTestingSetup() { CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); - ::bitdb.MakeMock(); g_address_type = OUTPUT_TYPE_DEFAULT; g_change_type = OUTPUT_TYPE_DEFAULT; - wallet.reset(new CWallet(std::unique_ptr<CWalletDBWrapper>(new CWalletDBWrapper(&bitdb, "wallet_test.dat")))); + wallet = MakeUnique<CWallet>("mock", CWalletDBWrapper::CreateMock()); bool firstRun; wallet->LoadWallet(firstRun); AddKey(*wallet, coinbaseKey); @@ -620,29 +623,27 @@ public: ~ListCoinsTestingSetup() { wallet.reset(); - ::bitdb.Flush(true); - ::bitdb.Reset(); } CWalletTx& AddTx(CRecipient recipient) { - CWalletTx wtx; + CTransactionRef tx; CReserveKey reservekey(wallet.get()); CAmount fee; int changePos = -1; std::string error; CCoinControl dummy; - BOOST_CHECK(wallet->CreateTransaction({recipient}, wtx, reservekey, fee, changePos, error, dummy)); + BOOST_CHECK(wallet->CreateTransaction({recipient}, tx, reservekey, fee, changePos, error, dummy)); CValidationState state; - BOOST_CHECK(wallet->CommitTransaction(wtx, reservekey, nullptr, state)); + BOOST_CHECK(wallet->CommitTransaction(tx, {}, {}, {}, reservekey, nullptr, state)); CMutableTransaction blocktx; { LOCK(wallet->cs_wallet); - blocktx = CMutableTransaction(*wallet->mapWallet.at(wtx.GetHash()).tx); + blocktx = CMutableTransaction(*wallet->mapWallet.at(tx->GetHash()).tx); } CreateAndProcessBlock({CMutableTransaction(blocktx)}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); LOCK(wallet->cs_wallet); - auto it = wallet->mapWallet.find(wtx.GetHash()); + auto it = wallet->mapWallet.find(tx->GetHash()); BOOST_CHECK(it != wallet->mapWallet.end()); it->second.SetMerkleBranch(chainActive.Tip(), 1); return it->second; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 7f36aefeaf..f54405534a 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -5,7 +5,6 @@ #include <wallet/wallet.h> -#include <base58.h> #include <checkpoints.h> #include <chain.h> #include <wallet/coincontrol.h> @@ -14,6 +13,7 @@ #include <fs.h> #include <wallet/init.h> #include <key.h> +#include <key_io.h> #include <keystore.h> #include <validation.h> #include <net.h> @@ -34,7 +34,6 @@ #include <future> #include <boost/algorithm/string/replace.hpp> -#include <boost/thread.hpp> std::vector<CWalletRef> vpwallets; /** Transaction fee set by the user */ @@ -44,8 +43,8 @@ bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE; bool fWalletRbf = DEFAULT_WALLET_RBF; OutputType g_address_type = OUTPUT_TYPE_NONE; OutputType g_change_type = OUTPUT_TYPE_NONE; +bool g_wallet_allow_fallback_fee = true; //<! will be defined via chainparams -const char * DEFAULT_WALLET_DAT = "wallet.dat"; const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; /** @@ -532,7 +531,7 @@ void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> ran int nMinOrderPos = std::numeric_limits<int>::max(); const CWalletTx* copyFrom = nullptr; for (TxSpends::iterator it = range.first; it != range.second; ++it) { - const CWalletTx* wtx = &mapWallet[it->second]; + const CWalletTx* wtx = &mapWallet.at(it->second); if (wtx->nOrderPos < nMinOrderPos) { nMinOrderPos = wtx->nOrderPos;; copyFrom = wtx; @@ -545,7 +544,7 @@ void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> ran for (TxSpends::iterator it = range.first; it != range.second; ++it) { const uint256& hash = it->second; - CWalletTx* copyTo = &mapWallet[hash]; + CWalletTx* copyTo = &mapWallet.at(hash); if (copyFrom == copyTo) continue; assert(copyFrom && "Oldest wallet transaction in range assumed to have been found."); if (!copyFrom->IsEquivalentTo(*copyTo)) continue; @@ -976,7 +975,8 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) if (!strCmd.empty()) { boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); - boost::thread t(runCommand, strCmd); // thread runs free + std::thread t(runCommand, strCmd); + t.detach(); // thread runs free } return true; @@ -1147,11 +1147,9 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) LOCK2(cs_main, cs_wallet); int conflictconfirms = 0; - if (mapBlockIndex.count(hashBlock)) { - CBlockIndex* pindex = mapBlockIndex[hashBlock]; - if (chainActive.Contains(pindex)) { - conflictconfirms = -(chainActive.Height() - pindex->nHeight + 1); - } + CBlockIndex* pindex = LookupBlockIndex(hashBlock); + if (pindex && chainActive.Contains(pindex)) { + conflictconfirms = -(chainActive.Height() - pindex->nHeight + 1); } // If number of conflict confirms cannot be determined, this means // that the block is still unknown or not yet part of the main chain, @@ -1668,20 +1666,15 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex); dProgressTip = GuessVerificationProgress(chainParams.TxData(), tip); } + double gvp = dProgressStart; while (pindex && !fAbortRescan) { if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0) { - double gvp = 0; - { - LOCK(cs_main); - gvp = GuessVerificationProgress(chainParams.TxData(), pindex); - } ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((gvp - dProgressStart) / (dProgressTip - dProgressStart) * 100)))); } if (GetTime() >= nNow + 60) { nNow = GetTime(); - LOCK(cs_main); - LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex)); + LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, gvp); } CBlock block; @@ -1705,6 +1698,7 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock { LOCK(cs_main); pindex = chainActive.Next(pindex); + gvp = GuessVerificationProgress(chainParams.TxData(), pindex); if (tip != chainActive.Tip()) { tip = chainActive.Tip(); // in case the tip has changed, update progress max @@ -1713,7 +1707,7 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock } } if (pindex && fAbortRescan) { - LogPrintf("Rescan aborted at block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex)); + LogPrintf("Rescan aborted at block %d. Progress=%f\n", pindex->nHeight, gvp); } ShowProgress(_("Rescanning..."), 100); // hide progress dialog in GUI } @@ -2635,13 +2629,13 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC LOCK2(cs_main, cs_wallet); CReserveKey reservekey(this); - CWalletTx wtx; - if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosInOut, strFailReason, coinControl, false)) { + CTransactionRef tx_new; + if (!CreateTransaction(vecSend, tx_new, reservekey, nFeeRet, nChangePosInOut, strFailReason, coinControl, false)) { return false; } if (nChangePosInOut != -1) { - tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.tx->vout[nChangePosInOut]); + tx.vout.insert(tx.vout.begin() + nChangePosInOut, tx_new->vout[nChangePosInOut]); // We don't have the normal Create/Commit cycle, and don't want to risk // reusing change, so just remove the key from the keypool here. reservekey.KeepKey(); @@ -2650,11 +2644,11 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC // Copy output sizes from new transaction; they may have had the fee // subtracted from them. for (unsigned int idx = 0; idx < tx.vout.size(); idx++) { - tx.vout[idx].nValue = wtx.tx->vout[idx].nValue; + tx.vout[idx].nValue = tx_new->vout[idx].nValue; } // Add new txins while keeping original txin scriptSig/order. - for (const CTxIn& txin : wtx.tx->vin) { + for (const CTxIn& txin : tx_new->vin) { if (!coinControl.IsSelected(txin.prevout)) { tx.vin.push_back(txin); @@ -2695,7 +2689,7 @@ OutputType CWallet::TransactionChangeType(OutputType change_type, const std::vec return g_address_type; } -bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, +bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign) { CAmount nValue = 0; @@ -2719,8 +2713,6 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT return false; } - wtxNew.fTimeReceivedIsTxTime = true; - wtxNew.BindWallet(this); CMutableTransaction txNew; // Discourage fee sniping. @@ -2808,7 +2800,6 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT nChangePosInOut = nChangePosRequest; txNew.vin.clear(); txNew.vout.clear(); - wtxNew.fFromMe = true; bool fFirst = true; CAmount nValueToSelect = nValue; @@ -2922,6 +2913,11 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT } nFeeNeeded = GetMinimumFee(nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc); + if (feeCalc.reason == FeeReason::FALLBACK && !g_wallet_allow_fallback_fee) { + // eventually allow a fallback fee + strFailReason = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee."); + return false; + } // If we made it here and we aren't even able to meet the relay fee on the next pass, give up // because we must be at the maximum allowed fee. @@ -3018,11 +3014,11 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT } } - // Embed the constructed transaction data in wtxNew. - wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); + // Return the constructed transaction data. + tx = MakeTransactionRef(std::move(txNew)); // Limit size - if (GetTransactionWeight(*wtxNew.tx) >= MAX_STANDARD_TX_WEIGHT) + if (GetTransactionWeight(*tx) >= MAX_STANDARD_TX_WEIGHT) { strFailReason = _("Transaction too large"); return false; @@ -3032,7 +3028,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { // Lastly, ensure this tx will pass the mempool's chain limits LockPoints lp; - CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, false, 0, lp); + CTxMemPoolEntry entry(tx, 0, 0, 0, false, 0, lp); CTxMemPool::setEntries setAncestors; size_t nLimitAncestors = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); size_t nLimitAncestorSize = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000; @@ -3059,10 +3055,18 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT /** * Call after CreateTransaction unless you want to abort */ -bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, CValidationState& state) +bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, std::string fromAccount, CReserveKey& reservekey, CConnman* connman, CValidationState& state) { { LOCK2(cs_main, cs_wallet); + + CWalletTx wtxNew(this, std::move(tx)); + wtxNew.mapValue = std::move(mapValue); + wtxNew.vOrderForm = std::move(orderForm); + wtxNew.strFromAccount = std::move(fromAccount); + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.fFromMe = true; + LogPrintf("CommitTransaction:\n%s", wtxNew.tx->ToString()); { // Take key pair from key pool so it won't be used again @@ -3075,7 +3079,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon // Notify that old coins are spent for (const CTxIn& txin : wtxNew.tx->vin) { - CWalletTx &coin = mapWallet[txin.prevout.hash]; + CWalletTx &coin = mapWallet.at(txin.prevout.hash); coin.BindWallet(this); NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED); } @@ -3086,13 +3090,13 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon // Get the inserted-CWalletTx from mapWallet so that the // fInMempool flag is cached properly - CWalletTx& wtx = mapWallet[wtxNew.GetHash()]; + CWalletTx& wtx = mapWallet.at(wtxNew.GetHash()); if (fBroadcastTransactions) { // Broadcast if (!wtx.AcceptToMemoryPool(maxTxFee, state)) { - LogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", state.GetRejectReason()); + LogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", FormatStateMessage(state)); // TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure. } else { wtx.RelayWalletTransaction(connman); @@ -3542,7 +3546,7 @@ std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings() CTxDestination address; if(!IsMine(txin)) /* If this input isn't mine, ignore it */ continue; - if(!ExtractDestination(mapWallet[txin.prevout.hash].tx->vout[txin.prevout.n].scriptPubKey, address)) + if(!ExtractDestination(mapWallet.at(txin.prevout.hash).tx->vout[txin.prevout.n].scriptPubKey, address)) continue; grouping.insert(address); any_mine = true; @@ -3766,10 +3770,10 @@ void CWallet::GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) c for (const auto& entry : mapWallet) { // iterate over all wallet transactions... const CWalletTx &wtx = entry.second; - BlockMap::const_iterator blit = mapBlockIndex.find(wtx.hashBlock); - if (blit != mapBlockIndex.end() && chainActive.Contains(blit->second)) { + CBlockIndex* pindex = LookupBlockIndex(wtx.hashBlock); + if (pindex && chainActive.Contains(pindex)) { // ... which are already in a block - int nHeight = blit->second->nHeight; + int nHeight = pindex->nHeight; for (const CTxOut &txout : wtx.tx->vout) { // iterate over all their outputs CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey); @@ -3777,7 +3781,7 @@ void CWallet::GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) c // ... and all their affected keys std::map<CKeyID, CBlockIndex*>::iterator rit = mapKeyFirstBlock.find(keyid); if (rit != mapKeyFirstBlock.end() && nHeight < rit->second->nHeight) - rit->second = blit->second; + rit->second = pindex; } vAffected.clear(); } @@ -3814,7 +3818,7 @@ unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const { unsigned int nTimeSmart = wtx.nTimeReceived; if (!wtx.hashUnset()) { - if (mapBlockIndex.count(wtx.hashBlock)) { + if (const CBlockIndex* pindex = LookupBlockIndex(wtx.hashBlock)) { int64_t latestNow = wtx.nTimeReceived; int64_t latestEntry = 0; @@ -3845,7 +3849,7 @@ unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const } } - int64_t blocktime = mapBlockIndex[wtx.hashBlock]->GetBlockTime(); + int64_t blocktime = pindex->GetBlockTime(); nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow)); } else { LogPrintf("%s: found %s in block %s not in index\n", __func__, wtx.GetHash().ToString(), wtx.hashBlock.ToString()); @@ -3906,16 +3910,17 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const return values; } -CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) +CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& path) { + const std::string& walletFile = name; + // needed to restore wallet transaction meta data after -zapwallettxes std::vector<CWalletTx> vWtx; if (gArgs.GetBoolArg("-zapwallettxes", false)) { uiInterface.InitMessage(_("Zapping all transactions from wallet...")); - std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, walletFile)); - std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(std::move(dbw)); + std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(name, CWalletDBWrapper::Create(path)); DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx); if (nZapWalletRet != DB_LOAD_OK) { InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); @@ -3927,8 +3932,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) int64_t nStart = GetTimeMillis(); bool fFirstRun = true; - std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, walletFile)); - CWallet *walletInstance = new CWallet(std::move(dbw)); + CWallet *walletInstance = new CWallet(name, CWalletDBWrapper::Create(path)); DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun); if (nLoadWalletRet != DB_LOAD_OK) { @@ -4015,6 +4019,8 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) // Try to top up keypool. No-op if the wallet is locked. walletInstance->TopUpKeyPool(); + LOCK(cs_main); + CBlockIndex *pindexRescan = chainActive.Genesis(); if (!gArgs.GetBoolArg("-rescan", false)) { @@ -4158,10 +4164,7 @@ int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const AssertLockHeld(cs_main); // Find the block it claims to be in - BlockMap::iterator mi = mapBlockIndex.find(hashBlock); - if (mi == mapBlockIndex.end()) - return 0; - CBlockIndex* pindex = (*mi).second; + CBlockIndex* pindex = LookupBlockIndex(hashBlock); if (!pindex || !chainActive.Contains(pindex)) return 0; @@ -4179,11 +4182,6 @@ int CMerkleTx::GetBlocksToMaturity() const bool CWalletTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) { - // Quick check to avoid re-setting fInMempool to false - if (mempool.exists(tx->GetHash())) { - return false; - } - // We must set fInMempool here - while it will be re-set to true by the // entered-mempool callback, if we did not there would be a race where a // user could call sendmoney in a loop and hit spurious out of funds errors @@ -4191,7 +4189,7 @@ bool CWalletTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& // unavailable as we're not yet aware its in mempool. bool ret = ::AcceptToMemoryPool(mempool, state, tx, nullptr /* pfMissingInputs */, nullptr /* plTxnReplaced */, false /* bypass_limits */, nAbsurdFee); - fInMempool = ret; + fInMempool |= ret; return ret; } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index fefe415bb1..61314f36d2 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -15,6 +15,7 @@ #include <validationinterface.h> #include <script/ismine.h> #include <script/sign.h> +#include <util.h> #include <wallet/crypter.h> #include <wallet/walletdb.h> #include <wallet/rpcwallet.h> @@ -39,6 +40,7 @@ extern CFeeRate payTxFee; extern unsigned int nTxConfirmTarget; extern bool bSpendZeroConfChange; extern bool fWalletRbf; +extern bool g_wallet_allow_fallback_fee; static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000; //! -paytxfee default @@ -66,8 +68,6 @@ static const bool DEFAULT_WALLET_RBF = false; static const bool DEFAULT_WALLETBROADCAST = true; static const bool DEFAULT_DISABLE_WALLET = false; -extern const char * DEFAULT_WALLET_DAT; - static const int64_t TIMESTAMP_MIN = 0; class CBlockIndex; @@ -348,11 +348,6 @@ public: mutable CAmount nAvailableWatchCreditCached; mutable CAmount nChangeCached; - CWalletTx() - { - Init(nullptr); - } - CWalletTx(const CWallet* pwalletIn, CTransactionRef arg) : CMerkleTx(std::move(arg)) { Init(pwalletIn); @@ -390,42 +385,36 @@ public: nOrderPos = -1; } - ADD_SERIALIZE_METHODS; - - template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action) { - if (ser_action.ForRead()) - Init(nullptr); + template<typename Stream> + void Serialize(Stream& s) const + { char fSpent = false; + mapValue_t mapValueCopy = mapValue; - if (!ser_action.ForRead()) - { - mapValue["fromaccount"] = strFromAccount; - - WriteOrderPos(nOrderPos, mapValue); - - if (nTimeSmart) - mapValue["timesmart"] = strprintf("%u", nTimeSmart); + mapValueCopy["fromaccount"] = strFromAccount; + WriteOrderPos(nOrderPos, mapValueCopy); + if (nTimeSmart) { + mapValueCopy["timesmart"] = strprintf("%u", nTimeSmart); } - READWRITE(*static_cast<CMerkleTx*>(this)); + s << *static_cast<const CMerkleTx*>(this); std::vector<CMerkleTx> vUnused; //!< Used to be vtxPrev - READWRITE(vUnused); - READWRITE(mapValue); - READWRITE(vOrderForm); - READWRITE(fTimeReceivedIsTxTime); - READWRITE(nTimeReceived); - READWRITE(fFromMe); - READWRITE(fSpent); - - if (ser_action.ForRead()) - { - strFromAccount = mapValue["fromaccount"]; + s << vUnused << mapValueCopy << vOrderForm << fTimeReceivedIsTxTime << nTimeReceived << fFromMe << fSpent; + } - ReadOrderPos(nOrderPos, mapValue); + template<typename Stream> + void Unserialize(Stream& s) + { + Init(nullptr); + char fSpent; - nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(mapValue["timesmart"]) : 0; - } + s >> *static_cast<CMerkleTx*>(this); + std::vector<CMerkleTx> vUnused; //!< Used to be vtxPrev + s >> vUnused >> mapValue >> vOrderForm >> fTimeReceivedIsTxTime >> nTimeReceived >> fFromMe >> fSpent; + + strFromAccount = std::move(mapValue["fromaccount"]); + ReadOrderPos(nOrderPos, mapValue); + nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(mapValue["timesmart"]) : 0; mapValue.erase("fromaccount"); mapValue.erase("spent"); @@ -608,48 +597,49 @@ public: nEntryNo = 0; } - ADD_SERIALIZE_METHODS; - - template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action) { + template <typename Stream> + void Serialize(Stream& s) const { int nVersion = s.GetVersion(); - if (!(s.GetType() & SER_GETHASH)) - READWRITE(nVersion); + if (!(s.GetType() & SER_GETHASH)) { + s << nVersion; + } //! Note: strAccount is serialized as part of the key, not here. - READWRITE(nCreditDebit); - READWRITE(nTime); - READWRITE(LIMITED_STRING(strOtherAccount, 65536)); - - if (!ser_action.ForRead()) - { - WriteOrderPos(nOrderPos, mapValue); - - if (!(mapValue.empty() && _ssExtra.empty())) - { - CDataStream ss(s.GetType(), s.GetVersion()); - ss.insert(ss.begin(), '\0'); - ss << mapValue; - ss.insert(ss.end(), _ssExtra.begin(), _ssExtra.end()); - strComment.append(ss.str()); - } + s << nCreditDebit << nTime << strOtherAccount; + + mapValue_t mapValueCopy = mapValue; + WriteOrderPos(nOrderPos, mapValueCopy); + + std::string strCommentCopy = strComment; + if (!mapValueCopy.empty() || !_ssExtra.empty()) { + CDataStream ss(s.GetType(), s.GetVersion()); + ss.insert(ss.begin(), '\0'); + ss << mapValueCopy; + ss.insert(ss.end(), _ssExtra.begin(), _ssExtra.end()); + strCommentCopy.append(ss.str()); } + s << strCommentCopy; + } - READWRITE(LIMITED_STRING(strComment, 65536)); + template <typename Stream> + void Unserialize(Stream& s) { + int nVersion = s.GetVersion(); + if (!(s.GetType() & SER_GETHASH)) { + s >> nVersion; + } + //! Note: strAccount is serialized as part of the key, not here. + s >> nCreditDebit >> nTime >> LIMITED_STRING(strOtherAccount, 65536) >> LIMITED_STRING(strComment, 65536); size_t nSepPos = strComment.find("\0", 0, 1); - if (ser_action.ForRead()) - { - mapValue.clear(); - if (std::string::npos != nSepPos) - { - CDataStream ss(std::vector<char>(strComment.begin() + nSepPos + 1, strComment.end()), s.GetType(), s.GetVersion()); - ss >> mapValue; - _ssExtra = std::vector<char>(ss.begin(), ss.end()); - } - ReadOrderPos(nOrderPos, mapValue); + mapValue.clear(); + if (std::string::npos != nSepPos) { + CDataStream ss(std::vector<char>(strComment.begin() + nSepPos + 1, strComment.end()), s.GetType(), s.GetVersion()); + ss >> mapValue; + _ssExtra = std::vector<char>(ss.begin(), ss.end()); } - if (std::string::npos != nSepPos) + ReadOrderPos(nOrderPos, mapValue); + if (std::string::npos != nSepPos) { strComment.erase(nSepPos); + } mapValue.erase("n"); } @@ -736,6 +726,14 @@ private: */ bool AddWatchOnly(const CScript& dest) override; + /** + * Wallet filename from wallet=<path> command line or config option. + * Used in debug logs and to send RPCs to the right wallet instance when + * more than one wallet is loaded. + */ + std::string m_name; + + /** Internal database handle. */ std::unique_ptr<CWalletDBWrapper> dbw; /** @@ -767,14 +765,7 @@ public: /** Get a name for this wallet for logging/debugging purposes. */ - std::string GetName() const - { - if (dbw) { - return dbw->GetName(); - } else { - return "dummy"; - } - } + const std::string& GetName() const { return m_name; } void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool); @@ -788,14 +779,8 @@ public: MasterKeyMap mapMasterKeys; unsigned int nMasterKeyMaxID; - // Create wallet with dummy database handle - CWallet(): dbw(new CWalletDBWrapper()) - { - SetNull(); - } - - // Create wallet with passed-in database handle - explicit CWallet(std::unique_ptr<CWalletDBWrapper> dbw_in) : dbw(std::move(dbw_in)) + /** Construct wallet with specified name and database implementation. */ + CWallet(std::string name, std::unique_ptr<CWalletDBWrapper> dbw) : m_name(std::move(name)), dbw(std::move(dbw)) { SetNull(); } @@ -979,9 +964,9 @@ public: * selected by SelectCoins(); Also create the change output, when needed * @note passing nChangePosInOut as -1 will result in setting a random position */ - bool CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, + bool CreateTransaction(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign = true); - bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, CValidationState& state); + bool CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, std::string fromAccount, CReserveKey& reservekey, CConnman* connman, CValidationState& state); void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries); bool AddAccountingEntry(const CAccountingEntry&); @@ -1115,7 +1100,7 @@ public: bool MarkReplaced(const uint256& originalHash, const uint256& newHash); /* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */ - static CWallet* CreateWalletFromFile(const std::string walletFile); + static CWallet* CreateWalletFromFile(const std::string& name, const fs::path& path); /** * Wallet post-init setup diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index dd6835a06f..7f5f3b84b2 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -5,10 +5,10 @@ #include <wallet/walletdb.h> -#include <base58.h> #include <consensus/tx_verify.h> #include <consensus/validation.h> #include <fs.h> +#include <key_io.h> #include <protocol.h> #include <serialize.h> #include <sync.h> @@ -265,7 +265,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, { uint256 hash; ssKey >> hash; - CWalletTx wtx; + CWalletTx wtx(nullptr /* pwallet */, MakeTransactionRef()); ssValue >> wtx; CValidationState state; if (!(CheckTransaction(*wtx.tx, state) && (wtx.GetHash() == hash) && state.IsValid())) @@ -603,7 +603,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) pwallet->UpdateTimeFirstKey(1); for (uint256 hash : wss.vWalletUpgrade) - WriteTx(pwallet->mapWallet[hash]); + WriteTx(pwallet->mapWallet.at(hash)); // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000)) @@ -664,7 +664,7 @@ DBErrors CWalletDB::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWal uint256 hash; ssKey >> hash; - CWalletTx wtx; + CWalletTx wtx(nullptr /* pwallet */, MakeTransactionRef()); ssValue >> wtx; vTxHash.push_back(hash); @@ -771,16 +771,16 @@ void MaybeCompactWalletDB() // // Try to (very carefully!) recover wallet file if there is a problem. // -bool CWalletDB::Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename) +bool CWalletDB::Recover(const fs::path& wallet_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename) { - return CDB::Recover(filename, callbackDataIn, recoverKVcallback, out_backup_filename); + return CDB::Recover(wallet_path, callbackDataIn, recoverKVcallback, out_backup_filename); } -bool CWalletDB::Recover(const std::string& filename, std::string& out_backup_filename) +bool CWalletDB::Recover(const fs::path& wallet_path, std::string& out_backup_filename) { // recover without a key filter callback // results in recovering all record types - return CWalletDB::Recover(filename, nullptr, nullptr, out_backup_filename); + return CWalletDB::Recover(wallet_path, nullptr, nullptr, out_backup_filename); } bool CWalletDB::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue) @@ -806,14 +806,14 @@ bool CWalletDB::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDa return true; } -bool CWalletDB::VerifyEnvironment(const std::string& walletFile, const fs::path& walletDir, std::string& errorStr) +bool CWalletDB::VerifyEnvironment(const fs::path& wallet_path, std::string& errorStr) { - return CDB::VerifyEnvironment(walletFile, walletDir, errorStr); + return CDB::VerifyEnvironment(wallet_path, errorStr); } -bool CWalletDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& walletDir, std::string& warningStr, std::string& errorStr) +bool CWalletDB::VerifyDatabaseFile(const fs::path& wallet_path, std::string& warningStr, std::string& errorStr) { - return CDB::VerifyDatabaseFile(walletFile, walletDir, warningStr, errorStr, CWalletDB::Recover); + return CDB::VerifyDatabaseFile(wallet_path, warningStr, errorStr, CWalletDB::Recover); } bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value) diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 3691cfcb57..7d754c7284 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -218,17 +218,17 @@ public: DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx); DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut); /* Try to (very carefully!) recover wallet database (with a possible key type filter) */ - static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename); + static bool Recover(const fs::path& wallet_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename); /* Recover convenience-function to bypass the key filter callback, called when verify fails, recovers everything */ - static bool Recover(const std::string& filename, std::string& out_backup_filename); + static bool Recover(const fs::path& wallet_path, std::string& out_backup_filename); /* Recover filter (used as callback), will only let keys (cryptographical keys) as KV/key-type pass through */ static bool RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue); /* Function to determine if a certain KV/key-type is a key (cryptographical key) type */ static bool IsKeyType(const std::string& strType); /* verifies the database environment */ - static bool VerifyEnvironment(const std::string& walletFile, const fs::path& walletDir, std::string& errorStr); + static bool VerifyEnvironment(const fs::path& wallet_path, std::string& errorStr); /* verifies the database file */ - static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& walletDir, std::string& warningStr, std::string& errorStr); + static bool VerifyDatabaseFile(const fs::path& wallet_path, std::string& warningStr, std::string& errorStr); //! write the hdchain model (external chain child index counter) bool WriteHDChain(const CHDChain& chain); diff --git a/test/functional/combine_logs.py b/test/functional/combine_logs.py index 3ca74ea35e..d1bf9206b2 100755 --- a/test/functional/combine_logs.py +++ b/test/functional/combine_logs.py @@ -13,7 +13,7 @@ import re import sys # Matches on the date format at the start of the log event -TIMESTAMP_PATTERN = re.compile(r"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{6}") +TIMESTAMP_PATTERN = re.compile(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}Z") LogEvent = namedtuple('LogEvent', ['timestamp', 'source', 'event']) diff --git a/test/functional/feature_bip68_sequence.py b/test/functional/feature_bip68_sequence.py index 94b13653b9..e5db9e18c7 100755 --- a/test/functional/feature_bip68_sequence.py +++ b/test/functional/feature_bip68_sequence.py @@ -14,7 +14,7 @@ SEQUENCE_LOCKTIME_GRANULARITY = 9 # this is a bit-shift SEQUENCE_LOCKTIME_MASK = 0x0000ffff # RPC error for non-BIP68 final transactions -NOT_FINAL_ERROR = "64: non-BIP68-final" +NOT_FINAL_ERROR = "non-BIP68-final (code 64)" class BIP68Test(BitcoinTestFramework): def set_test_params(self): @@ -70,7 +70,7 @@ class BIP68Test(BitcoinTestFramework): tx1.vin = [CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), nSequence=sequence_value)] tx1.vout = [CTxOut(value, CScript([b'a']))] - tx1_signed = self.nodes[0].signrawtransaction(ToHex(tx1))["hex"] + tx1_signed = self.nodes[0].signrawtransactionwithwallet(ToHex(tx1))["hex"] tx1_id = self.nodes[0].sendrawtransaction(tx1_signed) tx1_id = int(tx1_id, 16) @@ -176,7 +176,7 @@ class BIP68Test(BitcoinTestFramework): # Overestimate the size of the tx - signatures should be less than 120 bytes, and leave 50 for the output tx_size = len(ToHex(tx))//2 + 120*num_inputs + 50 tx.vout.append(CTxOut(int(value-self.relayfee*tx_size*COIN/1000), CScript([b'a']))) - rawtx = self.nodes[0].signrawtransaction(ToHex(tx))["hex"] + rawtx = self.nodes[0].signrawtransactionwithwallet(ToHex(tx))["hex"] if (using_sequence_locks and not should_pass): # This transaction should be rejected @@ -205,7 +205,7 @@ class BIP68Test(BitcoinTestFramework): tx2.nVersion = 2 tx2.vin = [CTxIn(COutPoint(tx1.sha256, 0), nSequence=0)] tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee*COIN), CScript([b'a']))] - tx2_raw = self.nodes[0].signrawtransaction(ToHex(tx2))["hex"] + tx2_raw = self.nodes[0].signrawtransactionwithwallet(ToHex(tx2))["hex"] tx2 = FromHex(tx2, tx2_raw) tx2.rehash() @@ -278,7 +278,7 @@ class BIP68Test(BitcoinTestFramework): utxos = self.nodes[0].listunspent() tx5.vin.append(CTxIn(COutPoint(int(utxos[0]["txid"], 16), utxos[0]["vout"]), nSequence=1)) tx5.vout[0].nValue += int(utxos[0]["amount"]*COIN) - raw_tx5 = self.nodes[0].signrawtransaction(ToHex(tx5))["hex"] + raw_tx5 = self.nodes[0].signrawtransactionwithwallet(ToHex(tx5))["hex"] assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, raw_tx5) @@ -338,7 +338,7 @@ class BIP68Test(BitcoinTestFramework): tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee*COIN), CScript([b'a']))] # sign tx2 - tx2_raw = self.nodes[0].signrawtransaction(ToHex(tx2))["hex"] + tx2_raw = self.nodes[0].signrawtransactionwithwallet(ToHex(tx2))["hex"] tx2 = FromHex(tx2, tx2_raw) tx2.rehash() @@ -388,7 +388,7 @@ class BIP68Test(BitcoinTestFramework): rawtxfund = self.nodes[1].fundrawtransaction(rawtx)['hex'] tx = FromHex(CTransaction(), rawtxfund) tx.nVersion = 2 - tx_signed = self.nodes[1].signrawtransaction(ToHex(tx))["hex"] + tx_signed = self.nodes[1].signrawtransactionwithwallet(ToHex(tx))["hex"] self.nodes[1].sendrawtransaction(tx_signed) if __name__ == '__main__': diff --git a/test/functional/feature_bip9_softforks.py b/test/functional/feature_bip9_softforks.py index ae92e9f07c..71d3d04002 100755 --- a/test/functional/feature_bip9_softforks.py +++ b/test/functional/feature_bip9_softforks.py @@ -51,7 +51,7 @@ class BIP9SoftForksTest(ComparisonTestFramework): return tx def sign_transaction(self, node, tx): - signresult = node.signrawtransaction(bytes_to_hex_str(tx.serialize())) + signresult = node.signrawtransactionwithwallet(bytes_to_hex_str(tx.serialize())) tx = CTransaction() f = BytesIO(hex_str_to_bytes(signresult['hex'])) tx.deserialize(f) diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py index f62ae31654..e9a8945e76 100755 --- a/test/functional/feature_cltv.py +++ b/test/functional/feature_cltv.py @@ -41,7 +41,7 @@ def cltv_validate(node, tx, height): tx.nLockTime = height # Need to re-sign, since nSequence and nLockTime changed - signed_result = node.signrawtransaction(ToHex(tx)) + signed_result = node.signrawtransactionwithwallet(ToHex(tx)) new_tx = CTransaction() new_tx.deserialize(BytesIO(hex_str_to_bytes(signed_result['hex']))) @@ -54,7 +54,7 @@ def create_transaction(node, coinbase, to_address, amount): inputs = [{ "txid" : from_txid, "vout" : 0}] outputs = { to_address : amount } rawtx = node.createrawtransaction(inputs, outputs) - signresult = node.signrawtransaction(rawtx) + signresult = node.signrawtransactionwithwallet(rawtx) tx = CTransaction() tx.deserialize(BytesIO(hex_str_to_bytes(signresult['hex']))) return tx diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py index 61abba8082..c6cec0596b 100755 --- a/test/functional/feature_config_args.py +++ b/test/functional/feature_config_args.py @@ -37,13 +37,13 @@ class ConfArgsTest(BitcoinTestFramework): os.mkdir(new_data_dir) self.start_node(0, ['-conf='+conf_file, '-wallet=w1']) self.stop_node(0) - assert os.path.isfile(os.path.join(new_data_dir, 'regtest', 'wallets', 'w1')) + assert os.path.exists(os.path.join(new_data_dir, 'regtest', 'wallets', 'w1')) # Ensure command line argument overrides datadir in conf os.mkdir(new_data_dir_2) self.nodes[0].datadir = new_data_dir_2 self.start_node(0, ['-datadir='+new_data_dir_2, '-conf='+conf_file, '-wallet=w2']) - assert os.path.isfile(os.path.join(new_data_dir_2, 'regtest', 'wallets', 'w2')) + assert os.path.exists(os.path.join(new_data_dir_2, 'regtest', 'wallets', 'w2')) if __name__ == '__main__': ConfArgsTest().main() diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py index 82aa0ff891..8b5e5681e4 100755 --- a/test/functional/feature_csv_activation.py +++ b/test/functional/feature_csv_activation.py @@ -118,7 +118,7 @@ class BIP68_112_113Test(ComparisonTestFramework): def sign_transaction(self, node, unsignedtx): rawtx = ToHex(unsignedtx) - signresult = node.signrawtransaction(rawtx) + signresult = node.signrawtransactionwithwallet(rawtx) tx = CTransaction() f = BytesIO(hex_str_to_bytes(signresult['hex'])) tx.deserialize(f) diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py index 24b9765b4e..cef257cf9b 100755 --- a/test/functional/feature_dbcrash.py +++ b/test/functional/feature_dbcrash.py @@ -206,7 +206,7 @@ class ChainstateWriteCrashTest(BitcoinTestFramework): tx.vout.append(CTxOut(output_amount, hex_str_to_bytes(utxo['scriptPubKey']))) # Sign and send the transaction to get into the mempool - tx_signed_hex = node.signrawtransaction(ToHex(tx))['hex'] + tx_signed_hex = node.signrawtransactionwithwallet(ToHex(tx))['hex'] node.sendrawtransaction(tx_signed_hex) num_transactions += 1 diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py index 3414571678..02dcc3e55d 100755 --- a/test/functional/feature_dersig.py +++ b/test/functional/feature_dersig.py @@ -42,7 +42,7 @@ def create_transaction(node, coinbase, to_address, amount): inputs = [{ "txid" : from_txid, "vout" : 0}] outputs = { to_address : amount } rawtx = node.createrawtransaction(inputs, outputs) - signresult = node.signrawtransaction(rawtx) + signresult = node.signrawtransactionwithwallet(rawtx) tx = CTransaction() tx.deserialize(BytesIO(hex_str_to_bytes(signresult['hex']))) return tx diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py index e1263414bd..8a56d3eefa 100755 --- a/test/functional/feature_fee_estimation.py +++ b/test/functional/feature_fee_estimation.py @@ -91,7 +91,7 @@ def split_inputs(from_node, txins, txouts, initial_split=False): # If this is the initial split we actually need to sign the transaction # Otherwise we just need to insert the proper ScriptSig if (initial_split): - completetx = from_node.signrawtransaction(ToHex(tx))["hex"] + completetx = from_node.signrawtransactionwithwallet(ToHex(tx))["hex"] else: tx.vin[0].scriptSig = SCRIPT_SIG[prevtxout["vout"]] completetx = ToHex(tx) diff --git a/test/functional/feature_nulldummy.py b/test/functional/feature_nulldummy.py index e4f413cc2a..7db6a03b45 100755 --- a/test/functional/feature_nulldummy.py +++ b/test/functional/feature_nulldummy.py @@ -21,7 +21,7 @@ from test_framework.script import CScript from io import BytesIO import time -NULLDUMMY_ERROR = "64: non-mandatory-script-verify-flag (Dummy CHECKMULTISIG argument must be zero)" +NULLDUMMY_ERROR = "non-mandatory-script-verify-flag (Dummy CHECKMULTISIG argument must be zero) (code 64)" def trueDummy(tx): scriptSig = CScript(tx.vin[0].scriptSig) @@ -102,7 +102,7 @@ class NULLDUMMYTest(BitcoinTestFramework): inputs = [{ "txid" : txid, "vout" : 0}] outputs = { to_address : amount } rawtx = node.createrawtransaction(inputs, outputs) - signresult = node.signrawtransaction(rawtx) + signresult = node.signrawtransactionwithwallet(rawtx) tx = CTransaction() f = BytesIO(hex_str_to_bytes(signresult['hex'])) tx.deserialize(f) diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py index 6b7ab0f43e..d6ab5ecc37 100755 --- a/test/functional/feature_rbf.py +++ b/test/functional/feature_rbf.py @@ -42,7 +42,7 @@ def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])): tx2.vout = [CTxOut(amount, scriptPubKey)] tx2.rehash() - signed_tx = node.signrawtransaction(txToHex(tx2)) + signed_tx = node.signrawtransactionwithwallet(txToHex(tx2)) txid = node.sendrawtransaction(signed_tx['hex'], True) diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py index 7db05077c9..fa1732c4c5 100755 --- a/test/functional/feature_segwit.py +++ b/test/functional/feature_segwit.py @@ -96,7 +96,7 @@ class SegWitTest(BitcoinTestFramework): wit_ids = [] # wit_ids[NODE][VER] is an array of txids that spend to a witness version VER pkscript to an address for NODE via bare witness for i in range(3): newaddress = self.nodes[i].getnewaddress() - self.pubkey.append(self.nodes[i].validateaddress(newaddress)["pubkey"]) + self.pubkey.append(self.nodes[i].getaddressinfo(newaddress)["pubkey"]) multiscript = CScript([OP_1, hex_str_to_bytes(self.pubkey[-1]), OP_1, OP_CHECKMULTISIG]) p2sh_addr = self.nodes[i].addwitnessaddress(newaddress) bip173_addr = self.nodes[i].addwitnessaddress(newaddress, False) @@ -221,7 +221,7 @@ class SegWitTest(BitcoinTestFramework): tx = CTransaction() tx.vin.append(CTxIn(COutPoint(int(txid1, 16), 0), b'')) tx.vout.append(CTxOut(int(49.99*COIN), CScript([OP_TRUE]))) - tx2_hex = self.nodes[0].signrawtransaction(ToHex(tx))['hex'] + tx2_hex = self.nodes[0].signrawtransactionwithwallet(ToHex(tx))['hex'] txid2 = self.nodes[0].sendrawtransaction(tx2_hex) tx = FromHex(CTransaction(), tx2_hex) assert(not tx.wit.is_null()) @@ -274,8 +274,8 @@ class SegWitTest(BitcoinTestFramework): uncompressed_spendable_address = ["mvozP4UwyGD2mGZU4D2eMvMLPB9WkMmMQu"] self.nodes[0].importprivkey("cNC8eQ5dg3mFAVePDX4ddmPYpPbw41r9bm2jd1nLJT77e6RrzTRR") compressed_spendable_address = ["mmWQubrDomqpgSYekvsU7HWEVjLFHAakLe"] - assert ((self.nodes[0].validateaddress(uncompressed_spendable_address[0])['iscompressed'] == False)) - assert ((self.nodes[0].validateaddress(compressed_spendable_address[0])['iscompressed'] == True)) + assert ((self.nodes[0].getaddressinfo(uncompressed_spendable_address[0])['iscompressed'] == False)) + assert ((self.nodes[0].getaddressinfo(compressed_spendable_address[0])['iscompressed'] == True)) self.nodes[0].importpubkey(pubkeys[0]) compressed_solvable_address = [key_to_p2pkh(pubkeys[0])] @@ -308,7 +308,7 @@ class SegWitTest(BitcoinTestFramework): solvable_after_importaddress.append(CScript([OP_HASH160, hash160(script), OP_EQUAL])) for i in compressed_spendable_address: - v = self.nodes[0].validateaddress(i) + v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) # bare and p2sh multisig with compressed keys should always be spendable @@ -325,7 +325,7 @@ class SegWitTest(BitcoinTestFramework): spendable_anytime.extend([p2wpkh, p2sh_p2wpkh]) for i in uncompressed_spendable_address: - v = self.nodes[0].validateaddress(i) + v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) # bare and p2sh multisig with uncompressed keys should always be spendable @@ -342,7 +342,7 @@ class SegWitTest(BitcoinTestFramework): unseen_anytime.extend([p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh]) for i in compressed_solvable_address: - v = self.nodes[0].validateaddress(i) + v = self.nodes[0].getaddressinfo(i) if (v['isscript']): # Multisig without private is not seen after addmultisigaddress, but seen after importaddress [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) @@ -355,7 +355,7 @@ class SegWitTest(BitcoinTestFramework): solvable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh]) for i in uncompressed_solvable_address: - v = self.nodes[0].validateaddress(i) + v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) # Base uncompressed multisig without private is not seen after addmultisigaddress, but seen after importaddress @@ -395,7 +395,7 @@ class SegWitTest(BitcoinTestFramework): importlist = [] for i in compressed_spendable_address + uncompressed_spendable_address + compressed_solvable_address + uncompressed_solvable_address: - v = self.nodes[0].validateaddress(i) + v = self.nodes[0].getaddressinfo(i) if (v['isscript']): bare = hex_str_to_bytes(v['hex']) importlist.append(bytes_to_hex_str(bare)) @@ -473,7 +473,7 @@ class SegWitTest(BitcoinTestFramework): premature_witaddress = [] for i in compressed_spendable_address: - v = self.nodes[0].validateaddress(i) + v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) # P2WSH and P2SH(P2WSH) multisig with compressed keys are spendable after addwitnessaddress @@ -485,7 +485,7 @@ class SegWitTest(BitcoinTestFramework): spendable_anytime.extend([p2wpkh, p2sh_p2wpkh]) for i in uncompressed_spendable_address + uncompressed_solvable_address: - v = self.nodes[0].validateaddress(i) + v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen @@ -496,7 +496,7 @@ class SegWitTest(BitcoinTestFramework): unseen_anytime.extend([p2wpkh, p2sh_p2wpkh]) for i in compressed_solvable_address: - v = self.nodes[0].validateaddress(i) + v = self.nodes[0].getaddressinfo(i) if (v['isscript']): # P2WSH multisig without private key are seen after addwitnessaddress [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) @@ -519,7 +519,7 @@ class SegWitTest(BitcoinTestFramework): assert_raises_rpc_error(-4, "Public key or redeemscript not known to wallet, or the key is uncompressed", self.nodes[0].addwitnessaddress, i) # after importaddress it should pass addwitnessaddress - v = self.nodes[0].validateaddress(compressed_solvable_address[1]) + v = self.nodes[0].getaddressinfo(compressed_solvable_address[1]) self.nodes[0].importaddress(v['hex'],"",False,True) for i in compressed_spendable_address + compressed_solvable_address + premature_witaddress: witaddress = self.nodes[0].addwitnessaddress(i) @@ -559,7 +559,7 @@ class SegWitTest(BitcoinTestFramework): self.nodes[1].importaddress(scriptPubKey, "", False) rawtxfund = self.nodes[1].fundrawtransaction(transaction)['hex'] - rawtxfund = self.nodes[1].signrawtransaction(rawtxfund)["hex"] + rawtxfund = self.nodes[1].signrawtransactionwithwallet(rawtxfund)["hex"] txid = self.nodes[1].sendrawtransaction(rawtxfund) assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid) @@ -578,7 +578,7 @@ class SegWitTest(BitcoinTestFramework): for i in script_list: tx.vout.append(CTxOut(10000000, i)) tx.rehash() - signresults = self.nodes[0].signrawtransaction(bytes_to_hex_str(tx.serialize_without_witness()))['hex'] + signresults = self.nodes[0].signrawtransactionwithwallet(bytes_to_hex_str(tx.serialize_without_witness()))['hex'] txid = self.nodes[0].sendrawtransaction(signresults, True) self.nodes[0].generate(1) sync_blocks(self.nodes) @@ -630,7 +630,7 @@ class SegWitTest(BitcoinTestFramework): tx.vin.append(CTxIn(COutPoint(int('0x'+i,0), j))) tx.vout.append(CTxOut(0, CScript())) tx.rehash() - signresults = self.nodes[0].signrawtransaction(bytes_to_hex_str(tx.serialize_without_witness()))['hex'] + signresults = self.nodes[0].signrawtransactionwithwallet(bytes_to_hex_str(tx.serialize_without_witness()))['hex'] self.nodes[0].sendrawtransaction(signresults, True) self.nodes[0].generate(1) sync_blocks(self.nodes) diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py index 9006e27cbe..8440f13a0d 100755 --- a/test/functional/interface_rest.py +++ b/test/functional/interface_rest.py @@ -46,6 +46,7 @@ class RESTTest (BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 + self.extra_args = [["-rest"]] * self.num_nodes def setup_network(self, split=False): super().setup_network() @@ -295,8 +296,10 @@ class RESTTest (BitcoinTestFramework): # check that there are our submitted transactions in the TX memory pool json_string = http_get_call(url.hostname, url.port, '/rest/mempool/contents'+self.FORMAT_SEPARATOR+'json') json_obj = json.loads(json_string) - for tx in txs: + for i, tx in enumerate(txs): assert_equal(tx in json_obj, True) + assert_equal(json_obj[tx]['spentby'], txs[i+1:i+2]) + assert_equal(json_obj[tx]['depends'], txs[i-1:i]) # now mine the transactions newblockhash = self.nodes[1].generate(1) diff --git a/test/functional/mempool_limit.py b/test/functional/mempool_limit.py index e7ce3820d2..5382fe439e 100755 --- a/test/functional/mempool_limit.py +++ b/test/functional/mempool_limit.py @@ -32,7 +32,7 @@ class MempoolLimitTest(BitcoinTestFramework): self.nodes[0].settxfee(relayfee) # specifically fund this tx with low fee txF = self.nodes[0].fundrawtransaction(tx) self.nodes[0].settxfee(0) # return to automatic fee selection - txFS = self.nodes[0].signrawtransaction(txF['hex']) + txFS = self.nodes[0].signrawtransactionwithwallet(txF['hex']) txid = self.nodes[0].sendrawtransaction(txFS['hex']) relayfee = self.nodes[0].getnetworkinfo()['relayfee'] @@ -50,5 +50,15 @@ class MempoolLimitTest(BitcoinTestFramework): assert_equal(self.nodes[0].getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000')) assert_greater_than(self.nodes[0].getmempoolinfo()['mempoolminfee'], Decimal('0.00001000')) + self.log.info('Create a mempool tx that will not pass mempoolminfee') + us0 = utxos.pop() + inputs = [{ "txid" : us0["txid"], "vout" : us0["vout"]}] + outputs = {self.nodes[0].getnewaddress() : 0.0001} + tx = self.nodes[0].createrawtransaction(inputs, outputs) + # specifically fund this tx with a fee < mempoolminfee, >= than minrelaytxfee + txF = self.nodes[0].fundrawtransaction(tx, {'feeRate': relayfee}) + txFS = self.nodes[0].signrawtransactionwithwallet(txF['hex']) + assert_raises_rpc_error(-26, "mempool min fee not met", self.nodes[0].sendrawtransaction, txFS['hex']) + if __name__ == '__main__': MempoolLimitTest().main() diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py index a3e872a8c6..8880db8002 100755 --- a/test/functional/mempool_packages.py +++ b/test/functional/mempool_packages.py @@ -25,7 +25,7 @@ class MempoolPackagesTest(BitcoinTestFramework): for i in range(num_outputs): outputs[node.getnewaddress()] = send_value rawtx = node.createrawtransaction(inputs, outputs) - signedtx = node.signrawtransaction(rawtx) + signedtx = node.signrawtransactionwithwallet(rawtx) txid = node.sendrawtransaction(signedtx['hex']) fulltx = node.getrawtransaction(txid, 1) assert(len(fulltx['vout']) == num_outputs) # make sure we didn't generate a change output @@ -47,7 +47,7 @@ class MempoolPackagesTest(BitcoinTestFramework): value = sent_value chain.append(txid) - # Check mempool has MAX_ANCESTORS transactions in it, and descendant + # Check mempool has MAX_ANCESTORS transactions in it, and descendant and ancestor # count and fees should look correct mempool = self.nodes[0].getrawmempool(True) assert_equal(len(mempool), MAX_ANCESTORS) @@ -55,6 +55,10 @@ class MempoolPackagesTest(BitcoinTestFramework): descendant_fees = 0 descendant_size = 0 + ancestor_size = sum([mempool[tx]['size'] for tx in mempool]) + ancestor_count = MAX_ANCESTORS + ancestor_fees = sum([mempool[tx]['fee'] for tx in mempool]) + descendants = [] ancestors = list(chain) for x in reversed(chain): @@ -71,14 +75,43 @@ class MempoolPackagesTest(BitcoinTestFramework): assert_equal(mempool[x]['descendantsize'], descendant_size) descendant_count += 1 + # Check that ancestor calculations are correct + assert_equal(mempool[x]['ancestorcount'], ancestor_count) + assert_equal(mempool[x]['ancestorfees'], ancestor_fees * COIN) + assert_equal(mempool[x]['ancestorsize'], ancestor_size) + ancestor_size -= mempool[x]['size'] + ancestor_fees -= mempool[x]['fee'] + ancestor_count -= 1 + + # Check that parent/child list is correct + assert_equal(mempool[x]['spentby'], descendants[-1:]) + assert_equal(mempool[x]['depends'], ancestors[-2:-1]) + # Check that getmempooldescendants is correct assert_equal(sorted(descendants), sorted(self.nodes[0].getmempooldescendants(x))) + + # Check getmempooldescendants verbose output is correct + for descendant, dinfo in self.nodes[0].getmempooldescendants(x, True).items(): + assert_equal(dinfo['depends'], [chain[chain.index(descendant)-1]]) + if dinfo['descendantcount'] > 1: + assert_equal(dinfo['spentby'], [chain[chain.index(descendant)+1]]) + else: + assert_equal(dinfo['spentby'], []) descendants.append(x) # Check that getmempoolancestors is correct ancestors.remove(x) assert_equal(sorted(ancestors), sorted(self.nodes[0].getmempoolancestors(x))) + # Check that getmempoolancestors verbose output is correct + for ancestor, ainfo in self.nodes[0].getmempoolancestors(x, True).items(): + assert_equal(ainfo['spentby'], [chain[chain.index(ancestor)+1]]) + if ainfo['ancestorcount'] > 1: + assert_equal(ainfo['depends'], [chain[chain.index(ancestor)-1]]) + else: + assert_equal(ainfo['depends'], []) + + # Check that getmempoolancestors/getmempooldescendants correctly handle verbose=true v_ancestors = self.nodes[0].getmempoolancestors(chain[-1], True) assert_equal(len(v_ancestors), len(chain)-1) @@ -100,7 +133,7 @@ class MempoolPackagesTest(BitcoinTestFramework): for x in chain: ancestor_fees += mempool[x]['fee'] assert_equal(mempool[x]['ancestorfees'], ancestor_fees * COIN + 1000) - + # Undo the prioritisetransaction for later tests self.nodes[0].prioritisetransaction(txid=chain[0], fee_delta=-1000) @@ -149,6 +182,7 @@ class MempoolPackagesTest(BitcoinTestFramework): vout = utxo[1]['vout'] transaction_package = [] + tx_children = [] # First create one parent tx with 10 children (txid, sent_value) = self.chain_transaction(self.nodes[0], txid, vout, value, fee, 10) parent_transaction = txid @@ -159,11 +193,17 @@ class MempoolPackagesTest(BitcoinTestFramework): for i in range(MAX_DESCENDANTS - 1): utxo = transaction_package.pop(0) (txid, sent_value) = self.chain_transaction(self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10) + if utxo['txid'] is parent_transaction: + tx_children.append(txid) for j in range(10): transaction_package.append({'txid': txid, 'vout': j, 'amount': sent_value}) mempool = self.nodes[0].getrawmempool(True) assert_equal(mempool[parent_transaction]['descendantcount'], MAX_DESCENDANTS) + assert_equal(sorted(mempool[parent_transaction]['spentby']), sorted(tx_children)) + + for child in tx_children: + assert_equal(mempool[child]['depends'], [parent_transaction]) # Sending one more chained transaction will fail utxo = transaction_package.pop(0) @@ -205,7 +245,7 @@ class MempoolPackagesTest(BitcoinTestFramework): for i in range(2): outputs[self.nodes[0].getnewaddress()] = send_value rawtx = self.nodes[0].createrawtransaction(inputs, outputs) - signedtx = self.nodes[0].signrawtransaction(rawtx) + signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx) txid = self.nodes[0].sendrawtransaction(signedtx['hex']) tx0_id = txid value = send_value @@ -229,10 +269,10 @@ class MempoolPackagesTest(BitcoinTestFramework): inputs = [ {'txid' : tx1_id, 'vout': 0}, {'txid' : txid, 'vout': 0} ] outputs = { self.nodes[0].getnewaddress() : send_value + value - 4*fee } rawtx = self.nodes[0].createrawtransaction(inputs, outputs) - signedtx = self.nodes[0].signrawtransaction(rawtx) + signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx) txid = self.nodes[0].sendrawtransaction(signedtx['hex']) sync_mempools(self.nodes) - + # Now try to disconnect the tip on each node... self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash()) self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py index 17f0967219..53748df915 100755 --- a/test/functional/mempool_persist.py +++ b/test/functional/mempool_persist.py @@ -66,7 +66,9 @@ class MempoolPersistTest(BitcoinTestFramework): self.log.debug("Stop-start the nodes. Verify that node0 has the transactions in its mempool and node1 does not. Verify that node2 calculates its balance correctly after loading wallet transactions.") self.stop_nodes() - self.start_node(1) # Give this one a head-start, so we can be "extra-sure" that it didn't load anything later + # Give this node a head-start, so we can be "extra-sure" that it didn't load anything later + # Also don't store the mempool, to keep the datadir clean + self.start_node(1, extra_args=["-persistmempool=0"]) self.start_node(0) self.start_node(2) # Give bitcoind a second to reload the mempool diff --git a/test/functional/mempool_reorg.py b/test/functional/mempool_reorg.py index d6bb292a58..eabed5d633 100755 --- a/test/functional/mempool_reorg.py +++ b/test/functional/mempool_reorg.py @@ -48,7 +48,7 @@ class MempoolCoinbaseTest(BitcoinTestFramework): # Set the time lock timelock_tx = timelock_tx.replace("ffffffff", "11111191", 1) timelock_tx = timelock_tx[:-8] + hex(self.nodes[0].getblockcount() + 2)[2:] + "000000" - timelock_tx = self.nodes[0].signrawtransaction(timelock_tx)["hex"] + timelock_tx = self.nodes[0].signrawtransactionwithwallet(timelock_tx)["hex"] # This will raise an exception because the timelock transaction is too immature to spend assert_raises_rpc_error(-26, "non-final", self.nodes[0].sendrawtransaction, timelock_tx) diff --git a/test/functional/mining_prioritisetransaction.py b/test/functional/mining_prioritisetransaction.py index 57954ce321..32e2b47fc9 100755 --- a/test/functional/mining_prioritisetransaction.py +++ b/test/functional/mining_prioritisetransaction.py @@ -116,11 +116,11 @@ class PrioritiseTransactionTest(BitcoinTestFramework): inputs.append({"txid" : utxo["txid"], "vout" : utxo["vout"]}) outputs[self.nodes[0].getnewaddress()] = utxo["amount"] raw_tx = self.nodes[0].createrawtransaction(inputs, outputs) - tx_hex = self.nodes[0].signrawtransaction(raw_tx)["hex"] + tx_hex = self.nodes[0].signrawtransactionwithwallet(raw_tx)["hex"] tx_id = self.nodes[0].decoderawtransaction(tx_hex)["txid"] # This will raise an exception due to min relay fee not being met - assert_raises_rpc_error(-26, "66: min relay fee not met", self.nodes[0].sendrawtransaction, tx_hex) + assert_raises_rpc_error(-26, "min relay fee not met (code 66)", self.nodes[0].sendrawtransaction, tx_hex) assert(tx_id not in self.nodes[0].getrawmempool()) # This is a less than 1000-byte transaction, so just set the fee diff --git a/test/functional/p2p_fingerprint.py b/test/functional/p2p_fingerprint.py index 93ef73e25e..516ce8555b 100755 --- a/test/functional/p2p_fingerprint.py +++ b/test/functional/p2p_fingerprint.py @@ -4,7 +4,7 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test various fingerprinting protections. -If an stale block more than a month old or its header are requested by a peer, +If a stale block more than a month old or its header are requested by a peer, the node should pretend that it does not have it to avoid fingerprinting. """ diff --git a/test/functional/p2p_invalid_block.py b/test/functional/p2p_invalid_block.py index edcade63c1..e1f328ba77 100755 --- a/test/functional/p2p_invalid_block.py +++ b/test/functional/p2p_invalid_block.py @@ -10,75 +10,63 @@ In this test we connect to one node over p2p, and test block requests: 3) Invalid block with bad coinbase value should be rejected and not re-requested. """ - -from test_framework.test_framework import ComparisonTestFramework -from test_framework.util import * -from test_framework.comptool import TestManager, TestInstance, RejectResult -from test_framework.blocktools import * -from test_framework.mininode import network_thread_start import copy -import time -# Use the ComparisonTestFramework with 1 node: only use --testbinary. -class InvalidBlockRequestTest(ComparisonTestFramework): +from test_framework.blocktools import create_block, create_coinbase, create_transaction +from test_framework.messages import COIN +from test_framework.mininode import network_thread_start, P2PDataStore +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal - ''' Can either run this test as 1 node with expected answers, or two and compare them. - Change the "outcome" variable from each TestInstance object to only do the comparison. ''' +class InvalidBlockRequestTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True + self.extra_args = [["-whitelist=127.0.0.1"]] def run_test(self): - test = TestManager(self, self.options.tmpdir) - test.add_all_connections(self.nodes) - self.tip = None - self.block_time = None + # Add p2p connection to node0 + node = self.nodes[0] # convenience reference to the node + node.add_p2p_connection(P2PDataStore()) + network_thread_start() - test.run() + node.p2p.wait_for_verack() + + best_block = node.getblock(node.getbestblockhash()) + tip = int(node.getbestblockhash(), 16) + height = best_block["height"] + 1 + block_time = best_block["time"] + 1 - def get_tests(self): - if self.tip is None: - self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0) - self.block_time = int(time.time())+1 + self.log.info("Create a new block with an anyone-can-spend coinbase") - ''' - Create a new block with an anyone-can-spend coinbase - ''' height = 1 - block = create_block(self.tip, create_coinbase(height), self.block_time) - self.block_time += 1 + block = create_block(tip, create_coinbase(height), block_time) block.solve() # Save the coinbase for later - self.block1 = block - self.tip = block.sha256 - height += 1 - yield TestInstance([[block, True]]) - - ''' - Now we need that block to mature so we can spend the coinbase. - ''' - test = TestInstance(sync_every_block=False) - for i in range(100): - block = create_block(self.tip, create_coinbase(height), self.block_time) - block.solve() - self.tip = block.sha256 - self.block_time += 1 - test.blocks_and_transactions.append([block, True]) - height += 1 - yield test - - ''' - Now we use merkle-root malleability to generate an invalid block with - same blockheader. - Manufacture a block with 3 transactions (coinbase, spend of prior - coinbase, spend of that spend). Duplicate the 3rd transaction to - leave merkle root and blockheader unchanged but invalidate the block. - ''' - block2 = create_block(self.tip, create_coinbase(height), self.block_time) - self.block_time += 1 + block1 = block + tip = block.sha256 + node.p2p.send_blocks_and_test([block1], node, True) + + self.log.info("Mature the block.") + node.generate(100) + + best_block = node.getblock(node.getbestblockhash()) + tip = int(node.getbestblockhash(), 16) + height = best_block["height"] + 1 + block_time = best_block["time"] + 1 + + # Use merkle-root malleability to generate an invalid block with + # same blockheader. + # Manufacture a block with 3 transactions (coinbase, spend of prior + # coinbase, spend of that spend). Duplicate the 3rd transaction to + # leave merkle root and blockheader unchanged but invalidate the block. + self.log.info("Test merkle root malleability.") + + block2 = create_block(tip, create_coinbase(height), block_time) + block_time += 1 # b'0x51' is OP_TRUE - tx1 = create_transaction(self.block1.vtx[0], 0, b'\x51', 50 * COIN) + tx1 = create_transaction(block1.vtx[0], 0, b'\x51', 50 * COIN) tx2 = create_transaction(tx1, 0, b'\x51', 50 * COIN) block2.vtx.extend([tx1, tx2]) @@ -94,24 +82,20 @@ class InvalidBlockRequestTest(ComparisonTestFramework): assert_equal(orig_hash, block2.rehash()) assert(block2_orig.vtx != block2.vtx) - self.tip = block2.sha256 - yield TestInstance([[block2, RejectResult(16, b'bad-txns-duplicate')], [block2_orig, True]]) - height += 1 - - ''' - Make sure that a totally screwed up block is not valid. - ''' - block3 = create_block(self.tip, create_coinbase(height), self.block_time) - self.block_time += 1 - block3.vtx[0].vout[0].nValue = 100 * COIN # Too high! - block3.vtx[0].sha256=None + node.p2p.send_blocks_and_test([block2], node, False, False, 16, b'bad-txns-duplicate') + + self.log.info("Test very broken block.") + + block3 = create_block(tip, create_coinbase(height), block_time) + block_time += 1 + block3.vtx[0].vout[0].nValue = 100 * COIN # Too high! + block3.vtx[0].sha256 = None block3.vtx[0].calc_sha256() block3.hashMerkleRoot = block3.calc_merkle_root() block3.rehash() block3.solve() - yield TestInstance([[block3, RejectResult(16, b'bad-cb-amount')]]) - + node.p2p.send_blocks_and_test([block3], node, False, False, 16, b'bad-cb-amount') if __name__ == '__main__': InvalidBlockRequestTest().main() diff --git a/test/functional/p2p_invalid_tx.py b/test/functional/p2p_invalid_tx.py index 9c1100e070..69ce529ad6 100755 --- a/test/functional/p2p_invalid_tx.py +++ b/test/functional/p2p_invalid_tx.py @@ -4,70 +4,53 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test node responses to invalid transactions. -In this test we connect to one node over p2p, and test tx requests. -""" +In this test we connect to one node over p2p, and test tx requests.""" +from test_framework.blocktools import create_block, create_coinbase, create_transaction +from test_framework.messages import COIN +from test_framework.mininode import network_thread_start, P2PDataStore +from test_framework.test_framework import BitcoinTestFramework -from test_framework.test_framework import ComparisonTestFramework -from test_framework.comptool import TestManager, TestInstance, RejectResult -from test_framework.blocktools import * -import time +class InvalidTxRequestTest(BitcoinTestFramework): - - -# Use the ComparisonTestFramework with 1 node: only use --testbinary. -class InvalidTxRequestTest(ComparisonTestFramework): - - ''' Can either run this test as 1 node with expected answers, or two and compare them. - Change the "outcome" variable from each TestInstance object to only do the comparison. ''' def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True + self.extra_args = [["-whitelist=127.0.0.1"]] def run_test(self): - test = TestManager(self, self.options.tmpdir) - test.add_all_connections(self.nodes) - self.tip = None - self.block_time = None + # Add p2p connection to node0 + node = self.nodes[0] # convenience reference to the node + node.add_p2p_connection(P2PDataStore()) + network_thread_start() - test.run() + node.p2p.wait_for_verack() - def get_tests(self): - if self.tip is None: - self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0) - self.block_time = int(time.time())+1 + best_block = self.nodes[0].getbestblockhash() + tip = int(best_block, 16) + best_block_time = self.nodes[0].getblock(best_block)['time'] + block_time = best_block_time + 1 - ''' - Create a new block with an anyone-can-spend coinbase - ''' + self.log.info("Create a new block with an anyone-can-spend coinbase.") height = 1 - block = create_block(self.tip, create_coinbase(height), self.block_time) - self.block_time += 1 + block = create_block(tip, create_coinbase(height), block_time) block.solve() # Save the coinbase for later - self.block1 = block - self.tip = block.sha256 - height += 1 - yield TestInstance([[block, True]]) + block1 = block + tip = block.sha256 + node.p2p.send_blocks_and_test([block], node, success=True) - ''' - Now we need that block to mature so we can spend the coinbase. - ''' - test = TestInstance(sync_every_block=False) - for i in range(100): - block = create_block(self.tip, create_coinbase(height), self.block_time) - block.solve() - self.tip = block.sha256 - self.block_time += 1 - test.blocks_and_transactions.append([block, True]) - height += 1 - yield test + self.log.info("Mature the block.") + self.nodes[0].generate(100) # b'\x64' is OP_NOTIF # Transaction will be rejected with code 16 (REJECT_INVALID) - tx1 = create_transaction(self.block1.vtx[0], 0, b'\x64', 50 * COIN - 12000) - yield TestInstance([[tx1, RejectResult(16, b'mandatory-script-verify-flag-failed')]]) + tx1 = create_transaction(block1.vtx[0], 0, b'\x64', 50 * COIN - 12000) + node.p2p.send_txs_and_test([tx1], node, success=False, reject_code=16, reject_reason=b'mandatory-script-verify-flag-failed (Invalid OP_IF construction)') + + # Verify valid transaction + tx1 = create_transaction(block1.vtx[0], 0, b'', 50 * COIN - 12000) + node.p2p.send_txs_and_test([tx1], node, success=True) - # TODO: test further transactions... if __name__ == '__main__': InvalidTxRequestTest().main() diff --git a/test/functional/p2p_node_network_limited.py b/test/functional/p2p_node_network_limited.py index 70415e0168..81a41d6a97 100755 --- a/test/functional/p2p_node_network_limited.py +++ b/test/functional/p2p_node_network_limited.py @@ -8,16 +8,21 @@ Tests that a node configured with -prune=550 signals NODE_NETWORK_LIMITED correc and that it responds to getdata requests for blocks correctly: - send a block within 288 + 2 of the tip - disconnect peers who request blocks older than that.""" -from test_framework.messages import CInv, msg_getdata -from test_framework.mininode import NODE_BLOOM, NODE_NETWORK_LIMITED, NODE_WITNESS, NetworkThread, P2PInterface +from test_framework.messages import CInv, msg_getdata, msg_verack +from test_framework.mininode import NODE_BLOOM, NODE_NETWORK_LIMITED, NODE_WITNESS, P2PInterface, wait_until, mininode_lock, network_thread_start, network_thread_join from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal +from test_framework.util import assert_equal, disconnect_nodes, connect_nodes_bi, sync_blocks class P2PIgnoreInv(P2PInterface): + firstAddrnServices = 0 def on_inv(self, message): # The node will send us invs for other blocks. Ignore them. pass - + def on_addr(self, message): + self.firstAddrnServices = message.addrs[0].nServices + def wait_for_addr(self, timeout=5): + test_function = lambda: self.last_message.get("addr") + wait_until(test_function, timeout=timeout, lock=mininode_lock) def send_getdata_for_block(self, blockhash): getdata_request = msg_getdata() getdata_request.inv.append(CInv(2, int(blockhash, 16))) @@ -26,12 +31,24 @@ class P2PIgnoreInv(P2PInterface): class NodeNetworkLimitedTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True - self.num_nodes = 1 - self.extra_args = [['-prune=550']] + self.num_nodes = 3 + self.extra_args = [['-prune=550', '-addrmantest'], [], []] + + def disconnect_all(self): + disconnect_nodes(self.nodes[0], 1) + disconnect_nodes(self.nodes[1], 0) + disconnect_nodes(self.nodes[2], 1) + disconnect_nodes(self.nodes[2], 0) + disconnect_nodes(self.nodes[0], 2) + disconnect_nodes(self.nodes[1], 2) + + def setup_network(self): + super(NodeNetworkLimitedTest, self).setup_network() + self.disconnect_all() def run_test(self): node = self.nodes[0].add_p2p_connection(P2PIgnoreInv()) - NetworkThread().start() + network_thread_start() node.wait_for_verack() expected_services = NODE_BLOOM | NODE_WITNESS | NODE_NETWORK_LIMITED @@ -43,7 +60,9 @@ class NodeNetworkLimitedTest(BitcoinTestFramework): assert_equal(int(self.nodes[0].getnetworkinfo()['localservices'], 16), expected_services) self.log.info("Mine enough blocks to reach the NODE_NETWORK_LIMITED range.") - blocks = self.nodes[0].generate(292) + connect_nodes_bi(self.nodes, 0, 1) + blocks = self.nodes[1].generate(292) + sync_blocks([self.nodes[0], self.nodes[1]]) self.log.info("Make sure we can max retrive block at tip-288.") node.send_getdata_for_block(blocks[1]) # last block in valid range @@ -53,5 +72,48 @@ class NodeNetworkLimitedTest(BitcoinTestFramework): node.send_getdata_for_block(blocks[0]) # first block outside of the 288+2 limit node.wait_for_disconnect(5) + self.log.info("Check local address relay, do a fresh connection.") + self.nodes[0].disconnect_p2ps() + network_thread_join() + node1 = self.nodes[0].add_p2p_connection(P2PIgnoreInv()) + network_thread_start() + node1.wait_for_verack() + node1.send_message(msg_verack()) + + node1.wait_for_addr() + #must relay address with NODE_NETWORK_LIMITED + assert_equal(node1.firstAddrnServices, 1036) + + self.nodes[0].disconnect_p2ps() + node1.wait_for_disconnect() + + # connect unsynced node 2 with pruned NODE_NETWORK_LIMITED peer + # because node 2 is in IBD and node 0 is a NODE_NETWORK_LIMITED peer, sync must not be possible + connect_nodes_bi(self.nodes, 0, 2) + try: + sync_blocks([self.nodes[0], self.nodes[2]], timeout=5) + except: + pass + # node2 must remain at heigh 0 + assert_equal(self.nodes[2].getblockheader(self.nodes[2].getbestblockhash())['height'], 0) + + # now connect also to node 1 (non pruned) + connect_nodes_bi(self.nodes, 1, 2) + + # sync must be possible + sync_blocks(self.nodes) + + # disconnect all peers + self.disconnect_all() + + # mine 10 blocks on node 0 (pruned node) + self.nodes[0].generate(10) + + # connect node1 (non pruned) with node0 (pruned) and check if the can sync + connect_nodes_bi(self.nodes, 0, 1) + + # sync must be possible, node 1 is no longer in IBD and should therefore connect to node 0 (NODE_NETWORK_LIMITED) + sync_blocks([self.nodes[0], self.nodes[1]]) + if __name__ == '__main__': NodeNetworkLimitedTest().main() diff --git a/test/functional/rpc_bind.py b/test/functional/rpc_bind.py index 05433c7e24..d43c2cd5d0 100755 --- a/test/functional/rpc_bind.py +++ b/test/functional/rpc_bind.py @@ -14,6 +14,7 @@ from test_framework.netutil import * class RPCBindTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True + self.bind_to_localhost_only = False self.num_nodes = 1 def setup_network(self): diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index 7cf2abe6f0..a9e14d3e3c 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -102,6 +102,22 @@ class BlockchainTest(BitcoinTestFramework): def _test_getchaintxstats(self): self.log.info("Test getchaintxstats") + # Test `getchaintxstats` invalid extra parameters + assert_raises_rpc_error(-1, 'getchaintxstats', self.nodes[0].getchaintxstats, 0, '', 0) + + # Test `getchaintxstats` invalid `nblocks` + assert_raises_rpc_error(-1, "JSON value is not an integer as expected", self.nodes[0].getchaintxstats, '') + assert_raises_rpc_error(-8, "Invalid block count: should be between 0 and the block's height - 1", self.nodes[0].getchaintxstats, -1) + assert_raises_rpc_error(-8, "Invalid block count: should be between 0 and the block's height - 1", self.nodes[0].getchaintxstats, self.nodes[0].getblockcount()) + + # Test `getchaintxstats` invalid `blockhash` + assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[0].getchaintxstats, blockhash=0) + assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getchaintxstats, blockhash='0') + blockhash = self.nodes[0].getblockhash(200) + self.nodes[0].invalidateblock(blockhash) + assert_raises_rpc_error(-8, "Block is not in main chain", self.nodes[0].getchaintxstats, blockhash=blockhash) + self.nodes[0].reconsiderblock(blockhash) + chaintxstats = self.nodes[0].getchaintxstats(1) # 200 txs plus genesis tx assert_equal(chaintxstats['txcount'], 201) @@ -133,8 +149,6 @@ class BlockchainTest(BitcoinTestFramework): assert('window_interval' not in chaintxstats) assert('txrate' not in chaintxstats) - assert_raises_rpc_error(-8, "Invalid block count: should be between 0 and the block's height - 1", self.nodes[0].getchaintxstats, 201) - def _test_gettxoutsetinfo(self): node = self.nodes[0] res = node.gettxoutsetinfo() diff --git a/test/functional/rpc_deprecated.py b/test/functional/rpc_deprecated.py index 90183474bb..b94b9d8fae 100755 --- a/test/functional/rpc_deprecated.py +++ b/test/functional/rpc_deprecated.py @@ -9,7 +9,7 @@ class DeprecatedRpcTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True - self.extra_args = [[], []] + self.extra_args = [[], ["-deprecatedrpc=validateaddress"]] def run_test(self): # This test should be used to verify correct behaviour of deprecated @@ -18,10 +18,13 @@ class DeprecatedRpcTest(BitcoinTestFramework): # self.log.info("Make sure that -deprecatedrpc=createmultisig allows it to take addresses") # assert_raises_rpc_error(-5, "Invalid public key", self.nodes[0].createmultisig, 1, [self.nodes[0].getnewaddress()]) # self.nodes[1].createmultisig(1, [self.nodes[1].getnewaddress()]) - # - # There are currently no deprecated RPC methods in master, so this - # test is currently empty. - pass + + self.log.info("Test validateaddress deprecation") + SOME_ADDRESS = "mnvGjUy3NMj67yJ6gkK5o9e5RS33Z2Vqcu" # This is just some random address to pass as a parameter to validateaddress + dep_validate_address = self.nodes[0].validateaddress(SOME_ADDRESS) + assert "ismine" not in dep_validate_address + not_dep_val = self.nodes[1].validateaddress(SOME_ADDRESS) + assert "ismine" in not_dep_val if __name__ == '__main__': DeprecatedRpcTest().main() diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py index 6b9c9c15b7..5fb9a361d9 100755 --- a/test/functional/rpc_fundrawtransaction.py +++ b/test/functional/rpc_fundrawtransaction.py @@ -53,7 +53,7 @@ class RawTransactionsTest(BitcoinTestFramework): assert_equal(rawmatch["changepos"], -1) watchonly_address = self.nodes[0].getnewaddress() - watchonly_pubkey = self.nodes[0].validateaddress(watchonly_address)["pubkey"] + watchonly_pubkey = self.nodes[0].getaddressinfo(watchonly_address)["pubkey"] watchonly_amount = Decimal(200) self.nodes[3].importpubkey(watchonly_pubkey, "", True) watchonly_txid = self.nodes[0].sendtoaddress(watchonly_address, watchonly_amount) @@ -181,6 +181,9 @@ class RawTransactionsTest(BitcoinTestFramework): assert_raises_rpc_error(-3, "Unexpected key foo", self.nodes[2].fundrawtransaction, rawtx, {'foo':'bar'}) + # reserveChangeKey was deprecated and is now removed + assert_raises_rpc_error(-3, "Unexpected key reserveChangeKey", lambda: self.nodes[2].fundrawtransaction(hexstring=rawtx, options={'reserveChangeKey': True})) + ############################################################ # test a fundrawtransaction with an invalid change address # ############################################################ @@ -368,8 +371,8 @@ class RawTransactionsTest(BitcoinTestFramework): addr1 = self.nodes[1].getnewaddress() addr2 = self.nodes[1].getnewaddress() - addr1Obj = self.nodes[1].validateaddress(addr1) - addr2Obj = self.nodes[1].validateaddress(addr2) + addr1Obj = self.nodes[1].getaddressinfo(addr1) + addr2Obj = self.nodes[1].getaddressinfo(addr2) mSigObj = self.nodes[1].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']])['address'] @@ -398,11 +401,11 @@ class RawTransactionsTest(BitcoinTestFramework): addr4 = self.nodes[1].getnewaddress() addr5 = self.nodes[1].getnewaddress() - addr1Obj = self.nodes[1].validateaddress(addr1) - addr2Obj = self.nodes[1].validateaddress(addr2) - addr3Obj = self.nodes[1].validateaddress(addr3) - addr4Obj = self.nodes[1].validateaddress(addr4) - addr5Obj = self.nodes[1].validateaddress(addr5) + addr1Obj = self.nodes[1].getaddressinfo(addr1) + addr2Obj = self.nodes[1].getaddressinfo(addr2) + addr3Obj = self.nodes[1].getaddressinfo(addr3) + addr4Obj = self.nodes[1].getaddressinfo(addr4) + addr5Obj = self.nodes[1].getaddressinfo(addr5) mSigObj = self.nodes[1].addmultisigaddress(4, [addr1Obj['pubkey'], addr2Obj['pubkey'], addr3Obj['pubkey'], addr4Obj['pubkey'], addr5Obj['pubkey']])['address'] @@ -428,8 +431,8 @@ class RawTransactionsTest(BitcoinTestFramework): addr1 = self.nodes[2].getnewaddress() addr2 = self.nodes[2].getnewaddress() - addr1Obj = self.nodes[2].validateaddress(addr1) - addr2Obj = self.nodes[2].validateaddress(addr2) + addr1Obj = self.nodes[2].getaddressinfo(addr1) + addr2Obj = self.nodes[2].getaddressinfo(addr2) mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']])['address'] @@ -446,7 +449,7 @@ class RawTransactionsTest(BitcoinTestFramework): rawtx = self.nodes[2].createrawtransaction(inputs, outputs) fundedTx = self.nodes[2].fundrawtransaction(rawtx) - signedTx = self.nodes[2].signrawtransaction(fundedTx['hex']) + signedTx = self.nodes[2].signrawtransactionwithwallet(fundedTx['hex']) txId = self.nodes[2].sendrawtransaction(signedTx['hex']) self.sync_all() self.nodes[1].generate(1) @@ -500,7 +503,7 @@ class RawTransactionsTest(BitcoinTestFramework): #now we need to unlock self.nodes[1].walletpassphrase("test", 600) - signedTx = self.nodes[1].signrawtransaction(fundedTx['hex']) + signedTx = self.nodes[1].signrawtransactionwithwallet(fundedTx['hex']) txId = self.nodes[1].sendrawtransaction(signedTx['hex']) self.nodes[1].generate(1) self.sync_all() @@ -561,7 +564,7 @@ class RawTransactionsTest(BitcoinTestFramework): outputs = {self.nodes[0].getnewaddress():0.15,self.nodes[0].getnewaddress():0.04} rawtx = self.nodes[1].createrawtransaction(inputs, outputs) fundedTx = self.nodes[1].fundrawtransaction(rawtx) - fundedAndSignedTx = self.nodes[1].signrawtransaction(fundedTx['hex']) + fundedAndSignedTx = self.nodes[1].signrawtransactionwithwallet(fundedTx['hex']) txId = self.nodes[1].sendrawtransaction(fundedAndSignedTx['hex']) self.sync_all() self.nodes[0].generate(1) @@ -619,9 +622,9 @@ class RawTransactionsTest(BitcoinTestFramework): assert_greater_than(result["changepos"], -1) assert_equal(result["fee"] + res_dec["vout"][result["changepos"]]["value"], watchonly_amount / 10) - signedtx = self.nodes[3].signrawtransaction(result["hex"]) + signedtx = self.nodes[3].signrawtransactionwithwallet(result["hex"]) assert(not signedtx["complete"]) - signedtx = self.nodes[0].signrawtransaction(signedtx["hex"]) + signedtx = self.nodes[0].signrawtransactionwithwallet(signedtx["hex"]) assert(signedtx["complete"]) self.nodes[0].sendrawtransaction(signedtx["hex"]) self.nodes[0].generate(1) diff --git a/test/functional/rpc_listtransactions.py b/test/functional/rpc_listtransactions.py index ba71ac0967..0dd7372e6b 100755 --- a/test/functional/rpc_listtransactions.py +++ b/test/functional/rpc_listtransactions.py @@ -81,7 +81,7 @@ class ListTransactionsTest(BitcoinTestFramework): {"category":"receive","amount":Decimal("0.44")}, {"txid":txid, "account" : "toself"} ) - pubkey = self.nodes[1].validateaddress(self.nodes[1].getnewaddress())['pubkey'] + pubkey = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress())['pubkey'] multisig = self.nodes[1].createmultisig(1, [pubkey]) self.nodes[0].importaddress(multisig["redeemScript"], "watchonly", False, True) txid = self.nodes[1].sendtoaddress(multisig["address"], 0.1) @@ -131,7 +131,7 @@ class ListTransactionsTest(BitcoinTestFramework): inputs = [{"txid":utxo_to_use["txid"], "vout":utxo_to_use["vout"]}] outputs = {self.nodes[0].getnewaddress(): 0.999} tx2 = self.nodes[1].createrawtransaction(inputs, outputs) - tx2_signed = self.nodes[1].signrawtransaction(tx2)["hex"] + tx2_signed = self.nodes[1].signrawtransactionwithwallet(tx2)["hex"] txid_2 = self.nodes[1].sendrawtransaction(tx2_signed) # ...and check the result @@ -148,7 +148,7 @@ class ListTransactionsTest(BitcoinTestFramework): tx3_modified = txFromHex(tx3) tx3_modified.vin[0].nSequence = 0 tx3 = bytes_to_hex_str(tx3_modified.serialize()) - tx3_signed = self.nodes[0].signrawtransaction(tx3)['hex'] + tx3_signed = self.nodes[0].signrawtransactionwithwallet(tx3)['hex'] txid_3 = self.nodes[0].sendrawtransaction(tx3_signed) assert(is_opt_in(self.nodes[0], txid_3)) @@ -162,7 +162,7 @@ class ListTransactionsTest(BitcoinTestFramework): inputs = [{"txid": txid_3, "vout":utxo_to_use["vout"]}] outputs = {self.nodes[0].getnewaddress(): 0.997} tx4 = self.nodes[1].createrawtransaction(inputs, outputs) - tx4_signed = self.nodes[1].signrawtransaction(tx4)["hex"] + tx4_signed = self.nodes[1].signrawtransactionwithwallet(tx4)["hex"] txid_4 = self.nodes[1].sendrawtransaction(tx4_signed) assert(not is_opt_in(self.nodes[1], txid_4)) @@ -174,7 +174,7 @@ class ListTransactionsTest(BitcoinTestFramework): tx3_b = tx3_modified tx3_b.vout[0].nValue -= int(Decimal("0.004") * COIN) # bump the fee tx3_b = bytes_to_hex_str(tx3_b.serialize()) - tx3_b_signed = self.nodes[0].signrawtransaction(tx3_b)['hex'] + tx3_b_signed = self.nodes[0].signrawtransactionwithwallet(tx3_b)['hex'] txid_3b = self.nodes[0].sendrawtransaction(tx3_b_signed, True) assert(is_opt_in(self.nodes[0], txid_3b)) diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py index 16e4f6adb4..5f34b35bfb 100755 --- a/test/functional/rpc_net.py +++ b/test/functional/rpc_net.py @@ -15,6 +15,7 @@ from test_framework.util import ( assert_raises_rpc_error, connect_nodes_bi, p2p_port, + wait_until, ) class NetTest(BitcoinTestFramework): @@ -47,14 +48,13 @@ class NetTest(BitcoinTestFramework): # the bytes sent/received should change # note ping and pong are 32 bytes each self.nodes[0].ping() - time.sleep(0.1) + wait_until(lambda: (net_totals['totalbytessent'] + 32*2) == self.nodes[0].getnettotals()['totalbytessent'], timeout=1) + wait_until(lambda: (net_totals['totalbytesrecv'] + 32*2) == self.nodes[0].getnettotals()['totalbytesrecv'], timeout=1) + peer_info_after_ping = self.nodes[0].getpeerinfo() - net_totals_after_ping = self.nodes[0].getnettotals() for before, after in zip(peer_info, peer_info_after_ping): assert_equal(before['bytesrecv_per_msg']['pong'] + 32, after['bytesrecv_per_msg']['pong']) assert_equal(before['bytessent_per_msg']['ping'] + 32, after['bytessent_per_msg']['ping']) - assert_equal(net_totals['totalbytesrecv'] + 32*2, net_totals_after_ping['totalbytesrecv']) - assert_equal(net_totals['totalbytessent'] + 32*2, net_totals_after_ping['totalbytessent']) def _test_getnetworkinginfo(self): assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True) diff --git a/test/functional/rpc_preciousblock.py b/test/functional/rpc_preciousblock.py index 960cd0ad12..796a2edbef 100755 --- a/test/functional/rpc_preciousblock.py +++ b/test/functional/rpc_preciousblock.py @@ -8,7 +8,6 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, connect_nodes_bi, - sync_chain, sync_blocks, ) @@ -72,7 +71,7 @@ class PreciousTest(BitcoinTestFramework): assert_equal(self.nodes[0].getbestblockhash(), hashC) self.log.info("Make Node1 prefer block C") self.nodes[1].preciousblock(hashC) - sync_chain(self.nodes[0:2]) # wait because node 1 may not have downloaded hashC + sync_blocks(self.nodes[0:2]) # wait because node 1 may not have downloaded hashC assert_equal(self.nodes[1].getbestblockhash(), hashC) self.log.info("Make Node1 prefer block G again") self.nodes[1].preciousblock(hashG) diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py index c2ca7c70b8..825b897871 100755 --- a/test/functional/rpc_rawtransaction.py +++ b/test/functional/rpc_rawtransaction.py @@ -6,13 +6,18 @@ Test the following RPCs: - createrawtransaction - - signrawtransaction + - signrawtransactionwithwallet - sendrawtransaction - decoderawtransaction - getrawtransaction """ +from collections import OrderedDict +from io import BytesIO from test_framework.test_framework import BitcoinTestFramework +from test_framework.messages import ( + CTransaction, +) from test_framework.util import * @@ -43,11 +48,10 @@ class RawTransactionsTest(BitcoinTestFramework): def setup_network(self, split=False): super().setup_network() - connect_nodes_bi(self.nodes,0,2) + connect_nodes_bi(self.nodes, 0, 2) def run_test(self): - - #prepare some coins for multiple *rawtransaction commands + self.log.info('prepare some coins for multiple *rawtransaction commands') self.nodes[2].generate(1) self.sync_all() self.nodes[0].generate(101) @@ -59,10 +63,11 @@ class RawTransactionsTest(BitcoinTestFramework): self.nodes[0].generate(5) self.sync_all() - # Test getrawtransaction on genesis block coinbase returns an error + self.log.info('Test getrawtransaction on genesis block coinbase returns an error') block = self.nodes[0].getblock(self.nodes[0].getblockhash(0)) assert_raises_rpc_error(-5, "The genesis block coinbase is not considered an ordinary transaction", self.nodes[0].getrawtransaction, block['merkleroot']) + self.log.info('Check parameter types and required parameters of createrawtransaction') # Test `createrawtransaction` required parameters assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction) assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction, []) @@ -83,12 +88,18 @@ class RawTransactionsTest(BitcoinTestFramework): # Test `createrawtransaction` invalid `outputs` address = self.nodes[0].getnewaddress() - assert_raises_rpc_error(-3, "Expected type object", self.nodes[0].createrawtransaction, [], 'foo') + address2 = self.nodes[0].getnewaddress() + assert_raises_rpc_error(-1, "JSON value is not an array as expected", self.nodes[0].createrawtransaction, [], 'foo') + self.nodes[0].createrawtransaction(inputs=[], outputs={}) # Should not throw for backwards compatibility + self.nodes[0].createrawtransaction(inputs=[], outputs=[]) assert_raises_rpc_error(-8, "Data must be hexadecimal string", self.nodes[0].createrawtransaction, [], {'data': 'foo'}) assert_raises_rpc_error(-5, "Invalid Bitcoin address", self.nodes[0].createrawtransaction, [], {'foo': 0}) assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].createrawtransaction, [], {address: 'foo'}) assert_raises_rpc_error(-3, "Amount out of range", self.nodes[0].createrawtransaction, [], {address: -1}) assert_raises_rpc_error(-8, "Invalid parameter, duplicated address: %s" % address, self.nodes[0].createrawtransaction, [], multidict([(address, 1), (address, 1)])) + assert_raises_rpc_error(-8, "Invalid parameter, duplicated address: %s" % address, self.nodes[0].createrawtransaction, [], [{address: 1}, {address: 1}]) + assert_raises_rpc_error(-8, "Invalid parameter, key-value pair must contain exactly one key", self.nodes[0].createrawtransaction, [], [{'a': 1, 'b': 2}]) + assert_raises_rpc_error(-8, "Invalid parameter, key-value pair not an object as expected", self.nodes[0].createrawtransaction, [], [['key-value pair1'], ['2']]) # Test `createrawtransaction` invalid `locktime` assert_raises_rpc_error(-3, "Expected type number", self.nodes[0].createrawtransaction, [], {}, 'foo') @@ -98,13 +109,42 @@ class RawTransactionsTest(BitcoinTestFramework): # Test `createrawtransaction` invalid `replaceable` assert_raises_rpc_error(-3, "Expected type bool", self.nodes[0].createrawtransaction, [], {}, 0, 'foo') - ######################################### - # sendrawtransaction with missing input # - ######################################### + self.log.info('Check that createrawtransaction accepts an array and object as outputs') + tx = CTransaction() + # One output + tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs={address: 99})))) + assert_equal(len(tx.vout), 1) + assert_equal( + bytes_to_hex_str(tx.serialize()), + self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{address: 99}]), + ) + # Two outputs + tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=OrderedDict([(address, 99), (address2, 99)]))))) + assert_equal(len(tx.vout), 2) + assert_equal( + bytes_to_hex_str(tx.serialize()), + self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{address: 99}, {address2: 99}]), + ) + # Two data outputs + tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=multidict([('data', '99'), ('data', '99')]))))) + assert_equal(len(tx.vout), 2) + assert_equal( + bytes_to_hex_str(tx.serialize()), + self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{'data': '99'}, {'data': '99'}]), + ) + # Multiple mixed outputs + tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=multidict([(address, 99), ('data', '99'), ('data', '99')]))))) + assert_equal(len(tx.vout), 3) + assert_equal( + bytes_to_hex_str(tx.serialize()), + self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{address: 99}, {'data': '99'}, {'data': '99'}]), + ) + + self.log.info('sendrawtransaction with missing input') inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1}] #won't exists outputs = { self.nodes[0].getnewaddress() : 4.998 } rawtx = self.nodes[2].createrawtransaction(inputs, outputs) - rawtx = self.nodes[2].signrawtransaction(rawtx) + rawtx = self.nodes[2].signrawtransactionwithwallet(rawtx) # This will raise an exception since there are missing inputs assert_raises_rpc_error(-25, "Missing inputs", self.nodes[2].sendrawtransaction, rawtx['hex']) @@ -146,8 +186,8 @@ class RawTransactionsTest(BitcoinTestFramework): addr1 = self.nodes[2].getnewaddress() addr2 = self.nodes[2].getnewaddress() - addr1Obj = self.nodes[2].validateaddress(addr1) - addr2Obj = self.nodes[2].validateaddress(addr2) + addr1Obj = self.nodes[2].getaddressinfo(addr1) + addr2Obj = self.nodes[2].getaddressinfo(addr2) # Tests for createmultisig and addmultisigaddress assert_raises_rpc_error(-5, "Invalid public key", self.nodes[0].createmultisig, 1, ["01020304"]) @@ -173,9 +213,9 @@ class RawTransactionsTest(BitcoinTestFramework): addr2 = self.nodes[2].getnewaddress() addr3 = self.nodes[2].getnewaddress() - addr1Obj = self.nodes[1].validateaddress(addr1) - addr2Obj = self.nodes[2].validateaddress(addr2) - addr3Obj = self.nodes[2].validateaddress(addr3) + addr1Obj = self.nodes[1].getaddressinfo(addr1) + addr2Obj = self.nodes[2].getaddressinfo(addr2) + addr3Obj = self.nodes[2].getaddressinfo(addr3) mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey'], addr3Obj['pubkey']])['address'] @@ -186,7 +226,7 @@ class RawTransactionsTest(BitcoinTestFramework): self.nodes[0].generate(1) self.sync_all() - #THIS IS A INCOMPLETE FEATURE + #THIS IS AN INCOMPLETE FEATURE #NODE2 HAS TWO OF THREE KEY AND THE FUNDS SHOULD BE SPENDABLE AND COUNT AT BALANCE CALCULATION assert_equal(self.nodes[2].getbalance(), bal) #for now, assume the funds of a 2of3 multisig tx are not marked as spendable @@ -202,10 +242,10 @@ class RawTransactionsTest(BitcoinTestFramework): inputs = [{ "txid" : txId, "vout" : vout['n'], "scriptPubKey" : vout['scriptPubKey']['hex'], "amount" : vout['value']}] outputs = { self.nodes[0].getnewaddress() : 2.19 } rawTx = self.nodes[2].createrawtransaction(inputs, outputs) - rawTxPartialSigned = self.nodes[1].signrawtransaction(rawTx, inputs) + rawTxPartialSigned = self.nodes[1].signrawtransactionwithwallet(rawTx, inputs) assert_equal(rawTxPartialSigned['complete'], False) #node1 only has one key, can't comp. sign the tx - rawTxSigned = self.nodes[2].signrawtransaction(rawTx, inputs) + rawTxSigned = self.nodes[2].signrawtransactionwithwallet(rawTx, inputs) assert_equal(rawTxSigned['complete'], True) #node2 can sign the tx compl., own two of three keys self.nodes[2].sendrawtransaction(rawTxSigned['hex']) rawTx = self.nodes[0].decoderawtransaction(rawTxSigned['hex']) @@ -219,12 +259,12 @@ class RawTransactionsTest(BitcoinTestFramework): addr1 = self.nodes[1].getnewaddress() addr2 = self.nodes[2].getnewaddress() - addr1Obj = self.nodes[1].validateaddress(addr1) - addr2Obj = self.nodes[2].validateaddress(addr2) + addr1Obj = self.nodes[1].getaddressinfo(addr1) + addr2Obj = self.nodes[2].getaddressinfo(addr2) self.nodes[1].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']])['address'] mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']])['address'] - mSigObjValid = self.nodes[2].validateaddress(mSigObj) + mSigObjValid = self.nodes[2].getaddressinfo(mSigObj) txId = self.nodes[0].sendtoaddress(mSigObj, 2.2) decTx = self.nodes[0].gettransaction(txId) @@ -247,15 +287,15 @@ class RawTransactionsTest(BitcoinTestFramework): inputs = [{ "txid" : txId, "vout" : vout['n'], "scriptPubKey" : vout['scriptPubKey']['hex'], "redeemScript" : mSigObjValid['hex'], "amount" : vout['value']}] outputs = { self.nodes[0].getnewaddress() : 2.19 } rawTx2 = self.nodes[2].createrawtransaction(inputs, outputs) - rawTxPartialSigned1 = self.nodes[1].signrawtransaction(rawTx2, inputs) - self.log.info(rawTxPartialSigned1) + rawTxPartialSigned1 = self.nodes[1].signrawtransactionwithwallet(rawTx2, inputs) + self.log.debug(rawTxPartialSigned1) assert_equal(rawTxPartialSigned['complete'], False) #node1 only has one key, can't comp. sign the tx - rawTxPartialSigned2 = self.nodes[2].signrawtransaction(rawTx2, inputs) - self.log.info(rawTxPartialSigned2) + rawTxPartialSigned2 = self.nodes[2].signrawtransactionwithwallet(rawTx2, inputs) + self.log.debug(rawTxPartialSigned2) assert_equal(rawTxPartialSigned2['complete'], False) #node2 only has one key, can't comp. sign the tx rawTxComb = self.nodes[2].combinerawtransaction([rawTxPartialSigned1['hex'], rawTxPartialSigned2['hex']]) - self.log.info(rawTxComb) + self.log.debug(rawTxComb) self.nodes[2].sendrawtransaction(rawTxComb) rawTx2 = self.nodes[0].decoderawtransaction(rawTxComb) self.sync_all() @@ -273,7 +313,7 @@ class RawTransactionsTest(BitcoinTestFramework): encrawtx = "01000000010000000000000072c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000000ffffffff0100e1f505000000000000000000" decrawtx = self.nodes[0].decoderawtransaction(encrawtx, False) # decode as non-witness transaction assert_equal(decrawtx['vout'][0]['value'], Decimal('1.00000000')) - + # getrawtransaction tests # 1. valid parameters - only supply txid txHash = rawTx["hash"] diff --git a/test/functional/rpc_signrawtransaction.py b/test/functional/rpc_signrawtransaction.py index dd0fa6c02c..18829ef4b8 100755 --- a/test/functional/rpc_signrawtransaction.py +++ b/test/functional/rpc_signrawtransaction.py @@ -2,7 +2,7 @@ # Copyright (c) 2015-2017 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -"""Test transaction signing using the signrawtransaction RPC.""" +"""Test transaction signing using the signrawtransaction* RPCs.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -12,6 +12,7 @@ class SignRawTransactionsTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 + self.extra_args = [["-deprecatedrpc=signrawtransaction"]] def successful_signing_test(self): """Create and sign a valid raw transaction with one input. @@ -33,15 +34,18 @@ class SignRawTransactionsTest(BitcoinTestFramework): outputs = {'mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB': 0.1} rawTx = self.nodes[0].createrawtransaction(inputs, outputs) - rawTxSigned = self.nodes[0].signrawtransaction(rawTx, inputs, privKeys) + rawTxSigned = self.nodes[0].signrawtransactionwithkey(rawTx, privKeys, inputs) # 1) The transaction has a complete set of signatures - assert 'complete' in rawTxSigned - assert_equal(rawTxSigned['complete'], True) + assert rawTxSigned['complete'] # 2) No script verification error occurred assert 'errors' not in rawTxSigned + # Perform the same test on signrawtransaction + rawTxSigned2 = self.nodes[0].signrawtransaction(rawTx, inputs, privKeys) + assert_equal(rawTxSigned, rawTxSigned2) + def script_verification_error_test(self): """Create and sign a raw transaction with valid (vin 0), invalid (vin 1) and one missing (vin 2) input script. @@ -84,11 +88,10 @@ class SignRawTransactionsTest(BitcoinTestFramework): # Make sure decoderawtransaction throws if there is extra data assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, rawTx + "00") - rawTxSigned = self.nodes[0].signrawtransaction(rawTx, scripts, privKeys) + rawTxSigned = self.nodes[0].signrawtransactionwithkey(rawTx, privKeys, scripts) # 3) The transaction has no complete set of signatures - assert 'complete' in rawTxSigned - assert_equal(rawTxSigned['complete'], False) + assert not rawTxSigned['complete'] # 4) Two script verification errors occurred assert 'errors' in rawTxSigned @@ -109,14 +112,17 @@ class SignRawTransactionsTest(BitcoinTestFramework): assert_equal(rawTxSigned['errors'][1]['vout'], inputs[2]['vout']) assert not rawTxSigned['errors'][0]['witness'] + # Perform same test with signrawtransaction + rawTxSigned2 = self.nodes[0].signrawtransaction(rawTx, scripts, privKeys) + assert_equal(rawTxSigned, rawTxSigned2) + # Now test signing failure for transaction with input witnesses p2wpkh_raw_tx = "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f00000000494830450221008b9d1dc26ba6a9cb62127b02742fa9d754cd3bebf337f7a55d114c8e5cdd30be022040529b194ba3f9281a99f2b1c0a19c0489bc22ede944ccf4ecbab4cc618ef3ed01eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac000247304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee0121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000" - rawTxSigned = self.nodes[0].signrawtransaction(p2wpkh_raw_tx) + rawTxSigned = self.nodes[0].signrawtransactionwithwallet(p2wpkh_raw_tx) # 7) The transaction has no complete set of signatures - assert 'complete' in rawTxSigned - assert_equal(rawTxSigned['complete'], False) + assert not rawTxSigned['complete'] # 8) Two script verification errors occurred assert 'errors' in rawTxSigned @@ -134,6 +140,10 @@ class SignRawTransactionsTest(BitcoinTestFramework): assert_equal(rawTxSigned['errors'][1]['witness'], ["304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee01", "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357"]) assert not rawTxSigned['errors'][0]['witness'] + # Perform same test with signrawtransaction + rawTxSigned2 = self.nodes[0].signrawtransaction(p2wpkh_raw_tx) + assert_equal(rawTxSigned, rawTxSigned2) + def run_test(self): self.successful_signing_test() self.script_verification_error_test() diff --git a/test/functional/rpc_txoutproof.py b/test/functional/rpc_txoutproof.py index 50e0371fdf..c52a7397dc 100755 --- a/test/functional/rpc_txoutproof.py +++ b/test/functional/rpc_txoutproof.py @@ -34,9 +34,9 @@ class MerkleBlockTest(BitcoinTestFramework): node0utxos = self.nodes[0].listunspent(1) tx1 = self.nodes[0].createrawtransaction([node0utxos.pop()], {self.nodes[1].getnewaddress(): 49.99}) - txid1 = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransaction(tx1)["hex"]) + txid1 = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransactionwithwallet(tx1)["hex"]) tx2 = self.nodes[0].createrawtransaction([node0utxos.pop()], {self.nodes[1].getnewaddress(): 49.99}) - txid2 = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransaction(tx2)["hex"]) + txid2 = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransactionwithwallet(tx2)["hex"]) # This will raise an exception because the transaction is not yet in a block assert_raises_rpc_error(-5, "Transaction not yet in block", self.nodes[0].gettxoutproof, [txid1]) @@ -55,7 +55,7 @@ class MerkleBlockTest(BitcoinTestFramework): txin_spent = self.nodes[1].listunspent(1).pop() tx3 = self.nodes[1].createrawtransaction([txin_spent], {self.nodes[0].getnewaddress(): 49.98}) - txid3 = self.nodes[0].sendrawtransaction(self.nodes[1].signrawtransaction(tx3)["hex"]) + txid3 = self.nodes[0].sendrawtransaction(self.nodes[1].signrawtransactionwithwallet(tx3)["hex"]) self.nodes[0].generate(1) self.sync_all() diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py index 642ef98a27..43982cd09a 100644 --- a/test/functional/test_framework/blocktools.py +++ b/test/functional/test_framework/blocktools.py @@ -149,7 +149,7 @@ def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount): else: addr = key_to_p2sh_p2wpkh(pubkey) if encode_p2sh else key_to_p2wpkh(pubkey) if not encode_p2sh: - assert_equal(node.validateaddress(addr)['scriptPubKey'], witness_script(use_p2wsh, pubkey)) + assert_equal(node.getaddressinfo(addr)['scriptPubKey'], witness_script(use_p2wsh, pubkey)) return node.createrawtransaction([utxo], {addr: amount}) # Create a transaction spending a given utxo to a segwit output corresponding @@ -160,7 +160,7 @@ def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount): def send_to_witness(use_p2wsh, node, utxo, pubkey, encode_p2sh, amount, sign=True, insert_redeem_script=""): tx_to_witness = create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount) if (sign): - signed = node.signrawtransaction(tx_to_witness) + signed = node.signrawtransactionwithwallet(tx_to_witness) assert("errors" not in signed or len(["errors"]) == 0) return node.sendrawtransaction(signed["hex"]) else: diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py index a54a0299c7..e032be1337 100644..100755 --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -44,6 +44,11 @@ NODE_UNSUPPORTED_SERVICE_BIT_5 = (1 << 5) NODE_UNSUPPORTED_SERVICE_BIT_7 = (1 << 7) NODE_NETWORK_LIMITED = (1 << 10) +MSG_TX = 1 +MSG_BLOCK = 2 +MSG_WITNESS_FLAG = 1 << 30 +MSG_TYPE_MASK = 0xffffffff >> 2 + # Serialization/deserialization tools def sha256(s): return hashlib.new('sha256', s).digest() @@ -181,19 +186,24 @@ def ToHex(obj): class CAddress(): def __init__(self): + self.time = 0 self.nServices = 1 self.pchReserved = b"\x00" * 10 + b"\xff" * 2 self.ip = "0.0.0.0" self.port = 0 - def deserialize(self, f): + def deserialize(self, f, with_time=True): + if with_time: + self.time = struct.unpack("<i", f.read(4))[0] self.nServices = struct.unpack("<Q", f.read(8))[0] self.pchReserved = f.read(12) self.ip = socket.inet_ntoa(f.read(4)) self.port = struct.unpack(">H", f.read(2))[0] - def serialize(self): + def serialize(self, with_time=True): r = b"" + if with_time: + r += struct.pack("<i", self.time) r += struct.pack("<Q", self.nServices) r += self.pchReserved r += socket.inet_aton(self.ip) @@ -204,8 +214,6 @@ class CAddress(): return "CAddress(nServices=%i ip=%s port=%i)" % (self.nServices, self.ip, self.port) -MSG_WITNESS_FLAG = 1<<30 - class CInv(): typemap = { 0: "Error", @@ -853,11 +861,11 @@ class msg_version(): self.nServices = struct.unpack("<Q", f.read(8))[0] self.nTime = struct.unpack("<q", f.read(8))[0] self.addrTo = CAddress() - self.addrTo.deserialize(f) + self.addrTo.deserialize(f, False) if self.nVersion >= 106: self.addrFrom = CAddress() - self.addrFrom.deserialize(f) + self.addrFrom.deserialize(f, False) self.nNonce = struct.unpack("<Q", f.read(8))[0] self.strSubVer = deser_string(f) else: @@ -885,8 +893,8 @@ class msg_version(): r += struct.pack("<i", self.nVersion) r += struct.pack("<Q", self.nServices) r += struct.pack("<q", self.nTime) - r += self.addrTo.serialize() - r += self.addrFrom.serialize() + r += self.addrTo.serialize(False) + r += self.addrFrom.serialize(False) r += struct.pack("<Q", self.nNonce) r += ser_string(self.strSubVer) r += struct.pack("<i", self.nStartingHeight) diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py index 4d9661de37..99d0abc3f9 100755 --- a/test/functional/test_framework/mininode.py +++ b/test/functional/test_framework/mininode.py @@ -10,7 +10,9 @@ This python code was modified from ArtForz' public domain half-a-node, as found in the mini-node branch of http://github.com/jgarzik/pynode. P2PConnection: A low-level connection object to a node's P2P interface -P2PInterface: A high-level interface object for communicating to a node over P2P""" +P2PInterface: A high-level interface object for communicating to a node over P2P +P2PDataStore: A p2p interface class that keeps a store of transactions and blocks + and can respond correctly to getdata and getheaders messages""" import asyncore from collections import defaultdict from io import BytesIO @@ -86,7 +88,7 @@ class P2PConnection(asyncore.dispatcher): self.network = net self.disconnect = False - logger.info('Connecting to Bitcoin Node: %s:%d' % (self.dstaddr, self.dstport)) + logger.debug('Connecting to Bitcoin Node: %s:%d' % (self.dstaddr, self.dstport)) try: self.connect((dstaddr, dstport)) @@ -356,10 +358,22 @@ class P2PInterface(P2PConnection): wait_until(test_function, timeout=timeout, lock=mininode_lock) def wait_for_getdata(self, timeout=60): + """Waits for a getdata message. + + Receiving any getdata message will satisfy the predicate. the last_message["getdata"] + value must be explicitly cleared before calling this method, or this will return + immediately with success. TODO: change this method to take a hash value and only + return true if the correct block/tx has been requested.""" test_function = lambda: self.last_message.get("getdata") wait_until(test_function, timeout=timeout, lock=mininode_lock) def wait_for_getheaders(self, timeout=60): + """Waits for a getheaders message. + + Receiving any getheaders message will satisfy the predicate. the last_message["getheaders"] + value must be explicitly cleared before calling this method, or this will return + immediately with success. TODO: change this method to take a hash value and only + return true if the correct block header has been requested.""" test_function = lambda: self.last_message.get("getheaders") wait_until(test_function, timeout=timeout, lock=mininode_lock) @@ -440,3 +454,138 @@ def network_thread_join(timeout=10): for thread in network_threads: thread.join(timeout) assert not thread.is_alive() + +class P2PDataStore(P2PInterface): + """A P2P data store class. + + Keeps a block and transaction store and responds correctly to getdata and getheaders requests.""" + + def __init__(self): + super().__init__() + self.reject_code_received = None + self.reject_reason_received = None + # store of blocks. key is block hash, value is a CBlock object + self.block_store = {} + self.last_block_hash = '' + # store of txs. key is txid, value is a CTransaction object + self.tx_store = {} + self.getdata_requests = [] + + def on_getdata(self, message): + """Check for the tx/block in our stores and if found, reply with an inv message.""" + for inv in message.inv: + self.getdata_requests.append(inv.hash) + if (inv.type & MSG_TYPE_MASK) == MSG_TX and inv.hash in self.tx_store.keys(): + self.send_message(msg_tx(self.tx_store[inv.hash])) + elif (inv.type & MSG_TYPE_MASK) == MSG_BLOCK and inv.hash in self.block_store.keys(): + self.send_message(msg_block(self.block_store[inv.hash])) + else: + logger.debug('getdata message type {} received.'.format(hex(inv.type))) + + def on_getheaders(self, message): + """Search back through our block store for the locator, and reply with a headers message if found.""" + + locator, hash_stop = message.locator, message.hashstop + + # Assume that the most recent block added is the tip + if not self.block_store: + return + + headers_list = [self.block_store[self.last_block_hash]] + maxheaders = 2000 + while headers_list[-1].sha256 not in locator.vHave: + # Walk back through the block store, adding headers to headers_list + # as we go. + prev_block_hash = headers_list[-1].hashPrevBlock + if prev_block_hash in self.block_store: + prev_block_header = self.block_store[prev_block_hash] + headers_list.append(prev_block_header) + if prev_block_header.sha256 == hash_stop: + # if this is the hashstop header, stop here + break + else: + logger.debug('block hash {} not found in block store'.format(hex(prev_block_hash))) + break + + # Truncate the list if there are too many headers + headers_list = headers_list[:-maxheaders - 1:-1] + response = msg_headers(headers_list) + + if response is not None: + self.send_message(response) + + def on_reject(self, message): + """Store reject reason and code for testing.""" + self.reject_code_received = message.code + self.reject_reason_received = message.reason + + def send_blocks_and_test(self, blocks, rpc, success=True, request_block=True, reject_code=None, reject_reason=None, timeout=60): + """Send blocks to test node and test whether the tip advances. + + - add all blocks to our block_store + - send a headers message for the final block + - the on_getheaders handler will ensure that any getheaders are responded to + - if request_block is True: wait for getdata for each of the blocks. The on_getdata handler will + ensure that any getdata messages are responded to + - if success is True: assert that the node's tip advances to the most recent block + - if success is False: assert that the node's tip doesn't advance + - if reject_code and reject_reason are set: assert that the correct reject message is received""" + + with mininode_lock: + self.reject_code_received = None + self.reject_reason_received = None + + for block in blocks: + self.block_store[block.sha256] = block + self.last_block_hash = block.sha256 + + self.send_message(msg_headers([blocks[-1]])) + + if request_block: + wait_until(lambda: blocks[-1].sha256 in self.getdata_requests, timeout=timeout, lock=mininode_lock) + + if success: + wait_until(lambda: rpc.getbestblockhash() == blocks[-1].hash, timeout=timeout) + else: + assert rpc.getbestblockhash() != blocks[-1].hash + + if reject_code is not None: + wait_until(lambda: self.reject_code_received == reject_code, lock=mininode_lock) + if reject_reason is not None: + wait_until(lambda: self.reject_reason_received == reject_reason, lock=mininode_lock) + + def send_txs_and_test(self, txs, rpc, success=True, reject_code=None, reject_reason=None): + """Send txs to test node and test whether they're accepted to the mempool. + + - add all txs to our tx_store + - send tx messages for all txs + - if success is True: assert that the tx is accepted to the mempool + - if success is False: assert that the tx is not accepted to the mempool + - if reject_code and reject_reason are set: assert that the correct reject message is received.""" + + with mininode_lock: + self.reject_code_received = None + self.reject_reason_received = None + + for tx in txs: + self.tx_store[tx.sha256] = tx + + for tx in txs: + self.send_message(msg_tx(tx)) + + self.sync_with_ping() + + raw_mempool = rpc.getrawmempool() + if success: + # Check that all txs are now in the mempool + for tx in txs: + assert tx.hash in raw_mempool, "{} not found in mempool".format(tx.hash) + else: + # Check that none of the txs are now in the mempool + for tx in txs: + assert tx.hash not in raw_mempool, "{} tx found in mempool".format(tx.hash) + + if reject_code is not None: + wait_until(lambda: self.reject_code_received == reject_code, lock=mininode_lock) + if reject_reason is not None: + wait_until(lambda: self.reject_reason_received == reject_reason, lock=mininode_lock) diff --git a/test/functional/test_framework/netutil.py b/test/functional/test_framework/netutil.py index 96fe283347..36d1a2f856 100644 --- a/test/functional/test_framework/netutil.py +++ b/test/functional/test_framework/netutil.py @@ -9,7 +9,6 @@ Roughly based on http://voorloopnul.com/blog/a-python-netstat-in-less-than-100-l import sys import socket -import fcntl import struct import array import os @@ -90,6 +89,8 @@ def all_interfaces(): ''' Return all interfaces that are up ''' + import fcntl # Linux only, so only import when required + is_64bits = sys.maxsize > 2**32 struct_size = 40 if is_64bits else 32 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index dae8a4e569..6fe0b445da 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -526,11 +526,9 @@ class CScript(bytes): yield CScriptOp(opcode) def __repr__(self): - # For Python3 compatibility add b before strings so testcases don't - # need to change def _repr(o): if isinstance(o, bytes): - return b"x('%s')" % hexlify(o).decode('ascii') + return "x('%s')" % hexlify(o).decode('ascii') else: return repr(o) diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index f8d66def64..8efac9c475 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -24,8 +24,8 @@ from .util import ( check_json_precision, connect_nodes_bi, disconnect_nodes, + get_datadir_path, initialize_datadir, - log_filename, p2p_port, set_node_times, sync_blocks, @@ -63,6 +63,7 @@ class BitcoinTestFramework(): self.nodes = [] self.mocktime = 0 self.supports_cli = False + self.bind_to_localhost_only = True self.set_test_params() assert hasattr(self, "num_nodes"), "Test must set self.num_nodes in set_test_params()" @@ -99,7 +100,9 @@ class BitcoinTestFramework(): PortSeed.n = self.options.port_seed - os.environ['PATH'] = self.options.srcdir + ":" + self.options.srcdir + "/qt:" + os.environ['PATH'] + os.environ['PATH'] = self.options.srcdir + os.pathsep + \ + self.options.srcdir + os.path.sep + "qt" + os.pathsep + \ + os.environ['PATH'] check_json_precision() @@ -148,10 +151,11 @@ class BitcoinTestFramework(): self.log.info("Note: bitcoinds were not stopped and may still be running") if not self.options.nocleanup and not self.options.noshutdown and success != TestStatus.FAILED: - self.log.info("Cleaning up") - shutil.rmtree(self.options.tmpdir) + self.log.info("Cleaning up {} on exit".format(self.options.tmpdir)) + cleanup_tree_on_exit = True else: self.log.warning("Not cleaning up dir %s" % self.options.tmpdir) + cleanup_tree_on_exit = False if success == TestStatus.PASSED: self.log.info("Tests successful") @@ -164,6 +168,8 @@ class BitcoinTestFramework(): self.log.error("Hint: Call {} '{}' to consolidate all logs".format(os.path.normpath(os.path.dirname(os.path.realpath(__file__)) + "/../combine_logs.py"), self.options.tmpdir)) exit_code = TEST_EXIT_FAILED logging.shutdown() + if cleanup_tree_on_exit: + shutil.rmtree(self.options.tmpdir) sys.exit(exit_code) # Methods to override in subclass test scripts. @@ -210,15 +216,19 @@ class BitcoinTestFramework(): def add_nodes(self, num_nodes, extra_args=None, rpchost=None, timewait=None, binary=None): """Instantiate TestNode objects""" - + if self.bind_to_localhost_only: + extra_confs = [["bind=127.0.0.1"]] * num_nodes + else: + extra_confs = [[]] * num_nodes if extra_args is None: extra_args = [[]] * num_nodes if binary is None: binary = [None] * num_nodes + assert_equal(len(extra_confs), num_nodes) assert_equal(len(extra_args), num_nodes) assert_equal(len(binary), num_nodes) for i in range(num_nodes): - self.nodes.append(TestNode(i, self.options.tmpdir, extra_args[i], rpchost, timewait=timewait, binary=binary[i], stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir, use_cli=self.options.usecli)) + self.nodes.append(TestNode(i, self.options.tmpdir, rpchost=rpchost, timewait=timewait, binary=binary[i], stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir, extra_conf=extra_confs[i], extra_args=extra_args[i], use_cli=self.options.usecli)) def start_node(self, i, *args, **kwargs): """Start a bitcoind""" @@ -348,7 +358,7 @@ class BitcoinTestFramework(): ll = int(self.options.loglevel) if self.options.loglevel.isdigit() else self.options.loglevel.upper() ch.setLevel(ll) # Format logs the same as bitcoind's debug.log with microprecision (so log files can be concatenated and sorted) - formatter = logging.Formatter(fmt='%(asctime)s.%(msecs)03d000 %(name)s (%(levelname)s): %(message)s', datefmt='%Y-%m-%d %H:%M:%S') + formatter = logging.Formatter(fmt='%(asctime)s.%(msecs)03d000Z %(name)s (%(levelname)s): %(message)s', datefmt='%Y-%m-%dT%H:%M:%S') formatter.converter = time.gmtime fh.setFormatter(formatter) ch.setFormatter(formatter) @@ -372,7 +382,7 @@ class BitcoinTestFramework(): assert self.num_nodes <= MAX_NODES create_cache = False for i in range(MAX_NODES): - if not os.path.isdir(os.path.join(self.options.cachedir, 'node' + str(i))): + if not os.path.isdir(get_datadir_path(self.options.cachedir, i)): create_cache = True break @@ -381,16 +391,16 @@ class BitcoinTestFramework(): # find and delete old cache directories if any exist for i in range(MAX_NODES): - if os.path.isdir(os.path.join(self.options.cachedir, "node" + str(i))): - shutil.rmtree(os.path.join(self.options.cachedir, "node" + str(i))) + if os.path.isdir(get_datadir_path(self.options.cachedir, i)): + shutil.rmtree(get_datadir_path(self.options.cachedir, i)) # Create cache directories, run bitcoinds: for i in range(MAX_NODES): datadir = initialize_datadir(self.options.cachedir, i) - args = [os.getenv("BITCOIND", "bitcoind"), "-server", "-keypool=1", "-datadir=" + datadir, "-discover=0"] + args = [os.getenv("BITCOIND", "bitcoind"), "-datadir=" + datadir] if i > 0: args.append("-connect=127.0.0.1:" + str(p2p_port(0))) - self.nodes.append(TestNode(i, self.options.cachedir, extra_args=[], rpchost=None, timewait=None, binary=None, stderr=None, mocktime=self.mocktime, coverage_dir=None)) + self.nodes.append(TestNode(i, self.options.cachedir, extra_conf=["bind=127.0.0.1"], extra_args=[],rpchost=None, timewait=None, binary=None, stderr=None, mocktime=self.mocktime, coverage_dir=None)) self.nodes[i].args = args self.start_node(i) @@ -420,15 +430,18 @@ class BitcoinTestFramework(): self.stop_nodes() self.nodes = [] self.disable_mocktime() + + def cache_path(n, *paths): + return os.path.join(get_datadir_path(self.options.cachedir, n), "regtest", *paths) + for i in range(MAX_NODES): - os.remove(log_filename(self.options.cachedir, i, "debug.log")) - os.remove(log_filename(self.options.cachedir, i, "wallets/db.log")) - os.remove(log_filename(self.options.cachedir, i, "peers.dat")) - os.remove(log_filename(self.options.cachedir, i, "fee_estimates.dat")) + for entry in os.listdir(cache_path(i)): + if entry not in ['wallets', 'chainstate', 'blocks']: + os.remove(cache_path(i, entry)) for i in range(self.num_nodes): - from_dir = os.path.join(self.options.cachedir, "node" + str(i)) - to_dir = os.path.join(self.options.tmpdir, "node" + str(i)) + from_dir = get_datadir_path(self.options.cachedir, i) + to_dir = get_datadir_path(self.options.tmpdir, i) shutil.copytree(from_dir, to_dir) initialize_datadir(self.options.tmpdir, i) # Overwrite port/rpcport in bitcoin.conf diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 1054e6d028..86e44e4c97 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -16,6 +16,7 @@ import time from .authproxy import JSONRPCException from .util import ( + append_config, assert_equal, get_rpc_proxy, rpc_url, @@ -42,7 +43,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, dirname, extra_args, rpchost, timewait, binary, stderr, mocktime, coverage_dir, use_cli=False): + def __init__(self, i, dirname, rpchost, timewait, binary, stderr, mocktime, coverage_dir, extra_conf=None, extra_args=None, use_cli=False): self.index = i self.datadir = os.path.join(dirname, "node" + str(i)) self.rpchost = rpchost @@ -57,9 +58,13 @@ class TestNode(): self.binary = binary self.stderr = stderr self.coverage_dir = coverage_dir - # Most callers will just need to add extra args to the standard list below. For those callers that need more flexibity, they can just set the args property directly. + if extra_conf != None: + append_config(dirname, i, extra_conf) + # Most callers will just need to add extra args to the standard list below. + # For those callers that need more flexibity, 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.args = [self.binary, "-datadir=" + self.datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-logtimemicros", "-debug", "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(mocktime), "-uacomment=testnode%d" % i] + self.args = [self.binary, "-datadir=" + self.datadir, "-logtimemicros", "-debug", "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(mocktime), "-uacomment=testnode%d" % i] self.cli = TestNodeCLI(os.getenv("BITCOINCLI", "bitcoin-cli"), self.datadir) self.use_cli = use_cli diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index 7fdc171332..7be695550b 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -26,7 +26,7 @@ logger = logging.getLogger("TestFramework.utils") def assert_fee_amount(fee, tx_size, fee_per_kB): """Assert the fee was in range""" - target_fee = tx_size * fee_per_kB / 1000 + target_fee = round(tx_size * fee_per_kB / 1000, 8) if fee < target_fee: raise AssertionError("Fee of %s BTC too low! (Should be %s BTC)" % (str(fee), str(target_fee))) # allow the wallet's estimation to be at most 2 bytes off @@ -291,12 +291,21 @@ def initialize_datadir(dirname, n): f.write("regtest=1\n") f.write("port=" + str(p2p_port(n)) + "\n") f.write("rpcport=" + str(rpc_port(n)) + "\n") + f.write("server=1\n") + f.write("keypool=1\n") + f.write("discover=0\n") f.write("listenonion=0\n") return datadir def get_datadir_path(dirname, n): return os.path.join(dirname, "node" + str(n)) +def append_config(dirname, n, options): + datadir = get_datadir_path(dirname, n) + with open(os.path.join(datadir, "bitcoin.conf"), 'a', encoding='utf8') as f: + for option in options: + f.write(option + "\n") + def get_auth_cookie(datadir): user = None password = None @@ -319,9 +328,6 @@ def get_auth_cookie(datadir): raise ValueError("No RPC credentials") return user, password -def log_filename(dirname, n_node, logname): - return os.path.join(dirname, "node" + str(n_node), "regtest", logname) - def get_bip9_status(node, key): info = node.getblockchaininfo() return info['bip9_softforks'][key] @@ -361,54 +367,29 @@ def sync_blocks(rpc_connections, *, wait=1, timeout=60): one node already synced to the latest, stable tip, otherwise there's a chance it might return before all nodes are stably synced. """ - # Use getblockcount() instead of waitforblockheight() to determine the - # initial max height because the two RPCs look at different internal global - # variables (chainActive vs latestBlock) and the former gets updated - # earlier. - maxheight = max(x.getblockcount() for x in rpc_connections) - start_time = cur_time = time.time() - while cur_time <= start_time + timeout: - tips = [r.waitforblockheight(maxheight, int(wait * 1000)) for r in rpc_connections] - if all(t["height"] == maxheight for t in tips): - if all(t["hash"] == tips[0]["hash"] for t in tips): - return - raise AssertionError("Block sync failed, mismatched block hashes:{}".format( - "".join("\n {!r}".format(tip) for tip in tips))) - cur_time = time.time() - raise AssertionError("Block sync to height {} timed out:{}".format( - maxheight, "".join("\n {!r}".format(tip) for tip in tips))) - -def sync_chain(rpc_connections, *, wait=1, timeout=60): - """ - Wait until everybody has the same best block - """ - while timeout > 0: + stop_time = time.time() + timeout + while time.time() <= stop_time: best_hash = [x.getbestblockhash() for x in rpc_connections] - if best_hash == [best_hash[0]] * len(best_hash): + if best_hash.count(best_hash[0]) == len(rpc_connections): return time.sleep(wait) - timeout -= wait - raise AssertionError("Chain sync failed: Best block hashes don't match") + raise AssertionError("Block sync timed out:{}".format("".join("\n {!r}".format(b) for b in best_hash))) def sync_mempools(rpc_connections, *, wait=1, timeout=60, flush_scheduler=True): """ Wait until everybody has the same transactions in their memory pools """ - while timeout > 0: - pool = set(rpc_connections[0].getrawmempool()) - num_match = 1 - for i in range(1, len(rpc_connections)): - if set(rpc_connections[i].getrawmempool()) == pool: - num_match = num_match + 1 - if num_match == len(rpc_connections): + stop_time = time.time() + timeout + while time.time() <= stop_time: + pool = [set(r.getrawmempool()) for r in rpc_connections] + if pool.count(pool[0]) == len(rpc_connections): if flush_scheduler: for r in rpc_connections: r.syncwithvalidationinterfacequeue() return time.sleep(wait) - timeout -= wait - raise AssertionError("Mempool sync failed") + raise AssertionError("Mempool sync timed out:{}".format("".join("\n {!r}".format(m) for m in pool))) # Transaction/Block functions ############################# @@ -472,7 +453,7 @@ def random_transaction(nodes, amount, min_fee, fee_increment, fee_variants): outputs[to_node.getnewaddress()] = float(amount) rawtx = from_node.createrawtransaction(inputs, outputs) - signresult = from_node.signrawtransaction(rawtx) + signresult = from_node.signrawtransactionwithwallet(rawtx) txid = from_node.sendrawtransaction(signresult["hex"], True) return (txid, signresult["hex"], fee) @@ -499,7 +480,7 @@ def create_confirmed_utxos(fee, node, count): outputs[addr1] = satoshi_round(send_value / 2) outputs[addr2] = satoshi_round(send_value / 2) raw_tx = node.createrawtransaction(inputs, outputs) - signed_tx = node.signrawtransaction(raw_tx)["hex"] + signed_tx = node.signrawtransactionwithwallet(raw_tx)["hex"] node.sendrawtransaction(signed_tx) while (node.getmempoolinfo()['size'] > 0): @@ -533,7 +514,7 @@ def create_tx(node, coinbase, to_address, amount): inputs = [{"txid": coinbase, "vout": 0}] outputs = {to_address: amount} rawtx = node.createrawtransaction(inputs, outputs) - signresult = node.signrawtransaction(rawtx) + signresult = node.signrawtransactionwithwallet(rawtx) assert_equal(signresult["complete"], True) return signresult["hex"] @@ -552,7 +533,7 @@ def create_lots_of_big_transactions(node, txouts, utxos, num, fee): newtx = rawtx[0:92] newtx = newtx + txouts newtx = newtx + rawtx[94:] - signresult = node.signrawtransaction(newtx, None, None, "NONE") + signresult = node.signrawtransactionwithwallet(newtx, None, "NONE") txid = node.sendrawtransaction(signresult["hex"], True) txids.append(txid) return txids diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index c670878d68..082191098e 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -126,6 +126,7 @@ BASE_SCRIPTS= [ 'feature_cltv.py', 'rpc_uptime.py', 'wallet_resendwallettransactions.py', + 'wallet_fallbackfee.py', 'feature_minchainwork.py', 'p2p_fingerprint.py', 'feature_uacomment.py', @@ -268,7 +269,7 @@ def main(): if args.help: # Print help for test_runner.py, then print help of the first script (with args removed) and exit. parser.print_help() - subprocess.check_call([(config["environment"]["SRCDIR"] + '/test/functional/' + test_list[0].split()[0])] + ['-h']) + subprocess.check_call([sys.executable, os.path.join(config["environment"]["SRCDIR"], 'test', 'functional', test_list[0].split()[0]), '-h']) sys.exit(0) check_script_list(config["environment"]["SRCDIR"]) @@ -312,7 +313,7 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove if len(test_list) > 1 and jobs > 1: # Populate cache try: - subprocess.check_output([tests_dir + 'create_cache.py'] + flags + ["--tmpdir=%s/cache" % tmpdir]) + subprocess.check_output([sys.executable, tests_dir + 'create_cache.py'] + flags + ["--tmpdir=%s/cache" % tmpdir]) except subprocess.CalledProcessError as e: sys.stdout.buffer.write(e.output) raise @@ -342,7 +343,7 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove print('\n============') print('{}Combined log for {}:{}'.format(BOLD[1], testdir, BOLD[0])) print('============\n') - combined_logs, _ = subprocess.Popen([os.path.join(tests_dir, 'combine_logs.py'), '-c', testdir], universal_newlines=True, stdout=subprocess.PIPE).communicate() + combined_logs, _ = subprocess.Popen([sys.executable, os.path.join(tests_dir, 'combine_logs.py'), '-c', testdir], universal_newlines=True, stdout=subprocess.PIPE).communicate() print("\n".join(deque(combined_logs.splitlines(), combined_logs_len))) print_results(test_results, max_len_name, (int(time.time() - time0))) @@ -412,7 +413,7 @@ class TestHandler: tmpdir_arg = ["--tmpdir={}".format(testdir)] self.jobs.append((t, time.time(), - subprocess.Popen([self.tests_dir + test_argv[0]] + test_argv[1:] + self.flags + portseed_arg + tmpdir_arg, + subprocess.Popen([sys.executable, self.tests_dir + test_argv[0]] + test_argv[1:] + self.flags + portseed_arg + tmpdir_arg, universal_newlines=True, stdout=log_stdout, stderr=log_stderr), diff --git a/test/functional/wallet_abandonconflict.py b/test/functional/wallet_abandonconflict.py index 8fb860cd7e..7e0635d80f 100755 --- a/test/functional/wallet_abandonconflict.py +++ b/test/functional/wallet_abandonconflict.py @@ -55,7 +55,7 @@ class AbandonConflictTest(BitcoinTestFramework): outputs[self.nodes[0].getnewaddress()] = Decimal("14.99998") outputs[self.nodes[1].getnewaddress()] = Decimal("5") - signed = self.nodes[0].signrawtransaction(self.nodes[0].createrawtransaction(inputs, outputs)) + signed = self.nodes[0].signrawtransactionwithwallet(self.nodes[0].createrawtransaction(inputs, outputs)) txAB1 = self.nodes[0].sendrawtransaction(signed["hex"]) # Identify the 14.99998btc output @@ -67,7 +67,7 @@ class AbandonConflictTest(BitcoinTestFramework): inputs.append({"txid":txC, "vout":nC}) outputs = {} outputs[self.nodes[0].getnewaddress()] = Decimal("24.9996") - signed2 = self.nodes[0].signrawtransaction(self.nodes[0].createrawtransaction(inputs, outputs)) + signed2 = self.nodes[0].signrawtransactionwithwallet(self.nodes[0].createrawtransaction(inputs, outputs)) txABC2 = self.nodes[0].sendrawtransaction(signed2["hex"]) # In mempool txs from self should increase balance from change @@ -138,7 +138,7 @@ class AbandonConflictTest(BitcoinTestFramework): outputs = {} outputs[self.nodes[1].getnewaddress()] = Decimal("9.9999") tx = self.nodes[0].createrawtransaction(inputs, outputs) - signed = self.nodes[0].signrawtransaction(tx) + signed = self.nodes[0].signrawtransactionwithwallet(tx) self.nodes[1].sendrawtransaction(signed["hex"]) self.nodes[1].generate(1) diff --git a/test/functional/wallet_address_types.py b/test/functional/wallet_address_types.py index 38a3425214..5d2428e6ef 100755 --- a/test/functional/wallet_address_types.py +++ b/test/functional/wallet_address_types.py @@ -93,8 +93,8 @@ class AddressTypeTest(BitcoinTestFramework): def test_address(self, node, address, multisig, typ): """Run sanity checks on an address.""" - info = self.nodes[node].validateaddress(address) - assert(info['isvalid']) + info = self.nodes[node].getaddressinfo(address) + assert(self.nodes[node].validateaddress(address)['isvalid']) if not multisig and typ == 'legacy': # P2PKH assert(not info['isscript']) diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py index a90dbc8adf..f686cb6ea5 100755 --- a/test/functional/wallet_basic.py +++ b/test/functional/wallet_basic.py @@ -66,7 +66,7 @@ class WalletTest(BitcoinTestFramework): assert_equal(txout['value'], 50) txout = self.nodes[0].gettxout(txid=confirmed_txid, n=confirmed_index, include_mempool=True) assert_equal(txout['value'], 50) - + # Send 21 BTC from 0 to 2 using sendtoaddress call. # Locked memory should use at least 32 bytes to sign each transaction self.log.info("test getmemoryinfo") @@ -140,7 +140,7 @@ class WalletTest(BitcoinTestFramework): inputs.append({ "txid" : utxo["txid"], "vout" : utxo["vout"]}) outputs[self.nodes[2].getnewaddress("from1")] = utxo["amount"] - 3 raw_tx = self.nodes[0].createrawtransaction(inputs, outputs) - txns_to_send.append(self.nodes[0].signrawtransaction(raw_tx)) + txns_to_send.append(self.nodes[0].signrawtransactionwithwallet(raw_tx)) # Have node 1 (miner) send the transactions self.nodes[1].sendrawtransaction(txns_to_send[0]["hex"], True) @@ -225,7 +225,7 @@ class WalletTest(BitcoinTestFramework): rawTx = self.nodes[1].createrawtransaction(inputs, outputs).replace("c0833842", "00000000") #replace 11.11 with 0.0 (int32) decRawTx = self.nodes[1].decoderawtransaction(rawTx) - signedRawTx = self.nodes[1].signrawtransaction(rawTx) + signedRawTx = self.nodes[1].signrawtransactionwithwallet(rawTx) decRawTx = self.nodes[1].decoderawtransaction(signedRawTx['hex']) zeroValueTxid= decRawTx['txid'] self.nodes[1].sendrawtransaction(signedRawTx['hex']) @@ -317,7 +317,7 @@ class WalletTest(BitcoinTestFramework): self.nodes[1].importaddress(address_to_import) # 3. Validate that the imported address is watch-only on node1 - assert(self.nodes[1].validateaddress(address_to_import)["iswatchonly"]) + assert(self.nodes[1].getaddressinfo(address_to_import)["iswatchonly"]) # 4. Check that the unspents after import are not spendable assert_array_result(self.nodes[1].listunspent(), @@ -400,7 +400,7 @@ class WalletTest(BitcoinTestFramework): node0_balance = self.nodes[0].getbalance() # Split into two chains rawtx = self.nodes[0].createrawtransaction([{"txid":singletxid, "vout":0}], {chain_addrs[0]:node0_balance/2-Decimal('0.01'), chain_addrs[1]:node0_balance/2-Decimal('0.01')}) - signedtx = self.nodes[0].signrawtransaction(rawtx) + signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx) singletxid = self.nodes[0].sendrawtransaction(signedtx["hex"]) self.nodes[0].generate(1) @@ -442,5 +442,14 @@ class WalletTest(BitcoinTestFramework): # Verify nothing new in wallet assert_equal(total_txs, len(self.nodes[0].listtransactions("*",99999))) + # Test getaddressinfo. Note that these addresses are taken from disablewallet.py + assert_raises_rpc_error(-5, "Invalid address", self.nodes[0].getaddressinfo, "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy") + address_info = self.nodes[0].getaddressinfo("mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ") + assert_equal(address_info['address'], "mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ") + assert_equal(address_info["scriptPubKey"], "76a9144e3854046c7bd1594ac904e4793b6a45b36dea0988ac") + assert not address_info["ismine"] + assert not address_info["iswatchonly"] + assert not address_info["isscript"] + if __name__ == '__main__': WalletTest().main() diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py index f621d41b4e..3e496248fd 100755 --- a/test/functional/wallet_bumpfee.py +++ b/test/functional/wallet_bumpfee.py @@ -104,7 +104,7 @@ def test_segwit_bumpfee_succeeds(rbf_node, dest_address): # which spends it, and make sure bumpfee can be called on it. segwit_in = next(u for u in rbf_node.listunspent() if u["amount"] == Decimal("0.001")) - segwit_out = rbf_node.validateaddress(rbf_node.getnewaddress()) + segwit_out = rbf_node.getaddressinfo(rbf_node.getnewaddress()) rbf_node.addwitnessaddress(segwit_out["address"]) segwitid = send_to_witness( use_p2wsh=False, @@ -121,7 +121,7 @@ def test_segwit_bumpfee_succeeds(rbf_node, dest_address): "sequence": BIP125_SEQUENCE_NUMBER }], {dest_address: Decimal("0.0005"), rbf_node.getrawchangeaddress(): Decimal("0.0003")}) - rbfsigned = rbf_node.signrawtransaction(rbfraw) + rbfsigned = rbf_node.signrawtransactionwithwallet(rbfraw) rbfid = rbf_node.sendrawtransaction(rbfsigned["hex"]) assert rbfid in rbf_node.getrawmempool() @@ -150,8 +150,8 @@ def test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address): } for utxo in utxos] output_val = sum(utxo["amount"] for utxo in utxos) - Decimal("0.001") rawtx = rbf_node.createrawtransaction(inputs, {dest_address: output_val}) - signedtx = rbf_node.signrawtransaction(rawtx) - signedtx = peer_node.signrawtransaction(signedtx["hex"]) + signedtx = rbf_node.signrawtransactionwithwallet(rawtx) + signedtx = peer_node.signrawtransactionwithwallet(signedtx["hex"]) rbfid = rbf_node.sendrawtransaction(signedtx["hex"]) assert_raises_rpc_error(-4, "Transaction contains inputs that don't belong to this wallet", rbf_node.bumpfee, rbfid) @@ -162,7 +162,7 @@ def test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address) # parent is send-to-self, so we don't have to check which output is change when creating the child tx parent_id = spend_one_input(rbf_node, rbf_node_address) tx = rbf_node.createrawtransaction([{"txid": parent_id, "vout": 0}], {dest_address: 0.00020000}) - tx = rbf_node.signrawtransaction(tx) + tx = rbf_node.signrawtransactionwithwallet(tx) rbf_node.sendrawtransaction(tx["hex"]) assert_raises_rpc_error(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id) @@ -277,7 +277,7 @@ def spend_one_input(node, dest_address): rawtx = node.createrawtransaction( [tx_input], {dest_address: Decimal("0.00050000"), node.getrawchangeaddress(): Decimal("0.00049000")}) - signedtx = node.signrawtransaction(rawtx) + signedtx = node.signrawtransactionwithwallet(rawtx) txid = node.sendrawtransaction(signedtx["hex"]) return txid diff --git a/test/functional/wallet_dump.py b/test/functional/wallet_dump.py index 5e943d048d..997f67ec7e 100755 --- a/test/functional/wallet_dump.py +++ b/test/functional/wallet_dump.py @@ -7,7 +7,10 @@ import os from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import (assert_equal, assert_raises_rpc_error) +from test_framework.util import ( + assert_equal, + assert_raises_rpc_error, +) def read_dump(file_name, addrs, script_addrs, hd_master_addr_old): @@ -49,7 +52,7 @@ def read_dump(file_name, addrs, script_addrs, hd_master_addr_old): # count key types for addrObj in addrs: if addrObj['address'] == addr.split(",")[0] and addrObj['hdkeypath'] == keypath and keytype == "label=": - # a labled entry in the wallet should contain both a native address + # a labeled entry in the wallet should contain both a native address # and the p2sh-p2wpkh address that was added at wallet setup if len(addr.split(",")) == 2: addr_list = addr.split(",") @@ -84,11 +87,12 @@ class WalletDumpTest(BitcoinTestFramework): # longer than the default 30 seconds due to an expensive # CWallet::TopUpKeyPool call, and the encryptwallet RPC made later in # the test often takes even longer. - self.add_nodes(self.num_nodes, self.extra_args, timewait=60) + self.add_nodes(self.num_nodes, extra_args=self.extra_args, timewait=60) self.start_nodes() def run_test (self): - tmpdir = self.options.tmpdir + wallet_unenc_dump = os.path.join(self.nodes[0].datadir, "wallet.unencrypted.dump") + wallet_enc_dump = os.path.join(self.nodes[0].datadir, "wallet.encrypted.dump") # generate 20 addresses to compare against the dump # but since we add a p2sh-p2wpkh address for the first pubkey in the @@ -97,7 +101,7 @@ class WalletDumpTest(BitcoinTestFramework): addrs = [] for i in range(0,test_addr_count): addr = self.nodes[0].getnewaddress() - vaddr= self.nodes[0].validateaddress(addr) #required to get hd keypath + vaddr= self.nodes[0].getaddressinfo(addr) #required to get hd keypath addrs.append(vaddr) # Should be a no-op: self.nodes[0].keypoolrefill() @@ -108,11 +112,11 @@ class WalletDumpTest(BitcoinTestFramework): script_addrs = [witness_addr, multisig_addr] # dump unencrypted wallet - result = self.nodes[0].dumpwallet(tmpdir + "/node0/wallet.unencrypted.dump") - assert_equal(result['filename'], os.path.abspath(tmpdir + "/node0/wallet.unencrypted.dump")) + result = self.nodes[0].dumpwallet(wallet_unenc_dump) + assert_equal(result['filename'], wallet_unenc_dump) found_addr, found_script_addr, found_addr_chg, found_addr_rsv, hd_master_addr_unenc, witness_addr_ret = \ - read_dump(tmpdir + "/node0/wallet.unencrypted.dump", addrs, script_addrs, None) + read_dump(wallet_unenc_dump, addrs, script_addrs, None) assert_equal(found_addr, test_addr_count) # all keys must be in the dump assert_equal(found_script_addr, 2) # all scripts must be in the dump assert_equal(found_addr_chg, 50) # 50 blocks where mined @@ -125,10 +129,10 @@ class WalletDumpTest(BitcoinTestFramework): self.nodes[0].walletpassphrase('test', 10) # Should be a no-op: self.nodes[0].keypoolrefill() - self.nodes[0].dumpwallet(tmpdir + "/node0/wallet.encrypted.dump") + self.nodes[0].dumpwallet(wallet_enc_dump) found_addr, found_script_addr, found_addr_chg, found_addr_rsv, _, witness_addr_ret = \ - read_dump(tmpdir + "/node0/wallet.encrypted.dump", addrs, script_addrs, hd_master_addr_unenc) + read_dump(wallet_enc_dump, addrs, script_addrs, hd_master_addr_unenc) assert_equal(found_addr, test_addr_count) assert_equal(found_script_addr, 2) assert_equal(found_addr_chg, 90*2 + 50) # old reserve keys are marked as change now @@ -136,21 +140,21 @@ class WalletDumpTest(BitcoinTestFramework): assert_equal(witness_addr_ret, witness_addr) # Overwriting should fail - assert_raises_rpc_error(-8, "already exists", self.nodes[0].dumpwallet, tmpdir + "/node0/wallet.unencrypted.dump") + assert_raises_rpc_error(-8, "already exists", lambda: self.nodes[0].dumpwallet(wallet_enc_dump)) # Restart node with new wallet, and test importwallet self.stop_node(0) self.start_node(0, ['-wallet=w2']) # Make sure the address is not IsMine before import - result = self.nodes[0].validateaddress(multisig_addr) + result = self.nodes[0].getaddressinfo(multisig_addr) assert(result['ismine'] == False) - self.nodes[0].importwallet(os.path.abspath(tmpdir + "/node0/wallet.unencrypted.dump")) + self.nodes[0].importwallet(wallet_unenc_dump) # Now check IsMine is true - result = self.nodes[0].validateaddress(multisig_addr) + result = self.nodes[0].getaddressinfo(multisig_addr) assert(result['ismine'] == True) if __name__ == '__main__': - WalletDumpTest().main () + WalletDumpTest().main() diff --git a/test/functional/wallet_fallbackfee.py b/test/functional/wallet_fallbackfee.py new file mode 100755 index 0000000000..e9cd052344 --- /dev/null +++ b/test/functional/wallet_fallbackfee.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test wallet replace-by-fee capabilities in conjunction with the fallbackfee.""" +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * + +class WalletRBFTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + self.setup_clean_chain = True + + def run_test(self): + self.nodes[0].generate(101) + + # sending a transaction without fee estimations must be possible by default on regtest + self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1) + + # test sending a tx with disabled fallback fee (must fail) + self.restart_node(0, extra_args=["-fallbackfee=0"]) + assert_raises_rpc_error(-4, "Fee estimation failed", lambda: self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)) + assert_raises_rpc_error(-4, "Fee estimation failed", lambda: self.nodes[0].fundrawtransaction(self.nodes[0].createrawtransaction([], {self.nodes[0].getnewaddress(): 1}))) + assert_raises_rpc_error(-4, "Fee estimation failed", lambda: self.nodes[0].sendfrom("", self.nodes[0].getnewaddress(), 1)) + assert_raises_rpc_error(-6, "Fee estimation failed", lambda: self.nodes[0].sendmany("", {self.nodes[0].getnewaddress(): 1})) + +if __name__ == '__main__': + WalletRBFTest().main() diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py index 9f0e9acb47..91f77dd5ba 100755 --- a/test/functional/wallet_hd.py +++ b/test/functional/wallet_hd.py @@ -33,7 +33,7 @@ class WalletHDTest(BitcoinTestFramework): # create an internal key change_addr = self.nodes[1].getrawchangeaddress() - change_addrV= self.nodes[1].validateaddress(change_addr) + change_addrV= self.nodes[1].getaddressinfo(change_addr) assert_equal(change_addrV["hdkeypath"], "m/0'/1'/0'") #first internal child key # Import a non-HD private key in the HD wallet @@ -51,7 +51,7 @@ class WalletHDTest(BitcoinTestFramework): num_hd_adds = 300 for i in range(num_hd_adds): hd_add = self.nodes[1].getnewaddress() - hd_info = self.nodes[1].validateaddress(hd_add) + hd_info = self.nodes[1].getaddressinfo(hd_add) assert_equal(hd_info["hdkeypath"], "m/0'/0'/"+str(i)+"'") assert_equal(hd_info["hdmasterkeyid"], masterkeyid) self.nodes[0].sendtoaddress(hd_add, 1) @@ -61,7 +61,7 @@ class WalletHDTest(BitcoinTestFramework): # create an internal key (again) change_addr = self.nodes[1].getrawchangeaddress() - change_addrV= self.nodes[1].validateaddress(change_addr) + change_addrV= self.nodes[1].getaddressinfo(change_addr) assert_equal(change_addrV["hdkeypath"], "m/0'/1'/1'") #second internal child key self.sync_all() @@ -80,7 +80,7 @@ class WalletHDTest(BitcoinTestFramework): hd_add_2 = None for _ in range(num_hd_adds): hd_add_2 = self.nodes[1].getnewaddress() - hd_info_2 = self.nodes[1].validateaddress(hd_add_2) + hd_info_2 = self.nodes[1].getaddressinfo(hd_add_2) assert_equal(hd_info_2["hdkeypath"], "m/0'/0'/"+str(_)+"'") assert_equal(hd_info_2["hdmasterkeyid"], masterkeyid) assert_equal(hd_add, hd_add_2) @@ -114,7 +114,7 @@ class WalletHDTest(BitcoinTestFramework): keypath = "" for out in outs: if out['value'] != 1: - keypath = self.nodes[1].validateaddress(out['scriptPubKey']['addresses'][0])['hdkeypath'] + keypath = self.nodes[1].getaddressinfo(out['scriptPubKey']['addresses'][0])['hdkeypath'] assert_equal(keypath[0:7], "m/0'/1'") diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py index d193a99d5b..bfd4638481 100755 --- a/test/functional/wallet_import_rescan.py +++ b/test/functional/wallet_import_rescan.py @@ -124,7 +124,7 @@ class ImportRescanTest(BitcoinTestFramework): if import_node.prune: extra_args[i] += ["-prune=1"] - self.add_nodes(self.num_nodes, extra_args) + self.add_nodes(self.num_nodes, extra_args=extra_args) self.start_nodes() for i in range(1, self.num_nodes): connect_nodes(self.nodes[i], 0) @@ -134,7 +134,7 @@ class ImportRescanTest(BitcoinTestFramework): # each possible type of wallet import RPC. for i, variant in enumerate(IMPORT_VARIANTS): variant.label = "label {} {}".format(i, variant) - variant.address = self.nodes[1].validateaddress(self.nodes[1].getnewaddress(variant.label)) + variant.address = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress(variant.label)) variant.key = self.nodes[1].dumpprivkey(variant.address["address"]) variant.initial_amount = 10 - (i + 1) / 4.0 variant.initial_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.initial_amount) diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py index be9be83839..56ebc2622a 100755 --- a/test/functional/wallet_importmulti.py +++ b/test/functional/wallet_importmulti.py @@ -21,7 +21,7 @@ class ImportMultiTest (BitcoinTestFramework): self.nodes[1].generate(1) timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime'] - node0_address1 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) + node0_address1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) #Check only one address assert_equal(node0_address1['ismine'], True) @@ -30,7 +30,7 @@ class ImportMultiTest (BitcoinTestFramework): assert_equal(self.nodes[1].getblockcount(),1) #Address Test - before import - address_info = self.nodes[1].validateaddress(node0_address1['address']) + address_info = self.nodes[1].getaddressinfo(node0_address1['address']) assert_equal(address_info['iswatchonly'], False) assert_equal(address_info['ismine'], False) @@ -39,7 +39,7 @@ class ImportMultiTest (BitcoinTestFramework): # Bitcoin Address self.log.info("Should import an address") - address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) + address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) result = self.nodes[1].importmulti([{ "scriptPubKey": { "address": address['address'] @@ -47,7 +47,7 @@ class ImportMultiTest (BitcoinTestFramework): "timestamp": "now", }]) assert_equal(result[0]['success'], True) - address_assert = self.nodes[1].validateaddress(address['address']) + address_assert = self.nodes[1].getaddressinfo(address['address']) assert_equal(address_assert['iswatchonly'], True) assert_equal(address_assert['ismine'], False) assert_equal(address_assert['timestamp'], timestamp) @@ -67,21 +67,21 @@ class ImportMultiTest (BitcoinTestFramework): # ScriptPubKey + internal self.log.info("Should import a scriptPubKey with internal flag") - address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) + address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) result = self.nodes[1].importmulti([{ "scriptPubKey": address['scriptPubKey'], "timestamp": "now", "internal": True }]) assert_equal(result[0]['success'], True) - address_assert = self.nodes[1].validateaddress(address['address']) + address_assert = self.nodes[1].getaddressinfo(address['address']) assert_equal(address_assert['iswatchonly'], True) assert_equal(address_assert['ismine'], False) assert_equal(address_assert['timestamp'], timestamp) # ScriptPubKey + !internal self.log.info("Should not import a scriptPubKey without internal flag") - address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) + address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) result = self.nodes[1].importmulti([{ "scriptPubKey": address['scriptPubKey'], "timestamp": "now", @@ -89,7 +89,7 @@ class ImportMultiTest (BitcoinTestFramework): assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -8) assert_equal(result[0]['error']['message'], 'Internal must be set for hex scriptPubKey') - address_assert = self.nodes[1].validateaddress(address['address']) + address_assert = self.nodes[1].getaddressinfo(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) assert_equal('timestamp' in address_assert, False) @@ -97,7 +97,7 @@ class ImportMultiTest (BitcoinTestFramework): # Address + Public key + !Internal self.log.info("Should import an address with public key") - address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) + address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) result = self.nodes[1].importmulti([{ "scriptPubKey": { "address": address['address'] @@ -106,7 +106,7 @@ class ImportMultiTest (BitcoinTestFramework): "pubkeys": [ address['pubkey'] ] }]) assert_equal(result[0]['success'], True) - address_assert = self.nodes[1].validateaddress(address['address']) + address_assert = self.nodes[1].getaddressinfo(address['address']) assert_equal(address_assert['iswatchonly'], True) assert_equal(address_assert['ismine'], False) assert_equal(address_assert['timestamp'], timestamp) @@ -114,7 +114,7 @@ class ImportMultiTest (BitcoinTestFramework): # ScriptPubKey + Public key + internal self.log.info("Should import a scriptPubKey with internal and with public key") - address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) + address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) request = [{ "scriptPubKey": address['scriptPubKey'], "timestamp": "now", @@ -123,14 +123,14 @@ class ImportMultiTest (BitcoinTestFramework): }] result = self.nodes[1].importmulti(request) assert_equal(result[0]['success'], True) - address_assert = self.nodes[1].validateaddress(address['address']) + address_assert = self.nodes[1].getaddressinfo(address['address']) assert_equal(address_assert['iswatchonly'], True) assert_equal(address_assert['ismine'], False) assert_equal(address_assert['timestamp'], timestamp) # ScriptPubKey + Public key + !internal self.log.info("Should not import a scriptPubKey without internal and with public key") - address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) + address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) request = [{ "scriptPubKey": address['scriptPubKey'], "timestamp": "now", @@ -140,14 +140,14 @@ class ImportMultiTest (BitcoinTestFramework): assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -8) assert_equal(result[0]['error']['message'], 'Internal must be set for hex scriptPubKey') - address_assert = self.nodes[1].validateaddress(address['address']) + address_assert = self.nodes[1].getaddressinfo(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) assert_equal('timestamp' in address_assert, False) # Address + Private key + !watchonly self.log.info("Should import an address with private key") - address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) + address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) result = self.nodes[1].importmulti([{ "scriptPubKey": { "address": address['address'] @@ -156,7 +156,7 @@ class ImportMultiTest (BitcoinTestFramework): "keys": [ self.nodes[0].dumpprivkey(address['address']) ] }]) assert_equal(result[0]['success'], True) - address_assert = self.nodes[1].validateaddress(address['address']) + address_assert = self.nodes[1].getaddressinfo(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], True) assert_equal(address_assert['timestamp'], timestamp) @@ -175,7 +175,7 @@ class ImportMultiTest (BitcoinTestFramework): # Address + Private key + watchonly self.log.info("Should not import an address with private key and with watchonly") - address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) + address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) result = self.nodes[1].importmulti([{ "scriptPubKey": { "address": address['address'] @@ -187,14 +187,14 @@ class ImportMultiTest (BitcoinTestFramework): assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -8) assert_equal(result[0]['error']['message'], 'Incompatibility found between watchonly and keys') - address_assert = self.nodes[1].validateaddress(address['address']) + address_assert = self.nodes[1].getaddressinfo(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) assert_equal('timestamp' in address_assert, False) # ScriptPubKey + Private key + internal self.log.info("Should import a scriptPubKey with internal and with private key") - address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) + address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) result = self.nodes[1].importmulti([{ "scriptPubKey": address['scriptPubKey'], "timestamp": "now", @@ -202,14 +202,14 @@ class ImportMultiTest (BitcoinTestFramework): "internal": True }]) assert_equal(result[0]['success'], True) - address_assert = self.nodes[1].validateaddress(address['address']) + address_assert = self.nodes[1].getaddressinfo(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], True) assert_equal(address_assert['timestamp'], timestamp) # ScriptPubKey + Private key + !internal self.log.info("Should not import a scriptPubKey without internal and with private key") - address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) + address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) result = self.nodes[1].importmulti([{ "scriptPubKey": address['scriptPubKey'], "timestamp": "now", @@ -218,16 +218,16 @@ class ImportMultiTest (BitcoinTestFramework): assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -8) assert_equal(result[0]['error']['message'], 'Internal must be set for hex scriptPubKey') - address_assert = self.nodes[1].validateaddress(address['address']) + address_assert = self.nodes[1].getaddressinfo(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) assert_equal('timestamp' in address_assert, False) # P2SH address - sig_address_1 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) - sig_address_2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) - sig_address_3 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) + sig_address_1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + sig_address_2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + sig_address_3 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['pubkey'], sig_address_2['pubkey'], sig_address_3['pubkey']]) self.nodes[1].generate(100) transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) @@ -242,7 +242,7 @@ class ImportMultiTest (BitcoinTestFramework): "timestamp": "now", }]) assert_equal(result[0]['success'], True) - address_assert = self.nodes[1].validateaddress(multi_sig_script['address']) + address_assert = self.nodes[1].getaddressinfo(multi_sig_script['address']) assert_equal(address_assert['isscript'], True) assert_equal(address_assert['iswatchonly'], True) assert_equal(address_assert['timestamp'], timestamp) @@ -252,9 +252,9 @@ class ImportMultiTest (BitcoinTestFramework): # P2SH + Redeem script - sig_address_1 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) - sig_address_2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) - sig_address_3 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) + sig_address_1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + sig_address_2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + sig_address_3 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['pubkey'], sig_address_2['pubkey'], sig_address_3['pubkey']]) self.nodes[1].generate(100) transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) @@ -270,7 +270,7 @@ class ImportMultiTest (BitcoinTestFramework): "redeemscript": multi_sig_script['redeemScript'] }]) assert_equal(result[0]['success'], True) - address_assert = self.nodes[1].validateaddress(multi_sig_script['address']) + address_assert = self.nodes[1].getaddressinfo(multi_sig_script['address']) assert_equal(address_assert['timestamp'], timestamp) p2shunspent = self.nodes[1].listunspent(0,999999, [multi_sig_script['address']])[0] @@ -279,9 +279,9 @@ class ImportMultiTest (BitcoinTestFramework): # P2SH + Redeem script + Private Keys + !Watchonly - sig_address_1 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) - sig_address_2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) - sig_address_3 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) + sig_address_1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + sig_address_2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + sig_address_3 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['pubkey'], sig_address_2['pubkey'], sig_address_3['pubkey']]) self.nodes[1].generate(100) transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) @@ -298,7 +298,7 @@ class ImportMultiTest (BitcoinTestFramework): "keys": [ self.nodes[0].dumpprivkey(sig_address_1['address']), self.nodes[0].dumpprivkey(sig_address_2['address'])] }]) assert_equal(result[0]['success'], True) - address_assert = self.nodes[1].validateaddress(multi_sig_script['address']) + address_assert = self.nodes[1].getaddressinfo(multi_sig_script['address']) assert_equal(address_assert['timestamp'], timestamp) p2shunspent = self.nodes[1].listunspent(0,999999, [multi_sig_script['address']])[0] @@ -306,9 +306,9 @@ class ImportMultiTest (BitcoinTestFramework): assert_equal(p2shunspent['solvable'], True) # P2SH + Redeem script + Private Keys + Watchonly - sig_address_1 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) - sig_address_2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) - sig_address_3 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) + sig_address_1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + sig_address_2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + sig_address_3 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['pubkey'], sig_address_2['pubkey'], sig_address_3['pubkey']]) self.nodes[1].generate(100) transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) @@ -332,8 +332,8 @@ class ImportMultiTest (BitcoinTestFramework): # Address + Public key + !Internal + Wrong pubkey self.log.info("Should not import an address with a wrong public key") - address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) - address2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) + address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + address2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) result = self.nodes[1].importmulti([{ "scriptPubKey": { "address": address['address'] @@ -344,7 +344,7 @@ class ImportMultiTest (BitcoinTestFramework): assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -5) assert_equal(result[0]['error']['message'], 'Consistency check failed') - address_assert = self.nodes[1].validateaddress(address['address']) + address_assert = self.nodes[1].getaddressinfo(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) assert_equal('timestamp' in address_assert, False) @@ -352,8 +352,8 @@ class ImportMultiTest (BitcoinTestFramework): # ScriptPubKey + Public key + internal + Wrong pubkey self.log.info("Should not import a scriptPubKey with internal and with a wrong public key") - address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) - address2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) + address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + address2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) request = [{ "scriptPubKey": address['scriptPubKey'], "timestamp": "now", @@ -364,7 +364,7 @@ class ImportMultiTest (BitcoinTestFramework): assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -5) assert_equal(result[0]['error']['message'], 'Consistency check failed') - address_assert = self.nodes[1].validateaddress(address['address']) + address_assert = self.nodes[1].getaddressinfo(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) assert_equal('timestamp' in address_assert, False) @@ -372,8 +372,8 @@ class ImportMultiTest (BitcoinTestFramework): # Address + Private key + !watchonly + Wrong private key self.log.info("Should not import an address with a wrong private key") - address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) - address2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) + address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + address2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) result = self.nodes[1].importmulti([{ "scriptPubKey": { "address": address['address'] @@ -384,7 +384,7 @@ class ImportMultiTest (BitcoinTestFramework): assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -5) assert_equal(result[0]['error']['message'], 'Consistency check failed') - address_assert = self.nodes[1].validateaddress(address['address']) + address_assert = self.nodes[1].getaddressinfo(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) assert_equal('timestamp' in address_assert, False) @@ -392,8 +392,8 @@ class ImportMultiTest (BitcoinTestFramework): # ScriptPubKey + Private key + internal + Wrong private key self.log.info("Should not import a scriptPubKey with internal and with a wrong private key") - address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) - address2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) + address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + address2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) result = self.nodes[1].importmulti([{ "scriptPubKey": address['scriptPubKey'], "timestamp": "now", @@ -403,7 +403,7 @@ class ImportMultiTest (BitcoinTestFramework): assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -5) assert_equal(result[0]['error']['message'], 'Consistency check failed') - address_assert = self.nodes[1].validateaddress(address['address']) + address_assert = self.nodes[1].getaddressinfo(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) assert_equal('timestamp' in address_assert, False) @@ -419,7 +419,7 @@ class ImportMultiTest (BitcoinTestFramework): "timestamp": "now", }]) assert_equal(result[0]['success'], True) - address_assert = self.nodes[1].validateaddress(watchonly_address) + address_assert = self.nodes[1].getaddressinfo(watchonly_address) assert_equal(address_assert['iswatchonly'], True) assert_equal(address_assert['ismine'], False) assert_equal(address_assert['timestamp'], timestamp) @@ -429,7 +429,7 @@ class ImportMultiTest (BitcoinTestFramework): # restart nodes to check for proper serialization/deserialization of watch only address self.stop_nodes() self.start_nodes() - address_assert = self.nodes[1].validateaddress(watchonly_address) + address_assert = self.nodes[1].getaddressinfo(watchonly_address) assert_equal(address_assert['iswatchonly'], True) assert_equal(address_assert['ismine'], False) assert_equal(address_assert['timestamp'], watchonly_timestamp) diff --git a/test/functional/wallet_importprunedfunds.py b/test/functional/wallet_importprunedfunds.py index 6b2919b5ae..4d349db23f 100755 --- a/test/functional/wallet_importprunedfunds.py +++ b/test/functional/wallet_importprunedfunds.py @@ -26,7 +26,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework): address3_privkey = self.nodes[0].dumpprivkey(address3) # Using privkey #Check only one address - address_info = self.nodes[0].validateaddress(address1) + address_info = self.nodes[0].getaddressinfo(address1) assert_equal(address_info['ismine'], True) self.sync_all() @@ -35,15 +35,15 @@ class ImportPrunedFundsTest(BitcoinTestFramework): assert_equal(self.nodes[1].getblockcount(),101) #Address Test - before import - address_info = self.nodes[1].validateaddress(address1) + address_info = self.nodes[1].getaddressinfo(address1) assert_equal(address_info['iswatchonly'], False) assert_equal(address_info['ismine'], False) - address_info = self.nodes[1].validateaddress(address2) + address_info = self.nodes[1].getaddressinfo(address2) assert_equal(address_info['iswatchonly'], False) assert_equal(address_info['ismine'], False) - address_info = self.nodes[1].validateaddress(address3) + address_info = self.nodes[1].getaddressinfo(address3) assert_equal(address_info['iswatchonly'], False) assert_equal(address_info['ismine'], False) @@ -86,13 +86,13 @@ class ImportPrunedFundsTest(BitcoinTestFramework): assert_equal(balance3, Decimal('0.075')) #Addresses Test - after import - address_info = self.nodes[1].validateaddress(address1) + address_info = self.nodes[1].getaddressinfo(address1) assert_equal(address_info['iswatchonly'], False) assert_equal(address_info['ismine'], False) - address_info = self.nodes[1].validateaddress(address2) + address_info = self.nodes[1].getaddressinfo(address2) assert_equal(address_info['iswatchonly'], True) assert_equal(address_info['ismine'], False) - address_info = self.nodes[1].validateaddress(address3) + address_info = self.nodes[1].getaddressinfo(address3) assert_equal(address_info['iswatchonly'], False) assert_equal(address_info['ismine'], True) diff --git a/test/functional/wallet_keypool.py b/test/functional/wallet_keypool.py index 45a5eed8ec..9825e4d894 100755 --- a/test/functional/wallet_keypool.py +++ b/test/functional/wallet_keypool.py @@ -14,7 +14,7 @@ class KeyPoolTest(BitcoinTestFramework): def run_test(self): nodes = self.nodes addr_before_encrypting = nodes[0].getnewaddress() - addr_before_encrypting_data = nodes[0].validateaddress(addr_before_encrypting) + addr_before_encrypting_data = nodes[0].getaddressinfo(addr_before_encrypting) wallet_info_old = nodes[0].getwalletinfo() assert(addr_before_encrypting_data['hdmasterkeyid'] == wallet_info_old['hdmasterkeyid']) @@ -24,7 +24,7 @@ class KeyPoolTest(BitcoinTestFramework): self.start_node(0) # Keep creating keys addr = nodes[0].getnewaddress() - addr_data = nodes[0].validateaddress(addr) + addr_data = nodes[0].getaddressinfo(addr) wallet_info = nodes[0].getwalletinfo() assert(addr_before_encrypting_data['hdmasterkeyid'] != wallet_info['hdmasterkeyid']) assert(addr_data['hdmasterkeyid'] == wallet_info['hdmasterkeyid']) diff --git a/test/functional/wallet_keypool_topup.py b/test/functional/wallet_keypool_topup.py index e7af3c3987..e7b76dfaf2 100755 --- a/test/functional/wallet_keypool_topup.py +++ b/test/functional/wallet_keypool_topup.py @@ -68,7 +68,7 @@ class KeypoolRestoreTest(BitcoinTestFramework): assert_equal(self.nodes[1].listtransactions()[0]['category'], "receive") # Check that we have marked all keys up to the used keypool key as used - assert_equal(self.nodes[1].validateaddress(self.nodes[1].getnewaddress())['hdkeypath'], "m/0'/0'/110'") + assert_equal(self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress())['hdkeypath'], "m/0'/0'/110'") if __name__ == '__main__': KeypoolRestoreTest().main() diff --git a/test/functional/wallet_listreceivedby.py b/test/functional/wallet_listreceivedby.py index 1f2b3c8aa7..01c9899c71 100755 --- a/test/functional/wallet_listreceivedby.py +++ b/test/functional/wallet_listreceivedby.py @@ -45,10 +45,44 @@ class ReceivedByTest(BitcoinTestFramework): assert_array_result(self.nodes[1].listreceivedbyaddress(11), {"address": addr}, {}, True) # Empty Tx - addr = self.nodes[1].getnewaddress() + empty_addr = self.nodes[1].getnewaddress() assert_array_result(self.nodes[1].listreceivedbyaddress(0, True), - {"address": addr}, - {"address": addr, "account": "", "amount": 0, "confirmations": 0, "txids": []}) + {"address": empty_addr}, + {"address": empty_addr, "account": "", "amount": 0, "confirmations": 0, "txids": []}) + + #Test Address filtering + #Only on addr + expected = {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]} + res = self.nodes[1].listreceivedbyaddress(minconf=0, include_empty=True, include_watchonly=True, address_filter=addr) + assert_array_result(res, {"address":addr}, expected) + assert_equal(len(res), 1) + #Error on invalid address + assert_raises_rpc_error(-4, "address_filter parameter was invalid", self.nodes[1].listreceivedbyaddress, minconf=0, include_empty=True, include_watchonly=True, address_filter="bamboozling") + #Another address receive money + res = self.nodes[1].listreceivedbyaddress(0, True, True) + assert_equal(len(res), 2) #Right now 2 entries + other_addr = self.nodes[1].getnewaddress() + txid2 = self.nodes[0].sendtoaddress(other_addr, 0.1) + self.nodes[0].generate(1) + self.sync_all() + #Same test as above should still pass + expected = {"address":addr, "account":"", "amount":Decimal("0.1"), "confirmations":11, "txids":[txid,]} + res = self.nodes[1].listreceivedbyaddress(0, True, True, addr) + assert_array_result(res, {"address":addr}, expected) + assert_equal(len(res), 1) + #Same test as above but with other_addr should still pass + expected = {"address":other_addr, "account":"", "amount":Decimal("0.1"), "confirmations":1, "txids":[txid2,]} + res = self.nodes[1].listreceivedbyaddress(0, True, True, other_addr) + assert_array_result(res, {"address":other_addr}, expected) + assert_equal(len(res), 1) + #Should be two entries though without filter + res = self.nodes[1].listreceivedbyaddress(0, True, True) + assert_equal(len(res), 3) #Became 3 entries + + #Not on random addr + other_addr = self.nodes[0].getnewaddress() # note on node[0]! just a random addr + res = self.nodes[1].listreceivedbyaddress(0, True, True, other_addr) + assert_equal(len(res), 0) self.log.info("getreceivedbyaddress Test") diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py index 67e7744bf8..25e2716661 100755 --- a/test/functional/wallet_listsinceblock.py +++ b/test/functional/wallet_listsinceblock.py @@ -158,7 +158,7 @@ class ListSinceBlockTest (BitcoinTestFramework): 'vout': utxo['vout'], }] txid1 = self.nodes[1].sendrawtransaction( - self.nodes[1].signrawtransaction( + self.nodes[1].signrawtransactionwithwallet( self.nodes[1].createrawtransaction(utxoDicts, recipientDict))['hex']) # send from nodes[2] using utxo to nodes[3] @@ -167,7 +167,7 @@ class ListSinceBlockTest (BitcoinTestFramework): self.nodes[2].getnewaddress(): change, } self.nodes[2].sendrawtransaction( - self.nodes[2].signrawtransaction( + self.nodes[2].signrawtransactionwithwallet( self.nodes[2].createrawtransaction(utxoDicts, recipientDict2))['hex']) # generate on both sides @@ -232,7 +232,7 @@ class ListSinceBlockTest (BitcoinTestFramework): 'txid': utxo['txid'], 'vout': utxo['vout'], }] - signedtxres = self.nodes[2].signrawtransaction( + signedtxres = self.nodes[2].signrawtransactionwithwallet( self.nodes[2].createrawtransaction(utxoDicts, recipientDict)) assert signedtxres['complete'] diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py index b07e451667..378c06ee59 100755 --- a/test/functional/wallet_multiwallet.py +++ b/test/functional/wallet_multiwallet.py @@ -16,7 +16,6 @@ class MultiWalletTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 - self.extra_args = [['-wallet=w1', '-wallet=w2', '-wallet=w3', '-wallet=w'], []] self.supports_cli = True def run_test(self): @@ -26,9 +25,42 @@ class MultiWalletTest(BitcoinTestFramework): wallet_dir = lambda *p: data_dir('wallets', *p) wallet = lambda name: node.get_wallet_rpc(name) - assert_equal(set(node.listwallets()), {"w1", "w2", "w3", "w"}) - + # check wallet.dat is created self.stop_nodes() + assert_equal(os.path.isfile(wallet_dir('wallet.dat')), True) + + # create symlink to verify wallet directory path can be referenced + # through symlink + os.mkdir(wallet_dir('w7')) + os.symlink('w7', wallet_dir('w7_symlink')) + + # rename wallet.dat to make sure plain wallet file paths (as opposed to + # directory paths) can be loaded + os.rename(wallet_dir("wallet.dat"), wallet_dir("w8")) + + # restart node with a mix of wallet names: + # w1, w2, w3 - to verify new wallets created when non-existing paths specified + # w - to verify wallet name matching works when one wallet path is prefix of another + # sub/w5 - to verify relative wallet path is created correctly + # extern/w6 - to verify absolute wallet path is created correctly + # w7_symlink - to verify symlinked wallet path is initialized correctly + # w8 - to verify existing wallet file is loaded correctly + # '' - to verify default wallet file is created correctly + wallet_names = ['w1', 'w2', 'w3', 'w', 'sub/w5', os.path.join(self.options.tmpdir, 'extern/w6'), 'w7_symlink', 'w8', ''] + extra_args = ['-wallet={}'.format(n) for n in wallet_names] + self.start_node(0, extra_args) + assert_equal(set(node.listwallets()), set(wallet_names)) + + # check that all requested wallets were created + self.stop_node(0) + for wallet_name in wallet_names: + if os.path.isdir(wallet_dir(wallet_name)): + assert_equal(os.path.isfile(wallet_dir(wallet_name, "wallet.dat")), True) + else: + assert_equal(os.path.isfile(wallet_dir(wallet_name)), True) + + # should not initialize if wallet path can't be created + self.assert_start_raises_init_error(0, ['-wallet=wallet.dat/bad'], 'Not a directory') self.assert_start_raises_init_error(0, ['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" does not exist') self.assert_start_raises_init_error(0, ['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" is a relative path', cwd=data_dir()) @@ -37,17 +69,13 @@ class MultiWalletTest(BitcoinTestFramework): # should not initialize if there are duplicate wallets self.assert_start_raises_init_error(0, ['-wallet=w1', '-wallet=w1'], 'Error loading wallet w1. Duplicate -wallet filename specified.') - # should not initialize if wallet file is a directory - os.mkdir(wallet_dir('w11')) - self.assert_start_raises_init_error(0, ['-wallet=w11'], 'Error loading wallet w11. -wallet filename must be a regular file.') - # should not initialize if one wallet is a copy of another - shutil.copyfile(wallet_dir('w2'), wallet_dir('w22')) - self.assert_start_raises_init_error(0, ['-wallet=w2', '-wallet=w22'], 'duplicates fileid') + shutil.copyfile(wallet_dir('w8'), wallet_dir('w8_copy')) + self.assert_start_raises_init_error(0, ['-wallet=w8', '-wallet=w8_copy'], 'duplicates fileid') # should not initialize if wallet file is a symlink - os.symlink(wallet_dir('w1'), wallet_dir('w12')) - self.assert_start_raises_init_error(0, ['-wallet=w12'], 'Error loading wallet w12. -wallet filename must be a regular file.') + os.symlink('w8', wallet_dir('w8_symlink')) + self.assert_start_raises_init_error(0, ['-wallet=w8_symlink'], 'Invalid -wallet path') # should not initialize if the specified walletdir does not exist self.assert_start_raises_init_error(0, ['-walletdir=bad'], 'Error: Specified -walletdir "bad" does not exist') @@ -77,15 +105,17 @@ class MultiWalletTest(BitcoinTestFramework): self.restart_node(0, ['-walletdir='+competing_wallet_dir]) self.assert_start_raises_init_error(1, ['-walletdir='+competing_wallet_dir], 'Error initializing wallet database environment') - self.restart_node(0, self.extra_args[0]) + self.restart_node(0, extra_args) - w1 = wallet("w1") - w2 = wallet("w2") - w3 = wallet("w3") - w4 = wallet("w") + wallets = [wallet(w) for w in wallet_names] wallet_bad = wallet("bad") - w1.generate(1) + # check wallet names and balances + wallets[0].generate(1) + for wallet_name, wallet in zip(wallet_names, wallets): + info = wallet.getwalletinfo() + assert_equal(info['immature_balance'], 50 if wallet is wallets[0] else 0) + assert_equal(info['walletname'], wallet_name) # accessing invalid wallet fails assert_raises_rpc_error(-18, "Requested wallet does not exist or is not loaded", wallet_bad.getwalletinfo) @@ -93,24 +123,7 @@ class MultiWalletTest(BitcoinTestFramework): # accessing wallet RPC without using wallet endpoint fails assert_raises_rpc_error(-19, "Wallet file not specified", node.getwalletinfo) - # check w1 wallet balance - w1_info = w1.getwalletinfo() - assert_equal(w1_info['immature_balance'], 50) - w1_name = w1_info['walletname'] - assert_equal(w1_name, "w1") - - # check w2 wallet balance - w2_info = w2.getwalletinfo() - assert_equal(w2_info['immature_balance'], 0) - w2_name = w2_info['walletname'] - assert_equal(w2_name, "w2") - - w3_name = w3.getwalletinfo()['walletname'] - assert_equal(w3_name, "w3") - - w4_name = w4.getwalletinfo()['walletname'] - assert_equal(w4_name, "w") - + w1, w2, w3, w4, *_ = wallets w1.generate(101) assert_equal(w1.getbalance(), 100) assert_equal(w2.getbalance(), 0) diff --git a/test/functional/wallet_txn_clone.py b/test/functional/wallet_txn_clone.py index ce26d6e0ee..d742ec4618 100755 --- a/test/functional/wallet_txn_clone.py +++ b/test/functional/wallet_txn_clone.py @@ -78,7 +78,7 @@ class TxnMallTest(BitcoinTestFramework): # Use a different signature hash type to sign. This creates an equivalent but malleated clone. # Don't send the clone anywhere yet - tx1_clone = self.nodes[0].signrawtransaction(clone_raw, None, None, "ALL|ANYONECANPAY") + tx1_clone = self.nodes[0].signrawtransactionwithwallet(clone_raw, None, "ALL|ANYONECANPAY") assert_equal(tx1_clone["complete"], True) # Have node0 mine a block, if requested: diff --git a/test/functional/wallet_txn_doublespend.py b/test/functional/wallet_txn_doublespend.py index 01129f3817..f16756eeaa 100755 --- a/test/functional/wallet_txn_doublespend.py +++ b/test/functional/wallet_txn_doublespend.py @@ -58,7 +58,7 @@ class TxnMallTest(BitcoinTestFramework): outputs[node1_address] = 1240 outputs[change_address] = 1248 - 1240 + doublespend_fee rawtx = self.nodes[0].createrawtransaction(inputs, outputs) - doublespend = self.nodes[0].signrawtransaction(rawtx) + doublespend = self.nodes[0].signrawtransactionwithwallet(rawtx) assert_equal(doublespend["complete"], True) # Create two spends using 1 50 BTC coin each diff --git a/test/util/bitcoin-util-test.py b/test/util/bitcoin-util-test.py index 64e826ad0b..30bd13d0dc 100755 --- a/test/util/bitcoin-util-test.py +++ b/test/util/bitcoin-util-test.py @@ -44,7 +44,7 @@ def main(): # Add the format/level to the logger logging.basicConfig(format=formatter, level=level) - bctester(os.path.join(env_conf["SRCDIR"], "test/util/data"), "bitcoin-util-test.json", env_conf) + bctester(os.path.join(env_conf["SRCDIR"], "test", "util", "data"), "bitcoin-util-test.json", env_conf) def bctester(testDir, input_basename, buildenv): """ Loads and parses the input file, runs all tests and reports results""" |