diff options
52 files changed, 870 insertions, 272 deletions
@@ -34,7 +34,7 @@ development team members simply pulls it. If it is a *more complicated or potentially controversial* change, then the patch submitter will be asked to start a discussion (if they haven't already) on the -[mailing list](http://sourceforge.net/mailarchive/forum.php?forum_name=bitcoin-development). +[mailing list](https://lists.linuxfoundation.org/mailman/listinfo/bitcoin-dev) The patch will be accepted if there is broad consensus that it is a good thing. Developers should expect to rework and resubmit patches if the code doesn't diff --git a/configure.ac b/configure.ac index 61b6a314af..8a54c2f76e 100644 --- a/configure.ac +++ b/configure.ac @@ -162,7 +162,7 @@ fi if test "x$CXXFLAGS_overridden" = "xno"; then CXXFLAGS="$CXXFLAGS -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter" fi -CPPFLAGS="$CPPFLAGS -DBOOST_SPIRIT_THREADSAFE -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS" +CPPFLAGS="$CPPFLAGS -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS" AC_ARG_WITH([utils], [AS_HELP_STRING([--with-utils], diff --git a/contrib/debian/copyright b/contrib/debian/copyright index 3741031f9c..55ebcaab42 100644 --- a/contrib/debian/copyright +++ b/contrib/debian/copyright @@ -2,8 +2,7 @@ Format: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?rev=174 Upstream-Name: Bitcoin Upstream-Contact: Satoshi Nakamoto <satoshin@gmx.com> irc://#bitcoin@freenode.net -Source: http://sourceforge.net/projects/bitcoin/files/ - https://github.com/bitcoin/bitcoin +Source: https://github.com/bitcoin/bitcoin Files: * Copyright: 2009-2012, Bitcoin Core Developers diff --git a/contrib/gitian-descriptors/gitian-osx-signer.yml b/contrib/gitian-descriptors/gitian-osx-signer.yml index c0c0b0c98b..36d7b01264 100644 --- a/contrib/gitian-descriptors/gitian-osx-signer.yml +++ b/contrib/gitian-descriptors/gitian-osx-signer.yml @@ -8,10 +8,11 @@ packages: - "libc6:i386" - "faketime" reference_datetime: "2015-06-01 00:00:00" -remotes: [] +remotes: +- "url": "https://github.com/bitcoin/bitcoin-detached-sigs.git" + "dir": "signature" files: - "bitcoin-osx-unsigned.tar.gz" -- "signature.tar.gz" script: | WRAP_DIR=$HOME/wrapped mkdir -p ${WRAP_DIR} @@ -32,6 +33,6 @@ script: | SIGNED=bitcoin-osx-signed.dmg tar -xf ${UNSIGNED} - ./detached-sig-apply.sh ${UNSIGNED} signature.tar.gz + ./detached-sig-apply.sh ${UNSIGNED} signature/osx ${WRAP_DIR}/genisoimage -no-cache-inodes -D -l -probe -V "Bitcoin-Core" -no-pad -r -apple -o uncompressed.dmg signed-app ${WRAP_DIR}/dmg dmg uncompressed.dmg ${OUTDIR}/${SIGNED} diff --git a/contrib/gitian-descriptors/gitian-win-signer.yml b/contrib/gitian-descriptors/gitian-win-signer.yml new file mode 100644 index 0000000000..0bf2ac9b2f --- /dev/null +++ b/contrib/gitian-descriptors/gitian-win-signer.yml @@ -0,0 +1,34 @@ +--- +name: "bitcoin-win-signer" +suites: +- "precise" +architectures: +- "amd64" +packages: +- "libssl-dev" +- "autoconf" +reference_datetime: "2015-06-01 00:00:00" +remotes: +- "url": "https://github.com/bitcoin/bitcoin-detached-sigs.git" + "dir": "signature" +files: +- "osslsigncode-1.7.1.tar.gz" +- "osslsigncode-Backports-to-1.7.1.patch" +- "bitcoin-win32-setup.exe" +- "bitcoin-win64-setup.exe" +script: | + BUILD_DIR=`pwd` + SIGDIR=${BUILD_DIR}/signature/win + + echo "f9a8cdb38b9c309326764ebc937cba1523a3a751a7ab05df3ecc99d18ae466c9 osslsigncode-1.7.1.tar.gz" | sha256sum -c + echo "a8c4e9cafba922f89de0df1f2152e7be286aba73f78505169bc351a7938dd911 osslsigncode-Backports-to-1.7.1.patch" | sha256sum -c + + tar xf osslsigncode-1.7.1.tar.gz + cd osslsigncode-1.7.1 + patch -p1 < ${BUILD_DIR}/osslsigncode-Backports-to-1.7.1.patch + + ./configure --without-gsf --without-curl --disable-dependency-tracking + make + + ./osslsigncode attach-signature -in ${BUILD_DIR}/bitcoin-win32-setup.exe -out ${OUTDIR}/bitcoin-win32-setup-signed.exe -sigin ${SIGDIR}/bitcoin-win32-setup.exe.pem + ./osslsigncode attach-signature -in ${BUILD_DIR}/bitcoin-win64-setup.exe -out ${OUTDIR}/bitcoin-win64-setup-signed.exe -sigin ${SIGDIR}/bitcoin-win64-setup.exe.pem diff --git a/contrib/gitian-downloader/cdecker-key.pgp b/contrib/gitian-downloader/cdecker-key.pgp Binary files differnew file mode 100644 index 0000000000..928a74b315 --- /dev/null +++ b/contrib/gitian-downloader/cdecker-key.pgp diff --git a/contrib/gitian-downloader/linux-download-config b/contrib/gitian-downloader/linux-download-config index 367d4c4216..c0048d336e 100644 --- a/contrib/gitian-downloader/linux-download-config +++ b/contrib/gitian-downloader/linux-download-config @@ -3,7 +3,7 @@ name: bitcoin urls: - http://bitcoin.org/bitcoin-latest-linux-gitian.zip rss: -- url: http://sourceforge.net/api/file/index/project-id/244765/mtime/desc/limit/100/rss +- url: xpath: //item/link/text() pattern: bitcoin-\d+.\d+.\d+-linux-gitian.zip signers: diff --git a/contrib/gitian-downloader/win32-download-config b/contrib/gitian-downloader/win32-download-config index ef47918a1f..0ad3bdd4f3 100644 --- a/contrib/gitian-downloader/win32-download-config +++ b/contrib/gitian-downloader/win32-download-config @@ -3,7 +3,7 @@ name: bitcoin urls: - http://bitcoin.org/bitcoin-latest-win32-gitian.zip rss: -- url: http://sourceforge.net/api/file/index/project-id/244765/mtime/desc/limit/100/rss +- url: xpath: //item/link/text() pattern: bitcoin-\d+.\d+.\d+-win32-gitian.zip signers: diff --git a/contrib/macdeploy/detached-sig-apply.sh b/contrib/macdeploy/detached-sig-apply.sh index 169f690438..781fe315ed 100755 --- a/contrib/macdeploy/detached-sig-apply.sh +++ b/contrib/macdeploy/detached-sig-apply.sh @@ -20,7 +20,7 @@ fi rm -rf ${TEMPDIR} && mkdir -p ${TEMPDIR} tar -C ${TEMPDIR} -xf ${UNSIGNED} -tar -C ${TEMPDIR} -xf ${SIGNATURE} +cp -rf "${SIGNATURE}"/* ${TEMPDIR} if [ -z "${PAGESTUFF}" ]; then PAGESTUFF=${TEMPDIR}/pagestuff diff --git a/contrib/macdeploy/detached-sig-create.sh b/contrib/macdeploy/detached-sig-create.sh index 18f8661cea..89a2da32f7 100755 --- a/contrib/macdeploy/detached-sig-create.sh +++ b/contrib/macdeploy/detached-sig-create.sh @@ -7,6 +7,7 @@ CODESIGN=codesign TEMPDIR=sign.temp TEMPLIST=${TEMPDIR}/signatures.txt OUT=signature.tar.gz +OUTROOT=osx if [ ! -n "$1" ]; then echo "usage: $0 <codesign args>" @@ -23,7 +24,7 @@ grep -v CodeResources < "${TEMPLIST}" | while read i; do TARGETFILE="${BUNDLE}/`echo "${i}" | sed "s|.*${BUNDLE}/||"`" SIZE=`pagestuff "$i" -p | tail -2 | grep size | sed 's/[^0-9]*//g'` OFFSET=`pagestuff "$i" -p | tail -2 | grep offset | sed 's/[^0-9]*//g'` - SIGNFILE="${TEMPDIR}/${TARGETFILE}.sign" + SIGNFILE="${TEMPDIR}/${OUTROOT}/${TARGETFILE}.sign" DIRNAME="`dirname "${SIGNFILE}"`" mkdir -p "${DIRNAME}" echo "Adding detached signature for: ${TARGETFILE}. Size: ${SIZE}. Offset: ${OFFSET}" @@ -32,7 +33,7 @@ done grep CodeResources < "${TEMPLIST}" | while read i; do TARGETFILE="${BUNDLE}/`echo "${i}" | sed "s|.*${BUNDLE}/||"`" - RESOURCE="${TEMPDIR}/${TARGETFILE}" + RESOURCE="${TEMPDIR}/${OUTROOT}/${TARGETFILE}" DIRNAME="`dirname "${RESOURCE}"`" mkdir -p "${DIRNAME}" echo "Adding resource for: "${TARGETFILE}"" diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk index f50828c546..e7aa48ddf8 100644 --- a/depends/packages/boost.mk +++ b/depends/packages/boost.mk @@ -3,7 +3,7 @@ $(package)_version=1_55_0 $(package)_download_path=http://sourceforge.net/projects/boost/files/boost/1.55.0 $(package)_file_name=$(package)_$($(package)_version).tar.bz2 $(package)_sha256_hash=fff00023dd79486d444c8e29922f4072e1d451fc5a4d2b6075852ead7f2b7b52 -$(package)_patches=darwin_boost_atomic-1.patch darwin_boost_atomic-2.patch +$(package)_patches=darwin_boost_atomic-1.patch darwin_boost_atomic-2.patch gcc_5_no_cxx11.patch define $(package)_set_vars $(package)_config_opts_release=variant=release @@ -28,6 +28,7 @@ endef define $(package)_preprocess_cmds patch -p2 < $($(package)_patch_dir)/darwin_boost_atomic-1.patch && \ patch -p2 < $($(package)_patch_dir)/darwin_boost_atomic-2.patch && \ + patch -p2 < $($(package)_patch_dir)/gcc_5_no_cxx11.patch && \ echo "using $(boost_toolset_$(host_os)) : : $($(package)_cxx) : <cxxflags>\"$($(package)_cxxflags) $($(package)_cppflags)\" <linkflags>\"$($(package)_ldflags)\" <archiver>\"$(boost_archiver_$(host_os))\" <striper>\"$(host_STRIP)\" <ranlib>\"$(host_RANLIB)\" <rc>\"$(host_WINDRES)\" : ;" > user-config.jam endef diff --git a/depends/packages/native_comparisontool.mk b/depends/packages/native_comparisontool.mk index d1b86dc2de..e0ae0cec70 100644 --- a/depends/packages/native_comparisontool.mk +++ b/depends/packages/native_comparisontool.mk @@ -1,8 +1,8 @@ package=native_comparisontool -$(package)_version=0f7b5d8 -$(package)_download_path=https://github.com/TheBlueMatt/test-scripts/raw/38b490a2599d422b12d5ce8f165792f63fd8f54f +$(package)_version=8c6666f +$(package)_download_path=https://github.com/theuni/bitcoind-comparisontool/raw/master $(package)_file_name=pull-tests-$($(package)_version).jar -$(package)_sha256_hash=ecd43b988a8b673b483e4f69f931596360a5e90fc415c75c4c259faa690df198 +$(package)_sha256_hash=a865332b3827abcde684ab79f5f43c083b0b6a4c97ff5508c79f29fee24f11cd $(package)_install_dirname=BitcoindComparisonTool_jar $(package)_install_filename=BitcoindComparisonTool.jar diff --git a/depends/patches/boost/gcc_5_no_cxx11.patch b/depends/patches/boost/gcc_5_no_cxx11.patch new file mode 100644 index 0000000000..04514c593a --- /dev/null +++ b/depends/patches/boost/gcc_5_no_cxx11.patch @@ -0,0 +1,37 @@ +From eec808554936ae068b23df07ab54d4dc6302a695 Mon Sep 17 00:00:00 2001 +From: jzmaddock <jzmaddock@gmail.com> +Date: Sat, 23 Aug 2014 09:38:02 +0100 +Subject: [PATCH] Fix BOOST_NO_CXX11_VARIADIC_TEMPLATES definition - the + feature was introduced in GCC 4.4. + +--- + include/boost/config/compiler/gcc.hpp | 9 +-------- + 1 file changed, 1 insertion(+), 8 deletions(-) + +diff --git a/include/boost/config/compiler/gcc.hpp b/include/boost/config/compiler/gcc.hpp +index f37159d..97d8a18 100644 +--- a/include/boost/config/compiler/gcc.hpp ++++ b/include/boost/config/compiler/gcc.hpp +@@ -154,14 +154,6 @@ + # define BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS + # define BOOST_NO_CXX11_RVALUE_REFERENCES + # define BOOST_NO_CXX11_STATIC_ASSERT +- +-// Variadic templates compiler: +-// http://www.generic-programming.org/~dgregor/cpp/variadic-templates.html +-# if defined(__VARIADIC_TEMPLATES) || (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4) && defined(__GXX_EXPERIMENTAL_CXX0X__)) +-# define BOOST_HAS_VARIADIC_TMPL +-# else +-# define BOOST_NO_CXX11_VARIADIC_TEMPLATES +-# endif + #endif + + // C++0x features in 4.4.n and later +@@ -176,6 +168,7 @@ + # define BOOST_NO_CXX11_DELETED_FUNCTIONS + # define BOOST_NO_CXX11_TRAILING_RESULT_TYPES + # define BOOST_NO_CXX11_INLINE_NAMESPACES ++# define BOOST_NO_CXX11_VARIADIC_TEMPLATES + #endif + + #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 5) diff --git a/doc/build-osx.md b/doc/build-osx.md index 913e72519f..dc319dd1c4 100644 --- a/doc/build-osx.md +++ b/doc/build-osx.md @@ -32,33 +32,10 @@ Instructions: Homebrew #### Install dependencies using Homebrew - brew install autoconf automake libtool boost miniupnpc openssl pkg-config protobuf qt5 + brew install autoconf automake berkeley-db4 libtool boost miniupnpc openssl pkg-config protobuf qt5 NOTE: Building with Qt4 is still supported, however, could result in a broken UI. As such, building with Qt5 is recommended. -#### Installing berkeley-db4 using Homebrew - -The homebrew package for berkeley-db4 has been broken for some time. It will install without Java though. - -Running this command takes you into brew's interactive mode, which allows you to configure, make, and install by hand: -``` -$ brew install https://raw.github.com/homebrew/homebrew/master/Library/Formula/berkeley-db4.rb -–without-java -``` - -The rest of these commands are run inside brew interactive mode: -``` -/private/tmp/berkeley-db4-UGpd0O/db-4.8.30 $ cd .. -/private/tmp/berkeley-db4-UGpd0O $ db-4.8.30/dist/configure --prefix=/usr/local/Cellar/berkeley-db4/4.8.30 --mandir=/usr/local/Cellar/berkeley-db4/4.8.30/share/man --enable-cxx -/private/tmp/berkeley-db4-UGpd0O $ make -/private/tmp/berkeley-db4-UGpd0O $ make install -/private/tmp/berkeley-db4-UGpd0O $ exit -``` - -After exiting, you'll get a warning that the install is keg-only, which means it wasn't symlinked to `/usr/local`. You don't need it to link it to build bitcoin, but if you want to, here's how: - - $ brew link --force berkeley-db4 - - ### Building `bitcoind` 1. Clone the github tree to get the source code and go into the directory. diff --git a/doc/dnsseed-policy.md b/doc/dnsseed-policy.md index 506e171153..814ae3876a 100644 --- a/doc/dnsseed-policy.md +++ b/doc/dnsseed-policy.md @@ -43,7 +43,8 @@ related to the DNS seed operation. If these expectations cannot be satisfied the operator should discontinue providing services and contact the active Bitcoin -Core development team as well as posting on bitcoin-development. +Core development team as well as posting on +[bitcoin-dev](https://lists.linuxfoundation.org/mailman/listinfo/bitcoin-dev). Behavior outside of these expectations may be reasonable in some situations but should be discussed in public in advance. diff --git a/doc/release-process.md b/doc/release-process.md index d3109c6aa2..07005bfbb9 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -41,6 +41,8 @@ Release Process ###fetch and build inputs: (first time, or when dependency versions change) mkdir -p inputs + wget -P inputs https://bitcoincore.org/cfields/osslsigncode-Backports-to-1.7.1.patch + wget -P inputs http://downloads.sourceforge.net/project/osslsigncode/osslsigncode/osslsigncode-1.7.1.tar.gz Register and download the Apple SDK: (see OSX Readme for details) @@ -65,7 +67,9 @@ Release Process mv build/out/bitcoin-*.tar.gz build/out/src/bitcoin-*.tar.gz ../ ./bin/gbuild --commit bitcoin=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-win.yml ./bin/gsign --signer $SIGNER --release ${VERSION}-win --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win.yml - mv build/out/bitcoin-*.zip build/out/bitcoin-*.exe ../ + mv build/out/bitcoin-*.zip ../ + mv build/out/bitcoin-*-win64-setup.exe inputs/bitcoin-win64-setup.exe + mv build/out/bitcoin-*-win32-setup.exe inputs/bitcoin-win32-setup.exe ./bin/gbuild --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 mv build/out/bitcoin-*-unsigned.tar.gz inputs/bitcoin-osx-unsigned.tar.gz @@ -75,7 +79,7 @@ Release Process 1. source tarball (bitcoin-${VERSION}.tar.gz) 2. linux 32-bit and 64-bit binaries dist tarballs (bitcoin-${VERSION}-linux[32|64].tar.gz) - 3. windows 32-bit and 64-bit installers and dist zips (bitcoin-${VERSION}-win[32|64]-setup.exe, bitcoin-${VERSION}-win[32|64].zip) + 3. windows 32-bit and 64-bit unsigned installers and dist zips (bitcoin-${VERSION}-win[32|64]-setup.exe, bitcoin-${VERSION}-win[32|64].zip) 4. OSX unsigned installer (bitcoin-${VERSION}-osx-unsigned.dmg) 5. Gitian signatures (in gitian.sigs/${VERSION}-<linux|win|osx-unsigned>/(your gitian key)/ @@ -91,24 +95,32 @@ Commit your signature to gitian.sigs: git push # Assuming you can push to the gitian.sigs tree popd - Wait for OSX detached signature: - Once the OSX build has 3 matching signatures, Gavin will sign it with the apple App-Store key. - He will then upload a detached signature to be combined with the unsigned app to create a signed binary. + Wait for Windows/OSX detached signatures: + Once the Windows/OSX builds each have 3 matching signatures, they will be signed with their respective release keys. + Detached signatures will then be committed to the bitcoin-detached-sigs repository, which can be combined with the unsigned apps to create signed binaries. Create the signed OSX binary: pushd ./gitian-builder - # Fetch the signature as instructed by Gavin - cp signature.tar.gz inputs/ - ./bin/gbuild -i ../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml + ./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 mv build/out/bitcoin-osx-signed.dmg ../bitcoin-${VERSION}-osx.dmg popd -Commit your signature for the signed OSX binary: + Create 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 + mv build/out/bitcoin-win64-setup-signed.exe ../bitcoin-${VERSION}-win64-setup.exe + mv build/out/bitcoin-win32-setup-signed.exe ../bitcoin-${VERSION}-win32-setup.exe + popd + +Commit your signature for the signed OSX/Windows binaries: pushd gitian.sigs 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 @@ -117,12 +129,6 @@ Commit your signature for the signed OSX binary: ### After 3 or more people have gitian-built and their results match: -- Perform code-signing. - - - Code-sign Windows -setup.exe (in a Windows virtual machine using signtool) - - Note: only Gavin has the code-signing keys currently. - - Create `SHA256SUMS.asc` for the builds, and GPG-sign it: ```bash sha256sum * > SHA256SUMS diff --git a/doc/translation_strings_policy.md b/doc/translation_strings_policy.md index 6824b1d8ef..cf72a55b20 100644 --- a/doc/translation_strings_policy.md +++ b/doc/translation_strings_policy.md @@ -64,6 +64,44 @@ Avoid dividing up a message into fragments. Translators see every string separat There have been difficulties with use of HTML in translation strings; translators should not be able to accidentally affect the formatting of messages. This may sometimes be at conflict with the recommendation in the previous section. +### Plurals + +Plurals can be complex in some languages. A quote from the gettext documentation: + + In Polish we use e.g. plik (file) this way: + 1 plik, + 2,3,4 pliki, + 5-21 pliko'w, + 22-24 pliki, + 25-31 pliko'w + and so on + +In Qt code use tr's third argument for optional plurality. For example: + + tr("%n hour(s)","",secs/HOUR_IN_SECONDS); + tr("%n day(s)","",secs/DAY_IN_SECONDS); + tr("%n week(s)","",secs/WEEK_IN_SECONDS); + +This adds `<numerusform>`s to the respective `.ts` file, which can be translated separately depending on the language. In English, this is simply: + + <message numerus="yes"> + <source>%n active connection(s) to Bitcoin network</source> + <translation> + <numerusform>%n active connection to Bitcoin network</numerusform> + <numerusform>%n active connections to Bitcoin network</numerusform> + </translation> + </message> + +Where it is possible try to avoid embedding numbers into the flow of the string at all. e.g. + + WARNING: check your network connection, %d blocks received in the last %d hours (%d expected) + +versus + + WARNING: check your network connection, less blocks (%d) were received in the last %n hours than expected (%d). + +The second example reduces the number of pluralized words that translators have to handle from three to one, at no cost to comprehensibility of the sentence. + ### String freezes During a string freeze (often before a major release), no translation strings are to be added, modified or removed. diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index f7f7b13cf9..d9a72fae2d 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -33,6 +33,7 @@ testScripts=( 'fundrawtransaction.py' 'signrawtransactions.py' 'walletbackup.py' + 'nodehandling.py' ); testScriptsExt=( 'bipdersig-p2p.py' diff --git a/qa/rpc-tests/README.md b/qa/rpc-tests/README.md index 6221c93d8b..efc81e7a97 100644 --- a/qa/rpc-tests/README.md +++ b/qa/rpc-tests/README.md @@ -25,7 +25,7 @@ Run all possible tests with `qa/pull-tester/rpc-tests.sh -extended`. Possible options: -```` +``` -h, --help show this help message and exit --nocleanup Leave bitcoinds and test.* datadir on exit or error --noshutdown Don't stop bitcoinds after the test execution diff --git a/qa/rpc-tests/httpbasics.py b/qa/rpc-tests/httpbasics.py index 64ba49df64..8ccb821286 100755 --- a/qa/rpc-tests/httpbasics.py +++ b/qa/rpc-tests/httpbasics.py @@ -4,7 +4,7 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. # -# Test REST interface +# Test rpc http basics # from test_framework.test_framework import BitcoinTestFramework @@ -20,83 +20,83 @@ try: except ImportError: import urlparse -class HTTPBasicsTest (BitcoinTestFramework): +class HTTPBasicsTest (BitcoinTestFramework): def setup_nodes(self): return start_nodes(4, self.options.tmpdir, extra_args=[['-rpckeepalive=1'], ['-rpckeepalive=0'], [], []]) - def run_test(self): - + def run_test(self): + ################################################# # lowlevel check for http persistent connection # ################################################# url = urlparse.urlparse(self.nodes[0].url) authpair = url.username + ':' + url.password headers = {"Authorization": "Basic " + base64.b64encode(authpair)} - + conn = httplib.HTTPConnection(url.hostname, url.port) conn.connect() conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) out1 = conn.getresponse().read(); assert_equal('"error":null' in out1, True) assert_equal(conn.sock!=None, True) #according to http/1.1 connection must still be open! - + #send 2nd request without closing connection conn.request('POST', '/', '{"method": "getchaintips"}', headers) out2 = conn.getresponse().read(); assert_equal('"error":null' in out1, True) #must also response with a correct json-rpc message assert_equal(conn.sock!=None, True) #according to http/1.1 connection must still be open! conn.close() - + #same should be if we add keep-alive because this should be the std. behaviour headers = {"Authorization": "Basic " + base64.b64encode(authpair), "Connection": "keep-alive"} - + conn = httplib.HTTPConnection(url.hostname, url.port) conn.connect() conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) out1 = conn.getresponse().read(); assert_equal('"error":null' in out1, True) assert_equal(conn.sock!=None, True) #according to http/1.1 connection must still be open! - + #send 2nd request without closing connection conn.request('POST', '/', '{"method": "getchaintips"}', headers) out2 = conn.getresponse().read(); assert_equal('"error":null' in out1, True) #must also response with a correct json-rpc message assert_equal(conn.sock!=None, True) #according to http/1.1 connection must still be open! conn.close() - + #now do the same with "Connection: close" headers = {"Authorization": "Basic " + base64.b64encode(authpair), "Connection":"close"} - + conn = httplib.HTTPConnection(url.hostname, url.port) conn.connect() conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) out1 = conn.getresponse().read(); assert_equal('"error":null' in out1, True) - assert_equal(conn.sock!=None, False) #now the connection must be closed after the response - + assert_equal(conn.sock!=None, False) #now the connection must be closed after the response + #node1 (2nd node) is running with disabled keep-alive option urlNode1 = urlparse.urlparse(self.nodes[1].url) authpair = urlNode1.username + ':' + urlNode1.password headers = {"Authorization": "Basic " + base64.b64encode(authpair)} - + conn = httplib.HTTPConnection(urlNode1.hostname, urlNode1.port) conn.connect() conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) out1 = conn.getresponse().read(); assert_equal('"error":null' in out1, True) assert_equal(conn.sock!=None, False) #connection must be closed because keep-alive was set to false - + #node2 (third node) is running with standard keep-alive parameters which means keep-alive is off urlNode2 = urlparse.urlparse(self.nodes[2].url) authpair = urlNode2.username + ':' + urlNode2.password headers = {"Authorization": "Basic " + base64.b64encode(authpair)} - + conn = httplib.HTTPConnection(urlNode2.hostname, urlNode2.port) conn.connect() conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) out1 = conn.getresponse().read(); assert_equal('"error":null' in out1, True) assert_equal(conn.sock!=None, True) #connection must be closed because bitcoind should use keep-alive by default - + if __name__ == '__main__': HTTPBasicsTest ().main () diff --git a/qa/rpc-tests/nodehandling.py b/qa/rpc-tests/nodehandling.py new file mode 100755 index 0000000000..9a77bd97e8 --- /dev/null +++ b/qa/rpc-tests/nodehandling.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python2 +# Copyright (c) 2014 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 node handling +# + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +import base64 + +try: + import http.client as httplib +except ImportError: + import httplib +try: + import urllib.parse as urlparse +except ImportError: + import urlparse + +class NodeHandlingTest (BitcoinTestFramework): + def run_test(self): + ########################### + # setban/listbanned tests # + ########################### + assert_equal(len(self.nodes[2].getpeerinfo()), 4) #we should have 4 nodes at this point + self.nodes[2].setban("127.0.0.1", "add") + time.sleep(3) #wait till the nodes are disconected + assert_equal(len(self.nodes[2].getpeerinfo()), 0) #all nodes must be disconnected at this point + assert_equal(len(self.nodes[2].listbanned()), 1) + self.nodes[2].clearbanned() + assert_equal(len(self.nodes[2].listbanned()), 0) + self.nodes[2].setban("127.0.0.0/24", "add") + assert_equal(len(self.nodes[2].listbanned()), 1) + try: + self.nodes[2].setban("127.0.0.1", "add") #throws exception because 127.0.0.1 is within range 127.0.0.0/24 + except: + pass + assert_equal(len(self.nodes[2].listbanned()), 1) #still only one banned ip because 127.0.0.1 is within the range of 127.0.0.0/24 + try: + self.nodes[2].setban("127.0.0.1", "remove") + except: + pass + assert_equal(len(self.nodes[2].listbanned()), 1) + self.nodes[2].setban("127.0.0.0/24", "remove") + assert_equal(len(self.nodes[2].listbanned()), 0) + self.nodes[2].clearbanned() + assert_equal(len(self.nodes[2].listbanned()), 0) + + ########################### + # RPC disconnectnode test # + ########################### + url = urlparse.urlparse(self.nodes[1].url) + self.nodes[0].disconnectnode(url.hostname+":"+str(p2p_port(1))) + time.sleep(2) #disconnecting a node needs a little bit of time + for node in self.nodes[0].getpeerinfo(): + assert(node['addr'] != url.hostname+":"+str(p2p_port(1))) + + connect_nodes_bi(self.nodes,0,1) #reconnect the node + found = False + for node in self.nodes[0].getpeerinfo(): + if node['addr'] == url.hostname+":"+str(p2p_port(1)): + found = True + assert(found) + +if __name__ == '__main__': + NodeHandlingTest ().main () diff --git a/qa/rpc-tests/proxy_test.py b/qa/rpc-tests/proxy_test.py index 9a9b2f5300..3623c16162 100755 --- a/qa/rpc-tests/proxy_test.py +++ b/qa/rpc-tests/proxy_test.py @@ -68,10 +68,10 @@ class ProxyTest(BitcoinTestFramework): ['-listen', '-debug=net', '-debug=proxy', '-proxy=%s:%i' % (self.conf1.addr),'-proxyrandomize=1'], ['-listen', '-debug=net', '-debug=proxy', '-proxy=%s:%i' % (self.conf1.addr),'-onion=%s:%i' % (self.conf2.addr),'-proxyrandomize=0'], ['-listen', '-debug=net', '-debug=proxy', '-proxy=%s:%i' % (self.conf2.addr),'-proxyrandomize=1'], - ['-listen', '-debug=net', '-debug=proxy', '-proxy=[%s]:%i' % (self.conf3.addr),'-proxyrandomize=0'] + ['-listen', '-debug=net', '-debug=proxy', '-proxy=[%s]:%i' % (self.conf3.addr),'-proxyrandomize=0', '-noonion'] ]) - def node_test(self, node, proxies, auth): + def node_test(self, node, proxies, auth, test_onion=True): rv = [] # Test: outgoing IPv4 connection through node node.addnode("15.61.23.23:1234", "onetry") @@ -99,17 +99,18 @@ class ProxyTest(BitcoinTestFramework): assert_equal(cmd.password, None) rv.append(cmd) - # Test: outgoing onion connection through node - node.addnode("bitcoinostk4e4re.onion:8333", "onetry") - cmd = proxies[2].queue.get() - assert(isinstance(cmd, Socks5Command)) - assert_equal(cmd.atyp, AddressType.DOMAINNAME) - assert_equal(cmd.addr, "bitcoinostk4e4re.onion") - assert_equal(cmd.port, 8333) - if not auth: - assert_equal(cmd.username, None) - assert_equal(cmd.password, None) - rv.append(cmd) + if test_onion: + # Test: outgoing onion connection through node + node.addnode("bitcoinostk4e4re.onion:8333", "onetry") + cmd = proxies[2].queue.get() + assert(isinstance(cmd, Socks5Command)) + assert_equal(cmd.atyp, AddressType.DOMAINNAME) + assert_equal(cmd.addr, "bitcoinostk4e4re.onion") + assert_equal(cmd.port, 8333) + if not auth: + assert_equal(cmd.username, None) + assert_equal(cmd.password, None) + rv.append(cmd) # Test: outgoing DNS name connection through node node.addnode("node.noumenon:8333", "onetry") @@ -139,8 +140,41 @@ class ProxyTest(BitcoinTestFramework): assert_equal(len(credentials), 4) # proxy on IPv6 localhost - self.node_test(self.nodes[3], [self.serv3, self.serv3, self.serv3, self.serv3], False) + self.node_test(self.nodes[3], [self.serv3, self.serv3, self.serv3, self.serv3], False, False) + + def networks_dict(d): + r = {} + for x in d['networks']: + r[x['name']] = x + return r + + # test RPC getnetworkinfo + n0 = networks_dict(self.nodes[0].getnetworkinfo()) + for net in ['ipv4','ipv6','onion']: + assert_equal(n0[net]['proxy'], '%s:%i' % (self.conf1.addr)) + assert_equal(n0[net]['proxy_randomize_credentials'], True) + assert_equal(n0['onion']['reachable'], True) + + n1 = networks_dict(self.nodes[1].getnetworkinfo()) + for net in ['ipv4','ipv6']: + assert_equal(n1[net]['proxy'], '%s:%i' % (self.conf1.addr)) + assert_equal(n1[net]['proxy_randomize_credentials'], False) + assert_equal(n1['onion']['proxy'], '%s:%i' % (self.conf2.addr)) + assert_equal(n1['onion']['proxy_randomize_credentials'], False) + assert_equal(n1['onion']['reachable'], True) + n2 = networks_dict(self.nodes[2].getnetworkinfo()) + for net in ['ipv4','ipv6','onion']: + assert_equal(n2[net]['proxy'], '%s:%i' % (self.conf2.addr)) + assert_equal(n2[net]['proxy_randomize_credentials'], True) + assert_equal(n2['onion']['reachable'], True) + + n3 = networks_dict(self.nodes[3].getnetworkinfo()) + for net in ['ipv4','ipv6']: + assert_equal(n3[net]['proxy'], '[%s]:%i' % (self.conf3.addr)) + assert_equal(n3[net]['proxy_randomize_credentials'], False) + assert_equal(n3['onion']['reachable'], False) + if __name__ == '__main__': ProxyTest().main() diff --git a/src/addrman.cpp b/src/addrman.cpp index c41ee3f9fc..b605f4351d 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -8,8 +8,6 @@ #include "serialize.h" #include "streams.h" -using namespace std; - int CAddrInfo::GetTriedBucket(const uint256& nKey) const { uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetKey()).GetHash().GetCheapHash(); @@ -68,7 +66,7 @@ double CAddrInfo::GetChance(int64_t nNow) const fChance *= 0.01; // deprioritize 66% after each failed attempt, but at most 1/28th to avoid the search taking forever or overly penalizing outages. - fChance *= pow(0.66, min(nAttempts, 8)); + fChance *= pow(0.66, std::min(nAttempts, 8)); return fChance; } @@ -258,7 +256,7 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < 24 * 60 * 60); int64_t nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60); if (addr.nTime && (!pinfo->nTime || pinfo->nTime < addr.nTime - nUpdateInterval - nTimePenalty)) - pinfo->nTime = max((int64_t)0, addr.nTime - nTimePenalty); + pinfo->nTime = std::max((int64_t)0, addr.nTime - nTimePenalty); // add services pinfo->nServices |= addr.nServices; @@ -283,7 +281,7 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP return false; } else { pinfo = Create(addr, source, &nId); - pinfo->nTime = max((int64_t)0, (int64_t)pinfo->nTime - nTimePenalty); + pinfo->nTime = std::max((int64_t)0, (int64_t)pinfo->nTime - nTimePenalty); nNew++; fNew = true; } diff --git a/src/addrman.h b/src/addrman.h index 373b0f39f3..2623d89809 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -458,7 +458,7 @@ public: } //! Return the number of (unique) addresses in all tables. - int size() + size_t size() const { return vRandom.size(); } diff --git a/src/init.cpp b/src/init.cpp index e269019db9..bcdd1f2f3d 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -260,10 +260,13 @@ void OnRPCPreCommand(const CRPCCommand& cmd) std::string HelpMessage(HelpMessageMode mode) { + const bool showDebug = GetBoolArg("-help-debug", false); // When adding new options to the categories, please keep and ensure alphabetical ordering. + // Do not translate _(...) -help-debug options, Many technical terms, and only a very small audience, so is unnecessary stress to translators. string strUsage = HelpMessageGroup(_("Options:")); strUsage += HelpMessageOpt("-?", _("This help message")); + strUsage += HelpMessageOpt("-alerts", strprintf(_("Receive and display P2P network alerts (default: %u)"), DEFAULT_ALERTS)); strUsage += HelpMessageOpt("-alertnotify=<cmd>", _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)")); strUsage += HelpMessageOpt("-blocknotify=<cmd>", _("Execute command when the best block changes (%s in cmd is replaced by block hash)")); strUsage += HelpMessageOpt("-checkblocks=<n>", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), 288)); @@ -331,8 +334,8 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageGroup(_("Wallet options:")); strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls")); strUsage += HelpMessageOpt("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), 100)); - if (GetBoolArg("-help-debug", false)) - strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf(_("Fees (in BTC/Kb) smaller than this are considered zero fee for transaction creation (default: %s)"), + if (showDebug) + strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf("Fees (in BTC/Kb) smaller than this are considered zero fee for transaction creation (default: %s)", FormatMoney(CWallet::minTxFee.GetFeePerK()))); strUsage += HelpMessageOpt("-paytxfee=<amt>", strprintf(_("Fee (in BTC/kB) to add to transactions you send (default: %s)"), FormatMoney(payTxFee.GetFeePerK()))); strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions") + " " + _("on startup")); @@ -351,16 +354,16 @@ std::string HelpMessage(HelpMessageMode mode) #endif strUsage += HelpMessageGroup(_("Debugging/Testing options:")); - if (GetBoolArg("-help-debug", false)) + if (showDebug) { - strUsage += HelpMessageOpt("-checkpoints", strprintf(_("Disable expensive verification for known chain history (default: %u)"), 1)); - strUsage += HelpMessageOpt("-dblogsize=<n>", strprintf(_("Flush database activity from memory pool to disk log every <n> megabytes (default: %u)"), 100)); - strUsage += HelpMessageOpt("-disablesafemode", strprintf(_("Disable safemode, override a real safe mode event (default: %u)"), 0)); - strUsage += HelpMessageOpt("-testsafemode", strprintf(_("Force safe mode (default: %u)"), 0)); - strUsage += HelpMessageOpt("-dropmessagestest=<n>", _("Randomly drop 1 of every <n> network messages")); - strUsage += HelpMessageOpt("-fuzzmessagestest=<n>", _("Randomly fuzz 1 of every <n> network messages")); - strUsage += HelpMessageOpt("-flushwallet", strprintf(_("Run a thread to flush wallet periodically (default: %u)"), 1)); - strUsage += HelpMessageOpt("-stopafterblockimport", strprintf(_("Stop running after importing blocks from disk (default: %u)"), 0)); + strUsage += HelpMessageOpt("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", 1)); + strUsage += HelpMessageOpt("-dblogsize=<n>", strprintf("Flush database activity from memory pool to disk log every <n> megabytes (default: %u)", 100)); + strUsage += HelpMessageOpt("-disablesafemode", strprintf("Disable safemode, override a real safe mode event (default: %u)", 0)); + strUsage += HelpMessageOpt("-testsafemode", strprintf("Force safe mode (default: %u)", 0)); + strUsage += HelpMessageOpt("-dropmessagestest=<n>", "Randomly drop 1 of every <n> network messages"); + strUsage += HelpMessageOpt("-fuzzmessagestest=<n>", "Randomly fuzz 1 of every <n> network messages"); + strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", 1)); + strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", 0)); } string debugCategories = "addrman, alert, bench, coindb, db, lock, rand, rpc, selectcoins, mempool, net, proxy, prune"; // Don't translate these and qt below if (mode == HMM_BITCOIN_QT) @@ -374,21 +377,20 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-help-debug", _("Show all debugging options (usage: --help -help-debug)")); strUsage += HelpMessageOpt("-logips", strprintf(_("Include IP addresses in debug output (default: %u)"), 0)); strUsage += HelpMessageOpt("-logtimestamps", strprintf(_("Prepend debug output with timestamp (default: %u)"), 1)); - if (GetBoolArg("-help-debug", false)) + if (showDebug) { - strUsage += HelpMessageOpt("-limitfreerelay=<n>", strprintf(_("Continuously rate-limit free transactions to <n>*1000 bytes per minute (default: %u)"), 15)); - strUsage += HelpMessageOpt("-relaypriority", strprintf(_("Require high priority for relaying free or low-fee transactions (default: %u)"), 1)); - strUsage += HelpMessageOpt("-maxsigcachesize=<n>", strprintf(_("Limit size of signature cache to <n> entries (default: %u)"), 50000)); + strUsage += HelpMessageOpt("-limitfreerelay=<n>", strprintf("Continuously rate-limit free transactions to <n>*1000 bytes per minute (default: %u)", 15)); + strUsage += HelpMessageOpt("-relaypriority", strprintf("Require high priority for relaying free or low-fee transactions (default: %u)", 1)); + strUsage += HelpMessageOpt("-maxsigcachesize=<n>", strprintf("Limit size of signature cache to <n> entries (default: %u)", 50000)); } strUsage += HelpMessageOpt("-minrelaytxfee=<amt>", strprintf(_("Fees (in BTC/Kb) smaller than this are considered zero fee for relaying (default: %s)"), FormatMoney(::minRelayTxFee.GetFeePerK()))); strUsage += HelpMessageOpt("-printtoconsole", _("Send trace/debug info to console instead of debug.log file")); - if (GetBoolArg("-help-debug", false)) + if (showDebug) { - strUsage += HelpMessageOpt("-printpriority", strprintf(_("Log transaction priority and fee per kB when mining blocks (default: %u)"), 0)); - strUsage += HelpMessageOpt("-privdb", strprintf(_("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)"), 1)); - strUsage += HelpMessageOpt("-regtest", _("Enter regression test mode, which uses a special chain in which blocks can be solved instantly.") + " " + - _("This is intended for regression testing tools and app development.") + " " + - _("In this mode -genproclimit controls how many blocks are generated immediately.")); + strUsage += HelpMessageOpt("-printpriority", strprintf("Log transaction priority and fee per kB when mining blocks (default: %u)", 0)); + strUsage += HelpMessageOpt("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", 1)); + strUsage += HelpMessageOpt("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. " + "This is intended for regression testing tools and app development."); } strUsage += HelpMessageOpt("-shrinkdebugfile", _("Shrink debug.log file on client startup (default: 1 when no -debug)")); strUsage += HelpMessageOpt("-testnet", _("Use the test network")); @@ -401,8 +403,8 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-blockminsize=<n>", strprintf(_("Set minimum block size in bytes (default: %u)"), 0)); strUsage += HelpMessageOpt("-blockmaxsize=<n>", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE)); strUsage += HelpMessageOpt("-blockprioritysize=<n>", strprintf(_("Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), DEFAULT_BLOCK_PRIORITY_SIZE)); - if (GetBoolArg("-help-debug", false)) - strUsage += HelpMessageOpt("-blockversion=<n>", strprintf(_("Override block version to test forking scenarios (default: %d)"), (int)CBlock::CURRENT_VERSION)); + if (showDebug) + strUsage += HelpMessageOpt("-blockversion=<n>", strprintf("Override block version to test forking scenarios (default: %d)", (int)CBlock::CURRENT_VERSION)); strUsage += HelpMessageGroup(_("RPC server options:")); strUsage += HelpMessageOpt("-server", _("Accept command line and JSON-RPC commands")); @@ -424,8 +426,8 @@ std::string HelpMessage(HelpMessageMode mode) if (mode == HMM_BITCOIN_QT) { strUsage += HelpMessageGroup(_("UI Options:")); - if (GetBoolArg("-help-debug", false)) { - strUsage += HelpMessageOpt("-allowselfsignedrootcertificates", _("Allow self signed root certificates (default: 0)")); + if (showDebug) { + strUsage += HelpMessageOpt("-allowselfsignedrootcertificates", "Allow self signed root certificates (default: 0)"); } strUsage += HelpMessageOpt("-choosedatadir", _("Choose data directory on startup (default: 0)")); strUsage += HelpMessageOpt("-lang=<lang>", _("Set language, for example \"de_DE\" (default: system locale)")); @@ -473,24 +475,43 @@ struct CImportingNow // If we're using -prune with -reindex, then delete block files that will be ignored by the // reindex. Since reindexing works by starting at block file 0 and looping until a blockfile -// is missing, and since pruning works by deleting the oldest block file first, just check -// for block file 0, and if it doesn't exist, delete all the block files in the -// directory (since they won't be read by the reindex but will take up disk space). -void DeleteAllBlockFiles() +// is missing, do the same here to delete any later block files after a gap. Also delete all +// rev files since they'll be rewritten by the reindex anyway. This ensures that vinfoBlockFile +// is in sync with what's actually on disk by the time we start downloading, so that pruning +// works correctly. +void CleanupBlockRevFiles() { - if (boost::filesystem::exists(GetBlockPosFilename(CDiskBlockPos(0, 0), "blk"))) - return; + using namespace boost::filesystem; + map<string, path> mapBlockFiles; + + // Glob all blk?????.dat and rev?????.dat files from the blocks directory. + // Remove the rev files immediately and insert the blk file paths into an + // ordered map keyed by block file index. + LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for -reindex with -prune\n"); + path blocksdir = GetDataDir() / "blocks"; + for (directory_iterator it(blocksdir); it != directory_iterator(); it++) { + if (is_regular_file(*it) && + it->path().filename().string().length() == 12 && + it->path().filename().string().substr(8,4) == ".dat") + { + if (it->path().filename().string().substr(0,3) == "blk") + mapBlockFiles[it->path().filename().string().substr(3,5)] = it->path(); + else if (it->path().filename().string().substr(0,3) == "rev") + remove(it->path()); + } + } - LogPrintf("Removing all blk?????.dat and rev?????.dat files for -reindex with -prune\n"); - boost::filesystem::path blocksdir = GetDataDir() / "blocks"; - for (boost::filesystem::directory_iterator it(blocksdir); it != boost::filesystem::directory_iterator(); it++) { - if (is_regular_file(*it)) { - if ((it->path().filename().string().length() == 12) && - (it->path().filename().string().substr(8,4) == ".dat") && - ((it->path().filename().string().substr(0,3) == "blk") || - (it->path().filename().string().substr(0,3) == "rev"))) - boost::filesystem::remove(it->path()); + // Remove all block files that aren't part of a contiguous set starting at + // zero by walking the ordered map (keys are block file indices) by + // keeping a separate counter. Once we hit a gap (or if 0 doesn't exist) + // start removing block files. + int nContigCounter = 0; + BOOST_FOREACH(const PAIRTYPE(string, path)& item, mapBlockFiles) { + if (atoi(item.first) == nContigCounter) { + nContigCounter++; + continue; } + remove(item.second); } } @@ -845,6 +866,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) fIsBareMultisigStd = GetBoolArg("-permitbaremultisig", true); nMaxDatacarrierBytes = GetArg("-datacarriersize", nMaxDatacarrierBytes); + fAlerts = GetBoolArg("-alerts", DEFAULT_ALERTS); + // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log // Initialize elliptic curve code @@ -964,31 +987,36 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } } - proxyType addrProxy; - bool fProxy = false; - if (mapArgs.count("-proxy")) { - addrProxy = proxyType(CService(mapArgs["-proxy"], 9050), GetBoolArg("-proxyrandomize", true)); + bool proxyRandomize = GetBoolArg("-proxyrandomize", true); + // -proxy sets a proxy for all outgoing network traffic + // -noproxy (or -proxy=0) as well as the empty string can be used to not set a proxy, this is the default + std::string proxyArg = GetArg("-proxy", ""); + if (proxyArg != "" && proxyArg != "0") { + proxyType addrProxy = proxyType(CService(proxyArg, 9050), proxyRandomize); if (!addrProxy.IsValid()) - return InitError(strprintf(_("Invalid -proxy address: '%s'"), mapArgs["-proxy"])); + return InitError(strprintf(_("Invalid -proxy address: '%s'"), proxyArg)); SetProxy(NET_IPV4, addrProxy); SetProxy(NET_IPV6, addrProxy); + SetProxy(NET_TOR, addrProxy); SetNameProxy(addrProxy); - fProxy = true; + SetReachable(NET_TOR); // by default, -proxy sets onion as reachable, unless -noonion later } - // -onion can override normal proxy, -noonion disables connecting to .onion entirely - if (!(mapArgs.count("-onion") && mapArgs["-onion"] == "0") && - (fProxy || mapArgs.count("-onion"))) { - proxyType addrOnion; - if (!mapArgs.count("-onion")) - addrOnion = addrProxy; - else - addrOnion = proxyType(CService(mapArgs["-onion"], 9050), GetBoolArg("-proxyrandomize", true)); - if (!addrOnion.IsValid()) - return InitError(strprintf(_("Invalid -onion address: '%s'"), mapArgs["-onion"])); - SetProxy(NET_TOR, addrOnion); - SetReachable(NET_TOR); + // -onion can be used to set only a proxy for .onion, or override normal proxy for .onion addresses + // -noonion (or -onion=0) disables connecting to .onion entirely + // An empty string is used to not override the onion proxy (in which case it defaults to -proxy set above, or none) + std::string onionArg = GetArg("-onion", ""); + if (onionArg != "") { + if (onionArg == "0") { // Handle -noonion/-onion=0 + SetReachable(NET_TOR, false); // set onions as unreachable + } else { + proxyType addrOnion = proxyType(CService(onionArg, 9050), proxyRandomize); + if (!addrOnion.IsValid()) + return InitError(strprintf(_("Invalid -onion address: '%s'"), onionArg)); + SetProxy(NET_TOR, addrOnion); + SetReachable(NET_TOR); + } } // see Step 2: parameter interactions for more information about these @@ -1106,9 +1134,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (fReindex) { pblocktree->WriteReindexing(true); - //If we're reindexing in prune mode, wipe away all our block and undo data files + //If we're reindexing in prune mode, wipe away unusable block files and all undo data files if (fPruneMode) - DeleteAllBlockFiles(); + CleanupBlockRevFiles(); } if (!LoadBlockIndex()) { @@ -1405,7 +1433,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // Monitor the chain, and alert if we get blocks much quicker or slower than expected int64_t nPowTargetSpacing = Params().GetConsensus().nPowTargetSpacing; CScheduler::Function f = boost::bind(&PartitionCheck, &IsInitialBlockDownload, - boost::ref(cs_main), boost::cref(chainActive), nPowTargetSpacing); + boost::ref(cs_main), boost::cref(pindexBestHeader), nPowTargetSpacing); scheduler.scheduleEvery(f, nPowTargetSpacing); #ifdef ENABLE_WALLET diff --git a/src/main.cpp b/src/main.cpp index cc9d277907..0be54ebd41 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -61,6 +61,7 @@ bool fCheckBlockIndex = false; bool fCheckpointsEnabled = true; size_t nCoinCacheUsage = 5000 * 300; uint64_t nPruneTarget = 0; +bool fAlerts = DEFAULT_ALERTS; /** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */ CFeeRate minRelayTxFee = CFeeRate(1000); @@ -1723,9 +1724,10 @@ void ThreadScriptCheck() { // we're being fed a bad chain (blocks being generated much // too slowly or too quickly). // -void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const CChain& chain, int64_t nPowTargetSpacing) +void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const CBlockIndex *const &bestHeader, + int64_t nPowTargetSpacing) { - if (initialDownloadCheck()) return; + if (bestHeader == NULL || initialDownloadCheck()) return; static int64_t lastAlertTime = 0; int64_t now = GetAdjustedTime(); @@ -1741,10 +1743,13 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const int64_t startTime = GetAdjustedTime()-SPAN_SECONDS; LOCK(cs); - int h = chain.Height(); - while (h > 0 && chain[h]->GetBlockTime() >= startTime) - --h; - int nBlocks = chain.Height()-h; + const CBlockIndex* i = bestHeader; + int nBlocks = 0; + while (i->GetBlockTime() >= startTime) { + ++nBlocks; + i = i->pprev; + if (i == NULL) return; // Ran out of chain, we must not be fully sync'ed + } // How likely is it to find that many by chance? double p = boost::math::pdf(poisson, nBlocks); @@ -3057,9 +3062,9 @@ void FindFilesToPrune(std::set<int>& setFilesToPrune) if (nCurrentUsage + nBuffer < nPruneTarget) // are we below our target? break; - // don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip + // don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip but keep scanning if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) - break; + continue; PruneOneBlockFile(fileNumber); // Queue up the files for removal @@ -4622,7 +4627,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - else if (strCommand == "alert") + else if (fAlerts && strCommand == "alert") { CAlert alert; vRecv >> alert; diff --git a/src/main.h b/src/main.h index 7eaf58d716..4e2efaada0 100644 --- a/src/main.h +++ b/src/main.h @@ -52,6 +52,8 @@ static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 750000; static const unsigned int DEFAULT_BLOCK_MIN_SIZE = 0; /** Default for -blockprioritysize, maximum space for zero/low-fee transactions **/ static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = 50000; +/** Default for accepting alerts from the P2P network. */ +static const bool DEFAULT_ALERTS = true; /** The maximum size for transactions we're willing to relay/mine */ static const unsigned int MAX_STANDARD_TX_SIZE = 100000; /** Maximum number of signature check operations in an IsStandard() P2SH script */ @@ -113,6 +115,7 @@ extern bool fCheckBlockIndex; extern bool fCheckpointsEnabled; extern size_t nCoinCacheUsage; extern CFeeRate minRelayTxFee; +extern bool fAlerts; /** Best header we've seen so far (used for getheaders queries' starting points). */ extern CBlockIndex *pindexBestHeader; @@ -186,7 +189,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle); /** Run an instance of the script checking thread */ void ThreadScriptCheck(); /** Try to detect Partition (network isolation) attacks against us */ -void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const CChain& chain, int64_t nPowTargetSpacing); +void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const CBlockIndex *const &bestHeader, int64_t nPowTargetSpacing); /** Check whether we are doing an initial block download (synchronizing from disk or network) */ bool IsInitialBlockDownload(); /** Format a string that describes several potential problems detected by the core */ diff --git a/src/net.cpp b/src/net.cpp index 42ac0e50ea..0511256e55 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -332,6 +332,15 @@ CNode* FindNode(const CNetAddr& ip) return NULL; } +CNode* FindNode(const CSubNet& subNet) +{ + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + if (subNet.Match((CNetAddr)pnode->addr)) + return (pnode); + return NULL; +} + CNode* FindNode(const std::string& addrName) { LOCK(cs_vNodes); @@ -434,11 +443,12 @@ void CNode::PushVersion() -std::map<CNetAddr, int64_t> CNode::setBanned; +std::map<CSubNet, int64_t> CNode::setBanned; CCriticalSection CNode::cs_setBanned; void CNode::ClearBanned() { + LOCK(cs_setBanned); setBanned.clear(); } @@ -447,7 +457,24 @@ bool CNode::IsBanned(CNetAddr ip) bool fResult = false; { LOCK(cs_setBanned); - std::map<CNetAddr, int64_t>::iterator i = setBanned.find(ip); + for (std::map<CSubNet, int64_t>::iterator it = setBanned.begin(); it != setBanned.end(); it++) + { + CSubNet subNet = (*it).first; + int64_t t = (*it).second; + + if(subNet.Match(ip) && GetTime() < t) + fResult = true; + } + } + return fResult; +} + +bool CNode::IsBanned(CSubNet subnet) +{ + bool fResult = false; + { + LOCK(cs_setBanned); + std::map<CSubNet, int64_t>::iterator i = setBanned.find(subnet); if (i != setBanned.end()) { int64_t t = (*i).second; @@ -458,14 +485,37 @@ bool CNode::IsBanned(CNetAddr ip) return fResult; } -bool CNode::Ban(const CNetAddr &addr) { +void CNode::Ban(const CNetAddr& addr, int64_t bantimeoffset, bool sinceUnixEpoch) { + CSubNet subNet(addr.ToString()+(addr.IsIPv4() ? "/32" : "/128")); + Ban(subNet, bantimeoffset, sinceUnixEpoch); +} + +void CNode::Ban(const CSubNet& subNet, int64_t bantimeoffset, bool sinceUnixEpoch) { int64_t banTime = GetTime()+GetArg("-bantime", 60*60*24); // Default 24-hour ban - { - LOCK(cs_setBanned); - if (setBanned[addr] < banTime) - setBanned[addr] = banTime; - } - return true; + if (bantimeoffset > 0) + banTime = (sinceUnixEpoch ? 0 : GetTime() )+bantimeoffset; + + LOCK(cs_setBanned); + if (setBanned[subNet] < banTime) + setBanned[subNet] = banTime; +} + +bool CNode::Unban(const CNetAddr &addr) { + CSubNet subNet(addr.ToString()+(addr.IsIPv4() ? "/32" : "/128")); + return Unban(subNet); +} + +bool CNode::Unban(const CSubNet &subNet) { + LOCK(cs_setBanned); + if (setBanned.erase(subNet)) + return true; + return false; +} + +void CNode::GetBanned(std::map<CSubNet, int64_t> &banMap) +{ + LOCK(cs_setBanned); + banMap = setBanned; //create a thread safe copy } @@ -66,6 +66,7 @@ unsigned int SendBufferSize(); void AddOneShot(const std::string& strDest); void AddressCurrentlyConnected(const CService& addr); CNode* FindNode(const CNetAddr& ip); +CNode* FindNode(const CSubNet& subNet); CNode* FindNode(const std::string& addrName); CNode* FindNode(const CService& ip); CNode* ConnectNode(CAddress addrConnect, const char *pszDest = NULL); @@ -284,7 +285,7 @@ protected: // Denial-of-service detection/prevention // Key is IP address, value is banned-until-time - static std::map<CNetAddr, int64_t> setBanned; + static std::map<CSubNet, int64_t> setBanned; static CCriticalSection cs_setBanned; // Whitelisted ranges. Any node connecting from these is automatically @@ -606,7 +607,13 @@ public: // new code. static void ClearBanned(); // needed for unit testing static bool IsBanned(CNetAddr ip); - static bool Ban(const CNetAddr &ip); + static bool IsBanned(CSubNet subnet); + static void Ban(const CNetAddr &ip, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); + static void Ban(const CSubNet &subNet, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); + static bool Unban(const CNetAddr &ip); + static bool Unban(const CSubNet &ip); + static void GetBanned(std::map<CSubNet, int64_t> &banmap); + void copyStats(CNodeStats &stats); static bool IsWhitelistedRange(const CNetAddr &ip); diff --git a/src/netbase.cpp b/src/netbase.cpp index e3cb4e706f..adac5c2d07 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -1330,6 +1330,11 @@ bool operator!=(const CSubNet& a, const CSubNet& b) return !(a==b); } +bool operator<(const CSubNet& a, const CSubNet& b) +{ + return (a.network < b.network || (a.network == b.network && memcmp(a.netmask, b.netmask, 16) < 0)); +} + #ifdef WIN32 std::string NetworkErrorString(int err) { diff --git a/src/netbase.h b/src/netbase.h index 1f2957116e..27f0eac2a2 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -125,6 +125,7 @@ class CSubNet friend bool operator==(const CSubNet& a, const CSubNet& b); friend bool operator!=(const CSubNet& a, const CSubNet& b); + friend bool operator<(const CSubNet& a, const CSubNet& b); }; /** A combination of a network address (CNetAddr) and a (TCP) port */ diff --git a/src/qt/forms/rpcconsole.ui b/src/qt/forms/rpcconsole.ui index c1eb185501..7ae8237476 100644 --- a/src/qt/forms/rpcconsole.ui +++ b/src/qt/forms/rpcconsole.ui @@ -745,13 +745,36 @@ </property> <layout class="QGridLayout" name="gridLayout_3"> <item row="0" column="0"> + <widget class="QLabel" name="label_30"> + <property name="text"> + <string>Whitelisted</string> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QLabel" name="peerWhitelisted"> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> + <property name="text"> + <string>N/A</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="1" column="0"> <widget class="QLabel" name="label_23"> <property name="text"> <string>Direction</string> </property> </widget> </item> - <item row="0" column="2"> + <item row="1" column="2"> <widget class="QLabel" name="peerDirection"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -767,14 +790,14 @@ </property> </widget> </item> - <item row="1" column="0"> + <item row="2" column="0"> <widget class="QLabel" name="label_21"> <property name="text"> <string>Version</string> </property> </widget> </item> - <item row="1" column="2"> + <item row="2" column="2"> <widget class="QLabel" name="peerVersion"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -790,14 +813,14 @@ </property> </widget> </item> - <item row="2" column="0"> + <item row="3" column="0"> <widget class="QLabel" name="label_28"> <property name="text"> <string>User Agent</string> </property> </widget> </item> - <item row="2" column="2"> + <item row="3" column="2"> <widget class="QLabel" name="peerSubversion"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -813,14 +836,14 @@ </property> </widget> </item> - <item row="3" column="0"> + <item row="4" column="0"> <widget class="QLabel" name="label_4"> <property name="text"> <string>Services</string> </property> </widget> </item> - <item row="3" column="2"> + <item row="4" column="2"> <widget class="QLabel" name="peerServices"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -839,7 +862,7 @@ <item row="5" column="0"> <widget class="QLabel" name="label_29"> <property name="text"> - <string>Starting Height</string> + <string>Starting Block</string> </property> </widget> </item> @@ -862,7 +885,7 @@ <item row="6" column="0"> <widget class="QLabel" name="label_27"> <property name="text"> - <string>Sync Height</string> + <string>Synced Headers</string> </property> </widget> </item> @@ -883,13 +906,36 @@ </widget> </item> <item row="7" column="0"> + <widget class="QLabel" name="label_25"> + <property name="text"> + <string>Synced Blocks</string> + </property> + </widget> + </item> + <item row="7" column="2"> + <widget class="QLabel" name="peerCommonHeight"> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> + <property name="text"> + <string>N/A</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="8" column="0"> <widget class="QLabel" name="label_24"> <property name="text"> <string>Ban Score</string> </property> </widget> </item> - <item row="7" column="2"> + <item row="8" column="2"> <widget class="QLabel" name="peerBanScore"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -905,14 +951,14 @@ </property> </widget> </item> - <item row="8" column="0"> + <item row="9" column="0"> <widget class="QLabel" name="label_22"> <property name="text"> <string>Connection Time</string> </property> </widget> </item> - <item row="8" column="2"> + <item row="9" column="2"> <widget class="QLabel" name="peerConnTime"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -928,14 +974,14 @@ </property> </widget> </item> - <item row="9" column="0"> + <item row="10" column="0"> <widget class="QLabel" name="label_15"> <property name="text"> <string>Last Send</string> </property> </widget> </item> - <item row="9" column="2"> + <item row="10" column="2"> <widget class="QLabel" name="peerLastSend"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -951,14 +997,14 @@ </property> </widget> </item> - <item row="10" column="0"> + <item row="11" column="0"> <widget class="QLabel" name="label_19"> <property name="text"> <string>Last Receive</string> </property> </widget> </item> - <item row="10" column="2"> + <item row="11" column="2"> <widget class="QLabel" name="peerLastRecv"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -974,14 +1020,14 @@ </property> </widget> </item> - <item row="11" column="0"> + <item row="12" column="0"> <widget class="QLabel" name="label_18"> <property name="text"> <string>Bytes Sent</string> </property> </widget> </item> - <item row="11" column="2"> + <item row="12" column="2"> <widget class="QLabel" name="peerBytesSent"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -997,14 +1043,14 @@ </property> </widget> </item> - <item row="12" column="0"> + <item row="13" column="0"> <widget class="QLabel" name="label_20"> <property name="text"> <string>Bytes Received</string> </property> </widget> </item> - <item row="12" column="2"> + <item row="13" column="2"> <widget class="QLabel" name="peerBytesRecv"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -1020,14 +1066,14 @@ </property> </widget> </item> - <item row="13" column="0"> + <item row="14" column="0"> <widget class="QLabel" name="label_26"> <property name="text"> <string>Ping Time</string> </property> </widget> </item> - <item row="13" column="2"> + <item row="14" column="2"> <widget class="QLabel" name="peerPingTime"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -1043,14 +1089,40 @@ </property> </widget> </item> - <item row="14" column="0"> + <item row="15" column="0"> + <widget class="QLabel" name="peerPingWaitLabel"> + <property name="toolTip"> + <string>The duration of a currently outstanding ping.</string> + </property> + <property name="text"> + <string>Ping Wait</string> + </property> + </widget> + </item> + <item row="15" column="2"> + <widget class="QLabel" name="peerPingWait"> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> + <property name="text"> + <string>N/A</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="16" column="0"> <widget class="QLabel" name="label_timeoffset"> <property name="text"> <string>Time Offset</string> </property> </widget> </item> - <item row="14" column="2"> + <item row="16" column="2"> <widget class="QLabel" name="timeoffset"> <property name="cursor"> <cursorShape>IBeamCursor</cursorShape> @@ -1066,7 +1138,7 @@ </property> </widget> </item> - <item row="15" column="1"> + <item row="17" column="1"> <spacer name="verticalSpacer_3"> <property name="orientation"> <enum>Qt::Vertical</enum> diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index 220f273d02..f5904a4d8e 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -63,11 +63,12 @@ public: #if QT_VERSION >= 0x040700 cachedNodeStats.reserve(vNodes.size()); #endif - BOOST_FOREACH(CNode* pnode, vNodes) + foreach (CNode* pnode, vNodes) { CNodeCombinedStats stats; stats.nodeStateStats.nMisbehavior = 0; stats.nodeStateStats.nSyncHeight = -1; + stats.nodeStateStats.nCommonHeight = -1; stats.fNodeStateStatsAvailable = false; pnode->copyStats(stats.nodeStats); cachedNodeStats.append(stats); @@ -91,7 +92,7 @@ public: // build index map mapNodeRows.clear(); int row = 0; - BOOST_FOREACH(CNodeCombinedStats &stats, cachedNodeStats) + foreach (const CNodeCombinedStats& stats, cachedNodeStats) mapNodeRows.insert(std::pair<NodeId, int>(stats.nodeStats.nodeid, row++)); } diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index e99972d498..f828ce2534 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -485,10 +485,10 @@ void RPCConsole::startExecutor() void RPCConsole::on_tabWidget_currentChanged(int index) { - if(ui->tabWidget->widget(index) == ui->tab_console) - { + if (ui->tabWidget->widget(index) == ui->tab_console) ui->lineEdit->setFocus(); - } + else if (ui->tabWidget->widget(index) != ui->tab_peers) + clearSelectedNode(); } void RPCConsole::on_openDebugLogfileButton_clicked() @@ -558,12 +558,11 @@ void RPCConsole::peerLayoutChanged() return; // find the currently selected row - int selectedRow; + int selectedRow = -1; QModelIndexList selectedModelIndex = ui->peerWidget->selectionModel()->selectedIndexes(); - if (selectedModelIndex.isEmpty()) - selectedRow = -1; - else + if (!selectedModelIndex.isEmpty()) { selectedRow = selectedModelIndex.first().row(); + } // check if our detail node has a row in the table (it may not necessarily // be at selectedRow since its position can change after a layout change) @@ -573,9 +572,6 @@ void RPCConsole::peerLayoutChanged() { // detail node dissapeared from table (node disconnected) fUnselect = true; - cachedNodeid = -1; - ui->detailWidget->hide(); - ui->peerHeading->setText(tr("Select a peer to view detailed information.")); } else { @@ -590,10 +586,8 @@ void RPCConsole::peerLayoutChanged() stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow); } - if (fUnselect && selectedRow >= 0) - { - ui->peerWidget->selectionModel()->select(QItemSelection(selectedModelIndex.first(), selectedModelIndex.last()), - QItemSelectionModel::Deselect); + if (fUnselect && selectedRow >= 0) { + clearSelectedNode(); } if (fReselect) @@ -611,7 +605,8 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats) cachedNodeid = stats->nodeStats.nodeid; // update the detail ui with latest node information - QString peerAddrDetails(QString::fromStdString(stats->nodeStats.addrName)); + QString peerAddrDetails(QString::fromStdString(stats->nodeStats.addrName) + " "); + peerAddrDetails += tr("(node id: %1)").arg(QString::number(stats->nodeStats.nodeid)); if (!stats->nodeStats.addrLocal.empty()) peerAddrDetails += "<br />" + tr("via %1").arg(QString::fromStdString(stats->nodeStats.addrLocal)); ui->peerHeading->setText(peerAddrDetails); @@ -622,11 +617,13 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats) ui->peerBytesRecv->setText(FormatBytes(stats->nodeStats.nRecvBytes)); ui->peerConnTime->setText(GUIUtil::formatDurationStr(GetTime() - stats->nodeStats.nTimeConnected)); ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingTime)); + ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingWait)); ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset)); - ui->peerVersion->setText(QString("%1").arg(stats->nodeStats.nVersion)); + ui->peerVersion->setText(QString("%1").arg(QString::number(stats->nodeStats.nVersion))); ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer)); ui->peerDirection->setText(stats->nodeStats.fInbound ? tr("Inbound") : tr("Outbound")); - ui->peerHeight->setText(QString("%1").arg(stats->nodeStats.nStartingHeight)); + ui->peerHeight->setText(QString("%1").arg(QString::number(stats->nodeStats.nStartingHeight))); + ui->peerWhitelisted->setText(stats->nodeStats.fWhitelisted ? tr("Yes") : tr("No")); // This check fails for example if the lock was busy and // nodeStateStats couldn't be fetched. @@ -639,9 +636,12 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats) ui->peerSyncHeight->setText(QString("%1").arg(stats->nodeStateStats.nSyncHeight)); else ui->peerSyncHeight->setText(tr("Unknown")); - } else { - ui->peerBanScore->setText(tr("Fetching...")); - ui->peerSyncHeight->setText(tr("Fetching...")); + + // Common height is init to -1 + if (stats->nodeStateStats.nCommonHeight > -1) + ui->peerCommonHeight->setText(QString("%1").arg(stats->nodeStateStats.nCommonHeight)); + else + ui->peerCommonHeight->setText(tr("Unknown")); } ui->detailWidget->show(); @@ -687,7 +687,15 @@ void RPCConsole::disconnectSelectedNode() QString strNode = GUIUtil::getEntryData(ui->peerWidget, 0, PeerTableModel::Address); // Find the node, disconnect it and clear the selected node if (CNode *bannedNode = FindNode(strNode.toStdString())) { - bannedNode->CloseSocketDisconnect(); - ui->peerWidget->selectionModel()->clearSelection(); + bannedNode->fDisconnect = true; + clearSelectedNode(); } } + +void RPCConsole::clearSelectedNode() +{ + ui->peerWidget->selectionModel()->clearSelection(); + cachedNodeid = -1; + ui->detailWidget->hide(); + ui->peerHeading->setText(tr("Select a peer to view detailed information.")); +} diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 767e9aaeea..a309df7ba7 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -76,7 +76,7 @@ public slots: void peerSelected(const QItemSelection &selected, const QItemSelection &deselected); /** Handle updated peer information */ void peerLayoutChanged(); - /** Disconnect a selected node on the Peers tab */ + /** Disconnect a selected node on the Peers tab */ void disconnectSelectedNode(); signals: @@ -90,6 +90,8 @@ private: void setTrafficGraphRange(int mins); /** show detailed information on ui about selected node */ void updateNodeDetail(const CNodeCombinedStats *stats); + /** clear the selected node */ + void clearSelectedNode(); enum ColumnWidths { diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index e45368cb97..85229e9857 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -345,8 +345,6 @@ UniValue gettxoutsetinfo(const UniValue& params, bool fHelp) + HelpExampleRpc("gettxoutsetinfo", "") ); - LOCK(cs_main); - UniValue ret(UniValue::VOBJ); CCoinsStats stats; diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index ca673aa527..4c6b47e4a0 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -94,6 +94,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "estimatepriority", 0 }, { "prioritisetransaction", 1 }, { "prioritisetransaction", 2 }, + { "setban", 2 }, + { "setban", 3 }, }; class CRPCConvertTable diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp index fe6eb81b84..1d47bc06a5 100644 --- a/src/rpcmisc.cpp +++ b/src/rpcmisc.cpp @@ -109,20 +109,14 @@ UniValue getinfo(const UniValue& params, bool fHelp) #ifdef ENABLE_WALLET class DescribeAddressVisitor : public boost::static_visitor<UniValue> { -private: - isminetype mine; - public: - DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {} - UniValue operator()(const CNoDestination &dest) const { return UniValue(UniValue::VOBJ); } UniValue operator()(const CKeyID &keyID) const { UniValue obj(UniValue::VOBJ); CPubKey vchPubKey; obj.push_back(Pair("isscript", false)); - if (mine == ISMINE_SPENDABLE) { - pwalletMain->GetPubKey(keyID, vchPubKey); + if (pwalletMain->GetPubKey(keyID, vchPubKey)) { obj.push_back(Pair("pubkey", HexStr(vchPubKey))); obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed())); } @@ -131,10 +125,9 @@ public: UniValue operator()(const CScriptID &scriptID) const { UniValue obj(UniValue::VOBJ); + CScript subscript; obj.push_back(Pair("isscript", true)); - if (mine != ISMINE_NO) { - CScript subscript; - pwalletMain->GetCScript(scriptID, subscript); + if (pwalletMain->GetCScript(scriptID, subscript)) { std::vector<CTxDestination> addresses; txnouttype whichType; int nRequired; @@ -200,11 +193,9 @@ UniValue validateaddress(const UniValue& params, bool fHelp) #ifdef ENABLE_WALLET isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : ISMINE_NO; ret.push_back(Pair("ismine", (mine & ISMINE_SPENDABLE) ? true : false)); - if (mine != ISMINE_NO) { - ret.push_back(Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true: false)); - UniValue detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest); - ret.pushKVs(detail); - } + ret.push_back(Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true: false)); + UniValue detail = boost::apply_visitor(DescribeAddressVisitor(), dest); + ret.pushKVs(detail); if (pwalletMain && pwalletMain->mapAddressBook.count(dest)) ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest].name)); #endif diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index a36831de2a..1572b16687 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -214,6 +214,28 @@ UniValue addnode(const UniValue& params, bool fHelp) return NullUniValue; } +UniValue disconnectnode(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "disconnectnode \"node\" \n" + "\nImmediately disconnects from the specified node.\n" + "\nArguments:\n" + "1. \"node\" (string, required) The node (see getpeerinfo for nodes)\n" + "\nExamples:\n" + + HelpExampleCli("disconnectnode", "\"192.168.0.6:8333\"") + + HelpExampleRpc("disconnectnode", "\"192.168.0.6:8333\"") + ); + + CNode* pNode = FindNode(params[0].get_str()); + if (pNode == NULL) + throw JSONRPCError(RPC_CLIENT_NODE_NOT_CONNECTED, "Node not found in connected nodes"); + + pNode->fDisconnect = true; + + return NullUniValue; +} + UniValue getaddednodeinfo(const UniValue& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) @@ -408,6 +430,7 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp) " }\n" " ,...\n" " ]\n" + " \"warnings\": \"...\" (string) any network warnings (such as alert messages) \n" "}\n" "\nExamples:\n" + HelpExampleCli("getnetworkinfo", "") @@ -439,5 +462,112 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp) } } obj.push_back(Pair("localaddresses", localAddresses)); + obj.push_back(Pair("warnings", GetWarnings("statusbar"))); return obj; } + +UniValue setban(const UniValue& params, bool fHelp) +{ + string strCommand; + if (params.size() >= 2) + strCommand = params[1].get_str(); + if (fHelp || params.size() < 2 || + (strCommand != "add" && strCommand != "remove")) + throw runtime_error( + "setban \"ip(/netmask)\" \"add|remove\" (bantime) (absolute)\n" + "\nAttempts add or remove a IP/Subnet from the banned list.\n" + "\nArguments:\n" + "1. \"ip(/netmask)\" (string, required) The IP/Subnet (see getpeerinfo for nodes ip) with a optional netmask (default is /32 = single ip)\n" + "2. \"command\" (string, required) 'add' to add a IP/Subnet to the list, 'remove' to remove a IP/Subnet from the list\n" + "3. \"bantime\" (numeric, optional) time in seconds how long (or until when if [absolute] is set) the ip is banned (0 or empty means using the default time of 24h which can also be overwritten by the -bantime startup argument)\n" + "4. \"absolute\" (boolean, optional) If set, the bantime must be a absolute timestamp in seconds since epoch (Jan 1 1970 GMT)\n" + "\nExamples:\n" + + HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400") + + HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"") + + HelpExampleRpc("setban", "\"192.168.0.6\", \"add\" 86400") + ); + + CSubNet subNet; + CNetAddr netAddr; + bool isSubnet = false; + + if (params[0].get_str().find("/") != string::npos) + isSubnet = true; + + if (!isSubnet) + netAddr = CNetAddr(params[0].get_str()); + else + subNet = CSubNet(params[0].get_str()); + + if (! (isSubnet ? subNet.IsValid() : netAddr.IsValid()) ) + throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Invalid IP/Subnet"); + + if (strCommand == "add") + { + if (isSubnet ? CNode::IsBanned(subNet) : CNode::IsBanned(netAddr)) + throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned"); + + int64_t banTime = 0; //use standard bantime if not specified + if (params.size() >= 3 && !params[2].isNull()) + banTime = params[2].get_int64(); + + bool absolute = false; + if (params.size() == 4 && params[3].isTrue()) + absolute = true; + + isSubnet ? CNode::Ban(subNet, banTime, absolute) : CNode::Ban(netAddr, banTime, absolute); + + //disconnect possible nodes + while(CNode *bannedNode = (isSubnet ? FindNode(subNet) : FindNode(netAddr))) + bannedNode->fDisconnect = true; + } + else if(strCommand == "remove") + { + if (!( isSubnet ? CNode::Unban(subNet) : CNode::Unban(netAddr) )) + throw JSONRPCError(RPC_MISC_ERROR, "Error: Unban failed"); + } + + return NullUniValue; +} + +UniValue listbanned(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "listbanned\n" + "\nList all banned IPs/Subnets.\n" + "\nExamples:\n" + + HelpExampleCli("listbanned", "") + + HelpExampleRpc("listbanned", "") + ); + + std::map<CSubNet, int64_t> banMap; + CNode::GetBanned(banMap); + + UniValue bannedAddresses(UniValue::VARR); + for (std::map<CSubNet, int64_t>::iterator it = banMap.begin(); it != banMap.end(); it++) + { + UniValue rec(UniValue::VOBJ); + rec.push_back(Pair("address", (*it).first.ToString())); + rec.push_back(Pair("banned_untill", (*it).second)); + bannedAddresses.push_back(rec); + } + + return bannedAddresses; +} + +UniValue clearbanned(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "clearbanned\n" + "\nClear all banned IPs.\n" + "\nExamples:\n" + + HelpExampleCli("clearbanned", "") + + HelpExampleRpc("clearbanned", "") + ); + + CNode::ClearBanned(); + + return NullUniValue; +} diff --git a/src/rpcprotocol.cpp b/src/rpcprotocol.cpp index 090e5ea7f4..89dec2977e 100644 --- a/src/rpcprotocol.cpp +++ b/src/rpcprotocol.cpp @@ -251,7 +251,6 @@ int ReadHTTPMessage(std::basic_istream<char>& stream, map<string, * * 1.0 spec: http://json-rpc.org/wiki/specification * 1.2 spec: http://jsonrpc.org/historical/json-rpc-over-http.html - * http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx */ string JSONRPCRequest(const string& strMethod, const UniValue& params, const UniValue& id) diff --git a/src/rpcprotocol.h b/src/rpcprotocol.h index b9fa091955..ccd2439c9f 100644 --- a/src/rpcprotocol.h +++ b/src/rpcprotocol.h @@ -63,6 +63,8 @@ enum RPCErrorCode RPC_CLIENT_IN_INITIAL_DOWNLOAD = -10, //! Still downloading initial blocks RPC_CLIENT_NODE_ALREADY_ADDED = -23, //! Node is already added RPC_CLIENT_NODE_NOT_ADDED = -24, //! Node has not been added before + RPC_CLIENT_NODE_NOT_CONNECTED = -29, //! Node to disconnect not found in connected nodes + RPC_CLIENT_INVALID_IP_OR_SUBNET = -30, //! Invalid IP/Subnet //! Wallet errors RPC_WALLET_ERROR = -4, //! Unspecified problem with wallet (key not found etc.) diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 011cfcb34e..2f28971589 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -273,11 +273,15 @@ static const CRPCCommand vRPCCommands[] = /* P2P networking */ { "network", "getnetworkinfo", &getnetworkinfo, true }, { "network", "addnode", &addnode, true }, + { "network", "disconnectnode", &disconnectnode, true }, { "network", "getaddednodeinfo", &getaddednodeinfo, true }, { "network", "getconnectioncount", &getconnectioncount, true }, { "network", "getnettotals", &getnettotals, true }, { "network", "getpeerinfo", &getpeerinfo, true }, { "network", "ping", &ping, true }, + { "network", "setban", &setban, true }, + { "network", "listbanned", &listbanned, true }, + { "network", "clearbanned", &clearbanned, true }, /* Block chain and UTXO */ { "blockchain", "getblockchaininfo", &getblockchaininfo, true }, diff --git a/src/rpcserver.h b/src/rpcserver.h index 5fb4faa8f9..45af10c2a8 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -151,8 +151,12 @@ extern UniValue getconnectioncount(const UniValue& params, bool fHelp); // in rp extern UniValue getpeerinfo(const UniValue& params, bool fHelp); extern UniValue ping(const UniValue& params, bool fHelp); extern UniValue addnode(const UniValue& params, bool fHelp); +extern UniValue disconnectnode(const UniValue& params, bool fHelp); extern UniValue getaddednodeinfo(const UniValue& params, bool fHelp); extern UniValue getnettotals(const UniValue& params, bool fHelp); +extern UniValue setban(const UniValue& params, bool fHelp); +extern UniValue listbanned(const UniValue& params, bool fHelp); +extern UniValue clearbanned(const UniValue& params, bool fHelp); extern UniValue dumpprivkey(const UniValue& params, bool fHelp); // in rpcdump.cpp extern UniValue importprivkey(const UniValue& params, bool fHelp); diff --git a/src/scheduler.cpp b/src/scheduler.cpp index c42eb7244d..d5bb588b71 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -50,8 +50,10 @@ void CScheduler::serviceQueue() // Keep waiting until timeout } #else + // Some boost versions have a conflicting overload of wait_until that returns void. + // Explicitly use a template here to avoid hitting that overload. while (!shouldStop() && !taskQueue.empty() && - newTaskScheduled.wait_until(lock, taskQueue.begin()->first) != boost::cv_status::timeout) { + newTaskScheduled.wait_until<>(lock, taskQueue.begin()->first) != boost::cv_status::timeout) { // Keep waiting until timeout } #endif diff --git a/src/test/alert_tests.cpp b/src/test/alert_tests.cpp index 22cb475e02..38dcc6023c 100644 --- a/src/test/alert_tests.cpp +++ b/src/test/alert_tests.cpp @@ -201,7 +201,6 @@ BOOST_AUTO_TEST_CASE(PartitionAlert) { // Test PartitionCheck CCriticalSection csDummy; - CChain chainDummy; CBlockIndex indexDummy[100]; CChainParams& params = Params(CBaseChainParams::MAIN); int64_t nPowTargetSpacing = params.GetConsensus().nPowTargetSpacing; @@ -220,17 +219,16 @@ BOOST_AUTO_TEST_CASE(PartitionAlert) // Other members don't matter, the partition check code doesn't // use them } - chainDummy.SetTip(&indexDummy[99]); // Test 1: chain with blocks every nPowTargetSpacing seconds, // as normal, no worries: - PartitionCheck(falseFunc, csDummy, chainDummy, nPowTargetSpacing); + PartitionCheck(falseFunc, csDummy, &indexDummy[99], nPowTargetSpacing); BOOST_CHECK(strMiscWarning.empty()); // Test 2: go 3.5 hours without a block, expect a warning: now += 3*60*60+30*60; SetMockTime(now); - PartitionCheck(falseFunc, csDummy, chainDummy, nPowTargetSpacing); + PartitionCheck(falseFunc, csDummy, &indexDummy[99], nPowTargetSpacing); BOOST_CHECK(!strMiscWarning.empty()); BOOST_TEST_MESSAGE(std::string("Got alert text: ")+strMiscWarning); strMiscWarning = ""; @@ -239,7 +237,7 @@ BOOST_AUTO_TEST_CASE(PartitionAlert) // code: now += 60*10; SetMockTime(now); - PartitionCheck(falseFunc, csDummy, chainDummy, nPowTargetSpacing); + PartitionCheck(falseFunc, csDummy, &indexDummy[99], nPowTargetSpacing); BOOST_CHECK(strMiscWarning.empty()); // Test 4: get 2.5 times as many blocks as expected: @@ -248,7 +246,7 @@ BOOST_AUTO_TEST_CASE(PartitionAlert) int64_t quickSpacing = nPowTargetSpacing*2/5; for (int i = 0; i < 100; i++) // Tweak chain timestamps: indexDummy[i].nTime = now - (100-i)*quickSpacing; - PartitionCheck(falseFunc, csDummy, chainDummy, nPowTargetSpacing); + PartitionCheck(falseFunc, csDummy, &indexDummy[99], nPowTargetSpacing); BOOST_CHECK(!strMiscWarning.empty()); BOOST_TEST_MESSAGE(std::string("Got alert text: ")+strMiscWarning); strMiscWarning = ""; diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 08f988fdbf..c38df0ecf3 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -177,4 +177,87 @@ BOOST_AUTO_TEST_CASE(rpc_boostasiotocnetaddr) BOOST_CHECK_EQUAL(BoostAsioToCNetAddr(boost::asio::ip::address::from_string("::ffff:127.0.0.1")).ToString(), "127.0.0.1"); } +BOOST_AUTO_TEST_CASE(rpc_ban) +{ + BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned"))); + + UniValue r; + BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 127.0.0.0 add"))); + BOOST_CHECK_THROW(r = CallRPC(string("setban 127.0.0.0:8334")), runtime_error); //portnumber for setban not allowed + BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned"))); + UniValue ar = r.get_array(); + UniValue o1 = ar[0].get_obj(); + UniValue adr = find_value(o1, "address"); + BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.255"); + BOOST_CHECK_NO_THROW(CallRPC(string("setban 127.0.0.0 remove")));; + BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned"))); + ar = r.get_array(); + BOOST_CHECK_EQUAL(ar.size(), 0); + + BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 127.0.0.0/24 add 1607731200 true"))); + BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned"))); + ar = r.get_array(); + o1 = ar[0].get_obj(); + adr = find_value(o1, "address"); + UniValue banned_until = find_value(o1, "banned_untill"); + BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.0"); + BOOST_CHECK_EQUAL(banned_until.get_int64(), 1607731200); // absolute time check + + BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned"))); + + BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 127.0.0.0/24 add 200"))); + BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned"))); + ar = r.get_array(); + o1 = ar[0].get_obj(); + adr = find_value(o1, "address"); + banned_until = find_value(o1, "banned_untill"); + BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/255.255.255.0"); + int64_t now = GetTime(); + BOOST_CHECK(banned_until.get_int64() > now); + BOOST_CHECK(banned_until.get_int64()-now <= 200); + + // must throw an exception because 127.0.0.1 is in already banned suubnet range + BOOST_CHECK_THROW(r = CallRPC(string("setban 127.0.0.1 add")), runtime_error); + + BOOST_CHECK_NO_THROW(CallRPC(string("setban 127.0.0.0/24 remove")));; + BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned"))); + ar = r.get_array(); + BOOST_CHECK_EQUAL(ar.size(), 0); + + BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 127.0.0.0/255.255.0.0 add"))); + BOOST_CHECK_THROW(r = CallRPC(string("setban 127.0.1.1 add")), runtime_error); + + BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned"))); + BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned"))); + ar = r.get_array(); + BOOST_CHECK_EQUAL(ar.size(), 0); + + + BOOST_CHECK_THROW(r = CallRPC(string("setban test add")), runtime_error); //invalid IP + + //IPv6 tests + BOOST_CHECK_NO_THROW(r = CallRPC(string("setban FE80:0000:0000:0000:0202:B3FF:FE1E:8329 add"))); + BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned"))); + ar = r.get_array(); + o1 = ar[0].get_obj(); + adr = find_value(o1, "address"); + BOOST_CHECK_EQUAL(adr.get_str(), "fe80::202:b3ff:fe1e:8329/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); + + BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned"))); + BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 2001:db8::/30 add"))); + BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned"))); + ar = r.get_array(); + o1 = ar[0].get_obj(); + adr = find_value(o1, "address"); + BOOST_CHECK_EQUAL(adr.get_str(), "2001:db8::/ffff:fffc:0:0:0:0:0:0"); + + BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned"))); + BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/128 add"))); + BOOST_CHECK_NO_THROW(r = CallRPC(string("listbanned"))); + ar = r.get_array(); + o1 = ar[0].get_obj(); + adr = find_value(o1, "address"); + BOOST_CHECK_EQUAL(adr.get_str(), "2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/univalue_tests.cpp b/src/test/univalue_tests.cpp index de84faca23..2c1d303f66 100644 --- a/src/test/univalue_tests.cpp +++ b/src/test/univalue_tests.cpp @@ -286,7 +286,7 @@ BOOST_AUTO_TEST_CASE(univalue_object) } static const char *json1 = -"[1.10000000,{\"key1\":\"str\",\"key2\":800,\"key3\":{\"name\":\"martian\"}}]"; +"[1.10000000,{\"key1\":\"str\\u0000\",\"key2\":800,\"key3\":{\"name\":\"martian\"}}]"; BOOST_AUTO_TEST_CASE(univalue_readwrite) { @@ -306,7 +306,9 @@ BOOST_AUTO_TEST_CASE(univalue_readwrite) BOOST_CHECK_EQUAL(obj.size(), 3); BOOST_CHECK(obj["key1"].isStr()); - BOOST_CHECK_EQUAL(obj["key1"].getValStr(), "str"); + std::string correctValue("str"); + correctValue.push_back('\0'); + BOOST_CHECK_EQUAL(obj["key1"].getValStr(), correctValue); BOOST_CHECK(obj["key2"].isNum()); BOOST_CHECK_EQUAL(obj["key2"].getValStr(), "800"); BOOST_CHECK(obj["key3"].isObject()); diff --git a/src/txdb.cpp b/src/txdb.cpp index df9ff8d8c9..935b784676 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -147,7 +147,10 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) const { return error("%s: Deserialize or I/O error - %s", __func__, e.what()); } } - stats.nHeight = mapBlockIndex.find(GetBestBlock())->second->nHeight; + { + LOCK(cs_main); + stats.nHeight = mapBlockIndex.find(stats.hashBlock)->second->nHeight; + } stats.hashSerialized = ss.GetHash(); stats.nTotalAmount = nTotalAmount; return true; diff --git a/src/univalue/univalue_read.cpp b/src/univalue/univalue_read.cpp index 5cea778996..261771811d 100644 --- a/src/univalue/univalue_read.cpp +++ b/src/univalue/univalue_read.cpp @@ -188,25 +188,22 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, case 't': valStr += "\t"; break; case 'u': { - char buf[4] = {0,0,0,0}; - char *last = &buf[0]; unsigned int codepoint; if (hatoui(raw + 1, raw + 1 + 4, codepoint) != raw + 1 + 4) return JTOK_ERR; if (codepoint <= 0x7f) - *last = (char)codepoint; + valStr.push_back((char)codepoint); else if (codepoint <= 0x7FF) { - *last++ = (char)(0xC0 | (codepoint >> 6)); - *last = (char)(0x80 | (codepoint & 0x3F)); + valStr.push_back((char)(0xC0 | (codepoint >> 6))); + valStr.push_back((char)(0x80 | (codepoint & 0x3F))); } else if (codepoint <= 0xFFFF) { - *last++ = (char)(0xE0 | (codepoint >> 12)); - *last++ = (char)(0x80 | ((codepoint >> 6) & 0x3F)); - *last = (char)(0x80 | (codepoint & 0x3F)); + valStr.push_back((char)(0xE0 | (codepoint >> 12))); + valStr.push_back((char)(0x80 | ((codepoint >> 6) & 0x3F))); + valStr.push_back((char)(0x80 | (codepoint & 0x3F))); } - valStr += buf; raw += 4; break; } diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 53cfcf0961..e5bc653c33 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -43,7 +43,7 @@ void CDBEnv::EnvShutdown() if (ret != 0) LogPrintf("CDBEnv::EnvShutdown: Error %d shutting down database environment: %s\n", ret, DbEnv::strerror(ret)); if (!fMockDb) - DbEnv(0).remove(path.string().c_str(), 0); + DbEnv(0).remove(strPath.c_str(), 0); } void CDBEnv::Reset() @@ -78,10 +78,10 @@ bool CDBEnv::Open(const boost::filesystem::path& pathIn) boost::this_thread::interruption_point(); - path = pathIn; - boost::filesystem::path pathLogDir = path / "database"; + strPath = pathIn.string(); + boost::filesystem::path pathLogDir = pathIn / "database"; TryCreateDirectory(pathLogDir); - boost::filesystem::path pathErrorFile = path / "db.log"; + boost::filesystem::path pathErrorFile = pathIn / "db.log"; LogPrintf("CDBEnv::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string()); unsigned int nEnvFlags = 0; @@ -98,7 +98,7 @@ bool CDBEnv::Open(const boost::filesystem::path& pathIn) dbenv->set_flags(DB_AUTO_COMMIT, 1); dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1); dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1); - int ret = dbenv->open(path.string().c_str(), + int ret = dbenv->open(strPath.c_str(), DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | @@ -455,7 +455,7 @@ void CDBEnv::Flush(bool fShutdown) dbenv->log_archive(&listp, DB_ARCH_REMOVE); Close(); if (!fMockDb) - boost::filesystem::remove_all(path / "database"); + boost::filesystem::remove_all(boost::filesystem::path(strPath) / "database"); } } } diff --git a/src/wallet/db.h b/src/wallet/db.h index 2df6f6e5a9..64071caa3a 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -27,7 +27,9 @@ class CDBEnv private: bool fDbEnvInit; bool fMockDb; - boost::filesystem::path path; + // Don't change into boost::filesystem::path, as that can result in + // shutdown problems/crashes caused by a static initialized internal pointer. + std::string strPath; void EnvShutdown(); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 1ef2980e88..8d88933878 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -733,12 +733,12 @@ UniValue getbalance(const UniValue& params, bool fHelp) if (params[0].get_str() == "*") { // Calculate total balance a different way from GetBalance() // (GetBalance() sums up all unspent TxOuts) - // getbalance and getbalance '*' 0 should return the same number + // getbalance and "getbalance * 1 true" should return the same number CAmount nBalance = 0; for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (!wtx.IsTrusted() || wtx.GetBlocksToMaturity() > 0) + if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) continue; CAmount allFee; @@ -2201,6 +2201,7 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp) " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n" " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" + " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in btc/kb\n" "}\n" "\nExamples:\n" + HelpExampleCli("getwalletinfo", "") @@ -2219,6 +2220,7 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp) obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); if (pwalletMain->IsCrypted()) obj.push_back(Pair("unlocked_until", nWalletUnlockTime)); + obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK()))); return obj; } |