diff options
76 files changed, 1674 insertions, 506 deletions
diff --git a/.travis.yml b/.travis.yml index 95ef36bf02..2f3ea4e76f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,8 +7,7 @@ # IPv6 support sudo: required -dist: precise -group: legacy +dist: trusty os: linux language: cpp @@ -38,22 +37,25 @@ matrix: - compiler: ": ARM" env: HOST=arm-linux-gnueabihf PACKAGES="g++-arm-linux-gnueabihf" DEP_OPTS="NO_QT=1" CHECK_DOC=1 GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports" - compiler: ": Win32" - env: HOST=i686-w64-mingw32 PPA="ppa:ubuntu-wine/ppa" PACKAGES="nsis gcc-mingw-w64-i686 g++-mingw-w64-i686 binutils-mingw-w64-i686 mingw-w64-dev wine1.7 bc" RUN_TESTS=true GOAL="deploy" BITCOIN_CONFIG="--enable-gui --enable-reduce-exports" MAKEJOBS="-j2" + env: HOST=i686-w64-mingw32 DPKG_ADD_ARCH="i386" DEP_OPTS="NO_QT=1" PACKAGES="nsis g++-mingw-w64-i686 wine1.6 bc" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-reduce-exports" - compiler: ": 32-bit + dash" - env: HOST=i686-pc-linux-gnu PACKAGES="g++-multilib bc python-zmq" PPA="ppa:chris-lea/zeromq" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++" USE_SHELL="/bin/dash" + env: HOST=i686-pc-linux-gnu PACKAGES="g++-multilib bc python-zmq" DEP_OPTS="NO_QT=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++" USE_SHELL="/bin/dash" - compiler: ": Win64" - env: HOST=x86_64-w64-mingw32 PPA="ppa:ubuntu-wine/ppa" PACKAGES="nsis gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 binutils-mingw-w64-x86-64 mingw-w64-dev wine1.7 bc" RUN_TESTS=true GOAL="deploy" BITCOIN_CONFIG="--enable-gui --enable-reduce-exports" MAKEJOBS="-j2" + env: HOST=x86_64-w64-mingw32 DPKG_ADD_ARCH="i386" DEP_OPTS="NO_QT=1" PACKAGES="nsis g++-mingw-w64-x86-64 wine1.6 bc" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-reduce-exports" - compiler: ": bitcoind" - env: HOST=x86_64-unknown-linux-gnu PACKAGES="bc python-zmq" PPA="ppa:chris-lea/zeromq" DEP_OPTS="NO_QT=1 NO_UPNP=1 DEBUG=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports CPPFLAGS=-DDEBUG_LOCKORDER" + env: HOST=x86_64-unknown-linux-gnu PACKAGES="bc python-zmq" DEP_OPTS="NO_QT=1 NO_UPNP=1 DEBUG=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports CPPFLAGS=-DDEBUG_LOCKORDER" - compiler: ": No wallet" env: HOST=x86_64-unknown-linux-gnu DEP_OPTS="NO_WALLET=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports" - compiler: ": Cross-Mac" env: HOST=x86_64-apple-darwin11 PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev" BITCOIN_CONFIG="--enable-reduce-exports" OSX_SDK=10.9 GOAL="deploy" exclude: - compiler: gcc +before_install: + - export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g") install: - - if [ -n "$PACKAGES" ]; then sudo rm -f /etc/apt/sources.list.d/travis_ci_zeromq3-source.list; fi + - if [ -n "$PACKAGES" ]; then sudo rm -f /etc/apt/sources.list.d/google-chrome.list; fi - if [ -n "$PPA" ]; then travis_retry sudo add-apt-repository "$PPA" -y; fi + - if [ -n "$DPKG_ADD_ARCH" ]; then sudo dpkg --add-architecture "$DPKG_ADD_ARCH" ; fi - if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get update; fi - if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get install --no-install-recommends --no-upgrade -qq $PACKAGES; fi before_script: @@ -69,7 +71,6 @@ script: - OUTDIR=$BASE_OUTDIR/$TRAVIS_PULL_REQUEST/$TRAVIS_JOB_NUMBER-$HOST - BITCOIN_CONFIG_ALL="--disable-dependency-tracking --prefix=$TRAVIS_BUILD_DIR/depends/$HOST --bindir=$OUTDIR/bin --libdir=$OUTDIR/lib" - depends/$HOST/native/bin/ccache --max-size=$CCACHE_SIZE - - if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then export CCACHE_READONLY=1; fi - test -n "$USE_SHELL" && eval '"$USE_SHELL" -c "./autogen.sh"' || ./autogen.sh - ./configure --cache-file=config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false) - make distdir PACKAGE=bitcoin VERSION=$HOST diff --git a/build-aux/m4/ax_cxx_compile_stdcxx.m4 b/build-aux/m4/ax_cxx_compile_stdcxx.m4 new file mode 100644 index 0000000000..2c18e49c56 --- /dev/null +++ b/build-aux/m4/ax_cxx_compile_stdcxx.m4 @@ -0,0 +1,562 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the specified +# version of the C++ standard. If necessary, add switches to CXX and +# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) +# or '14' (for the C++14 standard). +# +# The second argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for an extended mode. +# +# The third argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline support for the specified C++ standard is +# required and that the macro should error out if no mode with that +# support is found. If specified 'optional', then configuration proceeds +# regardless, after defining HAVE_CXX${VERSION} if and only if a +# supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com> +# Copyright (c) 2012 Zack Weinberg <zackw@panix.com> +# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu> +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com> +# Copyright (c) 2015 Paul Norman <penorman@mac.com> +# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu> +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 4 + +dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro +dnl (serial version number 13). + +AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl + m4_if([$1], [11], [], + [$1], [14], [], + [$1], [17], [m4_fatal([support for C++17 not yet implemented in AX_CXX_COMPILE_STDCXX])], + [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$2], [], [], + [$2], [ext], [], + [$2], [noext], [], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], + [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], + [$3], [optional], [ax_cxx_compile_cxx$1_required=false], + [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, + ax_cv_cxx_compile_cxx$1, + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [ax_cv_cxx_compile_cxx$1=yes], + [ax_cv_cxx_compile_cxx$1=no])]) + if test x$ax_cv_cxx_compile_cxx$1 = xyes; then + ac_success=yes + fi + + m4_if([$2], [noext], [], [dnl + if test x$ac_success = xno; then + for switch in -std=gnu++$1 -std=gnu++0x; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + fi]) + + m4_if([$2], [ext], [], [dnl + if test x$ac_success = xno; then + dnl HP's aCC needs +std=c++11 according to: + dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf + dnl Cray's crayCC needs "-h std=c++11" + for switch in -std=c++$1 -std=c++0x +std=c++$1 "-h std=c++$1"; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx$1_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) + fi + fi + if test x$ac_success = xno; then + HAVE_CXX$1=0 + AC_MSG_NOTICE([No compiler with C++$1 support was found]) + else + HAVE_CXX$1=1 + AC_DEFINE(HAVE_CXX$1,1, + [define if the compiler supports basic C++$1 syntax]) + fi + AC_SUBST(HAVE_CXX$1) +]) + + +dnl Test body for checking C++11 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 +) + + +dnl Test body for checking C++14 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 +) + + +dnl Tests for new features in C++11 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ + +// If the compiler admits that it is not ready for C++11, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201103L + +#error "This is not a C++11 compiler" + +#else + +namespace cxx11 +{ + + namespace test_static_assert + { + + template <typename T> + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + } + + namespace test_final_override + { + + struct Base + { + virtual void f() {} + }; + + struct Derived : public Base + { + virtual void f() override {} + }; + + } + + namespace test_double_right_angle_brackets + { + + template < typename T > + struct check {}; + + typedef check<void> single_type; + typedef check<check<void>> double_type; + typedef check<check<check<void>>> triple_type; + typedef check<check<check<check<void>>>> quadruple_type; + + } + + namespace test_decltype + { + + int + f() + { + int a = 1; + decltype(a) b = 2; + return a + b; + } + + } + + namespace test_type_deduction + { + + template < typename T1, typename T2 > + struct is_same + { + static const bool value = false; + }; + + template < typename T > + struct is_same<T, T> + { + static const bool value = true; + }; + + template < typename T1, typename T2 > + auto + add(T1 a1, T2 a2) -> decltype(a1 + a2) + { + return a1 + a2; + } + + int + test(const int c, volatile int v) + { + static_assert(is_same<int, decltype(0)>::value == true, ""); + static_assert(is_same<int, decltype(c)>::value == false, ""); + static_assert(is_same<int, decltype(v)>::value == false, ""); + auto ac = c; + auto av = v; + auto sumi = ac + av + 'x'; + auto sumf = ac + av + 1.0; + static_assert(is_same<int, decltype(ac)>::value == true, ""); + static_assert(is_same<int, decltype(av)>::value == true, ""); + static_assert(is_same<int, decltype(sumi)>::value == true, ""); + static_assert(is_same<int, decltype(sumf)>::value == false, ""); + static_assert(is_same<int, decltype(add(c, v))>::value == true, ""); + return (sumf > 0.0) ? sumi : add(c, v); + } + + } + + namespace test_noexcept + { + + int f() { return 0; } + int g() noexcept { return 0; } + + static_assert(noexcept(f()) == false, ""); + static_assert(noexcept(g()) == true, ""); + + } + + namespace test_constexpr + { + + template < typename CharT > + unsigned long constexpr + strlen_c_r(const CharT *const s, const unsigned long acc) noexcept + { + return *s ? strlen_c_r(s + 1, acc + 1) : acc; + } + + template < typename CharT > + unsigned long constexpr + strlen_c(const CharT *const s) noexcept + { + return strlen_c_r(s, 0UL); + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("1") == 1UL, ""); + static_assert(strlen_c("example") == 7UL, ""); + static_assert(strlen_c("another\0example") == 7UL, ""); + + } + + namespace test_rvalue_references + { + + template < int N > + struct answer + { + static constexpr int value = N; + }; + + answer<1> f(int&) { return answer<1>(); } + answer<2> f(const int&) { return answer<2>(); } + answer<3> f(int&&) { return answer<3>(); } + + void + test() + { + int i = 0; + const int c = 0; + static_assert(decltype(f(i))::value == 1, ""); + static_assert(decltype(f(c))::value == 2, ""); + static_assert(decltype(f(0))::value == 3, ""); + } + + } + + namespace test_uniform_initialization + { + + struct test + { + static const int zero {}; + static const int one {1}; + }; + + static_assert(test::zero == 0, ""); + static_assert(test::one == 1, ""); + + } + + namespace test_lambdas + { + + void + test1() + { + auto lambda1 = [](){}; + auto lambda2 = lambda1; + lambda1(); + lambda2(); + } + + int + test2() + { + auto a = [](int i, int j){ return i + j; }(1, 2); + auto b = []() -> int { return '0'; }(); + auto c = [=](){ return a + b; }(); + auto d = [&](){ return c; }(); + auto e = [a, &b](int x) mutable { + const auto identity = [](int y){ return y; }; + for (auto i = 0; i < a; ++i) + a += b--; + return x + identity(a + b); + }(0); + return a + b + c + d + e; + } + + int + test3() + { + const auto nullary = [](){ return 0; }; + const auto unary = [](int x){ return x; }; + using nullary_t = decltype(nullary); + using unary_t = decltype(unary); + const auto higher1st = [](nullary_t f){ return f(); }; + const auto higher2nd = [unary](nullary_t f1){ + return [unary, f1](unary_t f2){ return f2(unary(f1())); }; + }; + return higher1st(nullary) + higher2nd(nullary)(unary); + } + + } + + namespace test_variadic_templates + { + + template <int...> + struct sum; + + template <int N0, int... N1toN> + struct sum<N0, N1toN...> + { + static constexpr auto value = N0 + sum<N1toN...>::value; + }; + + template <> + struct sum<> + { + static constexpr auto value = 0; + }; + + static_assert(sum<>::value == 0, ""); + static_assert(sum<1>::value == 1, ""); + static_assert(sum<23>::value == 23, ""); + static_assert(sum<1, 2>::value == 3, ""); + static_assert(sum<5, 5, 11>::value == 21, ""); + static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); + + } + + // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function + // because of this. + namespace test_template_alias_sfinae + { + + struct foo {}; + + template<typename T> + using member = typename T::member_type; + + template<typename T> + void func(...) {} + + template<typename T> + void func(member<T>*) {} + + void test(); + + void test() { func<foo>(0); } + + } + +} // namespace cxx11 + +#endif // __cplusplus >= 201103L + +]]) + + +dnl Tests for new features in C++14 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ + +// If the compiler admits that it is not ready for C++14, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201402L + +#error "This is not a C++14 compiler" + +#else + +namespace cxx14 +{ + + namespace test_polymorphic_lambdas + { + + int + test() + { + const auto lambda = [](auto&&... args){ + const auto istiny = [](auto x){ + return (sizeof(x) == 1UL) ? 1 : 0; + }; + const int aretiny[] = { istiny(args)... }; + return aretiny[0]; + }; + return lambda(1, 1L, 1.0f, '1'); + } + + } + + namespace test_binary_literals + { + + constexpr auto ivii = 0b0000000000101010; + static_assert(ivii == 42, "wrong value"); + + } + + namespace test_generalized_constexpr + { + + template < typename CharT > + constexpr unsigned long + strlen_c(const CharT *const s) noexcept + { + auto length = 0UL; + for (auto p = s; *p; ++p) + ++length; + return length; + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("x") == 1UL, ""); + static_assert(strlen_c("test") == 4UL, ""); + static_assert(strlen_c("another\0test") == 7UL, ""); + + } + + namespace test_lambda_init_capture + { + + int + test() + { + auto x = 0; + const auto lambda1 = [a = x](int b){ return a + b; }; + const auto lambda2 = [a = lambda1(x)](){ return a; }; + return lambda2(); + } + + } + + namespace test_digit_seperators + { + + constexpr auto ten_million = 100'000'000; + static_assert(ten_million == 100000000, ""); + + } + + namespace test_return_type_deduction + { + + auto f(int& x) { return x; } + decltype(auto) g(int& x) { return x; } + + template < typename T1, typename T2 > + struct is_same + { + static constexpr auto value = false; + }; + + template < typename T > + struct is_same<T, T> + { + static constexpr auto value = true; + }; + + int + test() + { + auto x = 0; + static_assert(is_same<int, decltype(f(x))>::value, ""); + static_assert(is_same<int&, decltype(g(x))>::value, ""); + return x; + } + + } + +} // namespace cxx14 + +#endif // __cplusplus >= 201402L + +]]) diff --git a/configure.ac b/configure.ac index 6e463dfc55..a1c04daf53 100644 --- a/configure.ac +++ b/configure.ac @@ -55,6 +55,8 @@ case $host in lt_cv_deplibs_check_method="pass_all" ;; esac +dnl Require C++11 compiler (no GNU extensions) +AX_CXX_COMPILE_STDCXX([11], [noext], [mandatory]) dnl Libtool init checks. LT_INIT([pic-only]) @@ -185,6 +187,9 @@ AC_ARG_ENABLE([debug], [enable_debug=$enableval], [enable_debug=no]) +AC_LANG_PUSH([C++]) +AX_CHECK_COMPILE_FLAG([-Werror],[CXXFLAG_WERROR="-Werror"],[CXXFLAG_WERROR=""]) + if test "x$enable_debug" = xyes; then CPPFLAGS="$CPPFLAGS -DDEBUG -DDEBUG_LOCKORDER" if test "x$GCC" = xyes; then @@ -196,11 +201,19 @@ if test "x$enable_debug" = xyes; then fi fi -## TODO: Remove these hard-coded paths and flags. They are here for the sake of -## compatibility with the legacy buildsystem. -## if test "x$CXXFLAGS_overridden" = "xno"; then - CXXFLAGS="$CXXFLAGS -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter -Wno-self-assign" + AX_CHECK_COMPILE_FLAG([-Wall],[CXXFLAGS="$CXXFLAGS -Wall"],,[[$CXXFLAG_WERROR]]) + AX_CHECK_COMPILE_FLAG([-Wextra],[CXXFLAGS="$CXXFLAGS -Wextra"],,[[$CXXFLAG_WERROR]]) + AX_CHECK_COMPILE_FLAG([-Wformat],[CXXFLAGS="$CXXFLAGS -Wformat"],,[[$CXXFLAG_WERROR]]) + AX_CHECK_COMPILE_FLAG([-Wformat-security],[CXXFLAGS="$CXXFLAGS -Wformat-security"],,[[$CXXFLAG_WERROR]]) + + ## Some compilers (gcc) ignore unknown -Wno-* options, but warn about all + ## unknown options if any other warning is produced. Test the -Wfoo case, and + ## set the -Wno-foo case if it works. + AX_CHECK_COMPILE_FLAG([-Wunused-parameter],[CXXFLAGS="$CXXFLAGS -Wno-unused-parameter"],,[[$CXXFLAG_WERROR]]) + AX_CHECK_COMPILE_FLAG([-Wself-assign],[CXXFLAGS="$CXXFLAGS -Wno-self-assign"],,[[$CXXFLAG_WERROR]]) + AX_CHECK_COMPILE_FLAG([-Wunused-local-typedef],[CXXFLAGS="$CXXFLAGS -Wno-unused-local-typedef"],,[[$CXXFLAG_WERROR]]) + AX_CHECK_COMPILE_FLAG([-Wdeprecated-register],[CXXFLAGS="$CXXFLAGS -Wno-deprecated-register"],,[[$CXXFLAG_WERROR]]) fi CPPFLAGS="$CPPFLAGS -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS" @@ -222,8 +235,6 @@ AC_ARG_WITH([daemon], [build_bitcoind=$withval], [build_bitcoind=yes]) -AC_LANG_PUSH([C++]) - use_pkgconfig=yes case $host in *mingw*) @@ -267,7 +278,7 @@ case $host in fi CPPFLAGS="$CPPFLAGS -D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB" - LEVELDB_TARGET_FLAGS="TARGET_OS=OS_WINDOWS_CROSSCOMPILE" + LEVELDB_TARGET_FLAGS="-DOS_WINDOWS" if test "x$CXXFLAGS_overridden" = "xno"; then CXXFLAGS="$CXXFLAGS -w" fi @@ -289,7 +300,7 @@ case $host in ;; *darwin*) TARGET_OS=darwin - LEVELDB_TARGET_FLAGS="TARGET_OS=Darwin" + LEVELDB_TARGET_FLAGS="-DOS_MACOSX" if test x$cross_compiling != xyes; then BUILD_OS=darwin AC_CHECK_PROG([PORT],port, port) @@ -355,8 +366,11 @@ case $host in ;; *linux*) TARGET_OS=linux + LEVELDB_TARGET_FLAGS="-DOS_LINUX" ;; *) + OTHER_OS=`echo ${host_os} | awk '{print toupper($0)}'` + LEVELDB_TARGET_FLAGS="-DOS_${OTHER_OS}" ;; esac diff --git a/contrib/README.md b/contrib/README.md index 32b3a170ac..a23b197cc6 100644 --- a/contrib/README.md +++ b/contrib/README.md @@ -51,5 +51,5 @@ Test and Verify Tools ### [TestGen](/contrib/testgen) ### Utilities to generate test vectors for the data-driven Bitcoin tests. -### [Verify SF Binaries](/contrib/verifysfbinaries) ### -This script attempts to download and verify the signature file SHA256SUMS.asc from SourceForge. +### [Verify Binaries](/contrib/verifybinaries) ### +This script attempts to download and verify the signature file SHA256SUMS.asc from bitcoin.org. diff --git a/contrib/devtools/README.md b/contrib/devtools/README.md index 1103ca86c5..bb8b9246b8 100644 --- a/contrib/devtools/README.md +++ b/contrib/devtools/README.md @@ -48,9 +48,9 @@ the commit it claims to have been updated to. To use, make sure that you have fetched the upstream repository branch in which the subtree is maintained: -* for `src/secp256k1`: https://github.com/bitcoin/secp256k1.git (branch master) -* for `src/leveldb`: https://github.com/bitcoin/leveldb.git (branch bitcoin-fork) -* for `src/univalue`: https://github.com/bitcoin/univalue.git (branch master) +* for `src/secp256k1`: https://github.com/bitcoin-core/secp256k1.git (branch master) +* for `src/leveldb`: https://github.com/bitcoin-core/leveldb.git (branch bitcoin-fork) +* for `src/univalue`: https://github.com/bitcoin-core/univalue.git (branch master) Usage: `git-subtree-check.sh DIR COMMIT` diff --git a/contrib/gitian-descriptors/gitian-osx-signer.yml b/contrib/gitian-descriptors/gitian-osx-signer.yml index c4165470af..fac61aa3de 100644 --- a/contrib/gitian-descriptors/gitian-osx-signer.yml +++ b/contrib/gitian-descriptors/gitian-osx-signer.yml @@ -8,7 +8,7 @@ packages: - "faketime" reference_datetime: "2016-01-01 00:00:00" remotes: -- "url": "https://github.com/bitcoin/bitcoin-detached-sigs.git" +- "url": "https://github.com/bitcoin-core/bitcoin-detached-sigs.git" "dir": "signature" files: - "bitcoin-osx-unsigned.tar.gz" diff --git a/contrib/gitian-descriptors/gitian-win-signer.yml b/contrib/gitian-descriptors/gitian-win-signer.yml index 27c4f01eb4..88edb96627 100644 --- a/contrib/gitian-descriptors/gitian-win-signer.yml +++ b/contrib/gitian-descriptors/gitian-win-signer.yml @@ -9,7 +9,7 @@ packages: - "autoconf" reference_datetime: "2016-01-01 00:00:00" remotes: -- "url": "https://github.com/bitcoin/bitcoin-detached-sigs.git" +- "url": "https://github.com/bitcoin-core/bitcoin-detached-sigs.git" "dir": "signature" files: - "osslsigncode-1.7.1.tar.gz" diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus index 685ed8e5b8..f8201e72c6 100755 --- a/contrib/macdeploy/macdeployqtplus +++ b/contrib/macdeploy/macdeployqtplus @@ -210,8 +210,8 @@ def getFrameworks(binaryPath, verbose): sys.stderr.write(o_stderr) sys.stderr.flush() raise RuntimeError("otool failed with return code %d" % otool.returncode) - - otoolLines = o_stdout.split("\n") + + otoolLines = o_stdout.decode().split("\n") otoolLines.pop(0) # First line is the inspected binary if ".framework" in binaryPath or binaryPath.endswith(".dylib"): otoolLines.pop(0) # Frameworks and dylibs list themselves as a dependency. @@ -676,7 +676,7 @@ if verbose >= 2: print("+ Installing qt.conf +") f = open(os.path.join(applicationBundle.resourcesPath, "qt.conf"), "wb") -f.write(qt_conf) +f.write(qt_conf.encode()) f.close() # ------------------------------------------------ diff --git a/contrib/verify-commits/trusted-keys b/contrib/verify-commits/trusted-keys index a0d0f82db3..ad1b28be0c 100644 --- a/contrib/verify-commits/trusted-keys +++ b/contrib/verify-commits/trusted-keys @@ -5,3 +5,4 @@ AF8BE07C7049F3A26B239D5325B3083201782B2F 81291FA67D2C379A006A053FEAB5AF94D9E9ABE7 3F1888C6DCA92A6499C4911FDBA1A67379A1A931 32EE5C4C3FA15CCADB46ABE529D4BCB6416F53EC +FE09B823E6D83A3BC7983EAA2D7F2372E50FE137 diff --git a/contrib/verifysfbinaries/README.md b/contrib/verifybinaries/README.md index 1db3fe52fc..8970f3daa4 100644 --- a/contrib/verifysfbinaries/README.md +++ b/contrib/verifybinaries/README.md @@ -1,6 +1,13 @@ -### Verify Binaries ### +### Verify Binaries This script attempts to download the signature file `SHA256SUMS.asc` from https://bitcoin.org. It first checks if the signature passes, and then downloads the files specified in the file, and checks if the hashes of these files match those that are specified in the signature file. The script returns 0 if everything passes the checks. It returns 1 if either the signature check or the hash check doesn't pass. If an error occurs the return value is 2. + +Usage: + +```sh +./verify.sh bitcoin-core-0.11.2 +./verify.sh bitcoin-core-0.12.0 +``` diff --git a/contrib/verifysfbinaries/verify.sh b/contrib/verifybinaries/verify.sh index 847c507553..657c3bd33c 100755 --- a/contrib/verifysfbinaries/verify.sh +++ b/contrib/verifybinaries/verify.sh @@ -23,9 +23,6 @@ BASEDIR="https://bitcoin.org/bin/" VERSIONPREFIX="bitcoin-core-" RCVERSIONSTRING="rc" -#this URL is used if a version number is not specified as an argument to the script -SIGNATUREFILE="$BASEDIR""$VERSIONPREFIX""0.10.4/""$RCSUBDIR""$SIGNATUREFILENAME" - if [ ! -d "$WORKINGDIR" ]; then mkdir "$WORKINGDIR" fi @@ -53,7 +50,8 @@ if [ -n "$1" ]; then SIGNATUREFILE="$BASEDIR$SIGNATUREFILENAME" else - BASEDIR="${SIGNATUREFILE%/*}/" + echo "Error: need to specify a version on the command line" + exit 2 fi #first we fetch the file containing the signature diff --git a/depends/builders/darwin.mk b/depends/builders/darwin.mk index 200d6ed22a..27f550ab03 100644 --- a/depends/builders/darwin.mk +++ b/depends/builders/darwin.mk @@ -11,7 +11,7 @@ build_darwin_DOWNLOAD = curl --location --fail --connect-timeout $(DOWNLOAD_CONN #darwin host on darwin builder. overrides darwin host preferences. darwin_CC=$(shell xcrun -f clang) -mmacosx-version-min=$(OSX_MIN_VERSION) -darwin_CXX:=$(shell xcrun -f clang++) -mmacosx-version-min=$(OSX_MIN_VERSION) +darwin_CXX:=$(shell xcrun -f clang++) -mmacosx-version-min=$(OSX_MIN_VERSION) -stdlib=libc++ darwin_AR:=$(shell xcrun -f ar) darwin_RANLIB:=$(shell xcrun -f ranlib) darwin_STRIP:=$(shell xcrun -f strip) diff --git a/depends/hosts/darwin.mk b/depends/hosts/darwin.mk index 2958dc50cc..dbe6d00795 100644 --- a/depends/hosts/darwin.mk +++ b/depends/hosts/darwin.mk @@ -3,7 +3,7 @@ OSX_SDK_VERSION=10.9 OSX_SDK=$(SDK_PATH)/MacOSX$(OSX_SDK_VERSION).sdk LD64_VERSION=241.9 darwin_CC=clang -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION) -darwin_CXX=clang++ -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION) +darwin_CXX=clang++ -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION) -stdlib=libc++ darwin_CFLAGS=-pipe darwin_CXXFLAGS=$(darwin_CFLAGS) diff --git a/depends/packages/bdb.mk b/depends/packages/bdb.mk index e2f85ad4fc..200d57314e 100644 --- a/depends/packages/bdb.mk +++ b/depends/packages/bdb.mk @@ -9,6 +9,7 @@ define $(package)_set_vars $(package)_config_opts=--disable-shared --enable-cxx --disable-replication $(package)_config_opts_mingw32=--enable-mingw $(package)_config_opts_linux=--with-pic +$(package)_cxxflags=-std=c++11 endef define $(package)_preprocess_cmds diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk index 215c694b6b..ef1307c241 100644 --- a/depends/packages/boost.mk +++ b/depends/packages/boost.mk @@ -20,7 +20,7 @@ $(package)_archiver_$(host_os)=$($(package)_ar) $(package)_toolset_darwin=darwin $(package)_archiver_darwin=$($(package)_libtool) $(package)_config_libraries=chrono,filesystem,program_options,system,thread,test -$(package)_cxxflags=-fvisibility=hidden +$(package)_cxxflags=-std=c++11 -fvisibility=hidden $(package)_cxxflags_linux=-fPIC endef diff --git a/depends/packages/native_mac_alias.mk b/depends/packages/native_mac_alias.mk index d117c1c9a3..85a8a402bf 100644 --- a/depends/packages/native_mac_alias.mk +++ b/depends/packages/native_mac_alias.mk @@ -5,6 +5,11 @@ $(package)_download_file=v$($(package)_version).tar.bz2 $(package)_file_name=$(package)-$($(package)_version).tar.bz2 $(package)_sha256_hash=87ad827e66790028361e43fc754f68ed041a9bdb214cca03c853f079b04fb120 $(package)_install_libdir=$(build_prefix)/lib/python/dist-packages +$(package)_patches=python3.patch + +define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/python3.patch +endef define $(package)_build_cmds python setup.py build diff --git a/depends/packages/protobuf.mk b/depends/packages/protobuf.mk index 5affad2837..54d3fd9245 100644 --- a/depends/packages/protobuf.mk +++ b/depends/packages/protobuf.mk @@ -4,6 +4,7 @@ $(package)_download_path=$(native_$(package)_download_path) $(package)_file_name=$(native_$(package)_file_name) $(package)_sha256_hash=$(native_$(package)_sha256_hash) $(package)_dependencies=native_$(package) +$(package)_cxxflags=-std=c++11 define $(package)_set_vars $(package)_config_opts=--disable-shared --with-protoc=$(build_prefix)/bin/protoc diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index 77df77b73f..c1fc8e3058 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -31,7 +31,7 @@ $(package)_config_opts += -no-iconv $(package)_config_opts += -no-gif $(package)_config_opts += -no-freetype $(package)_config_opts += -no-nis -$(package)_config_opts += -no-pch +$(package)_config_opts += -pch $(package)_config_opts += -no-qml-debug $(package)_config_opts += -nomake examples $(package)_config_opts += -nomake tests @@ -49,7 +49,7 @@ $(package)_config_opts += -no-sql-sqlite2 $(package)_config_opts += -prefix $(host_prefix) $(package)_config_opts += -hostprefix $(build_prefix) $(package)_config_opts += -bindir $(build_prefix)/bin -$(package)_config_opts += -no-c++11 +$(package)_config_opts += -c++11 $(package)_config_opts += -openssl-linked $(package)_config_opts += -v $(package)_config_opts += -static diff --git a/depends/packages/zeromq.mk b/depends/packages/zeromq.mk index 7b866e9c0f..b3f18db056 100644 --- a/depends/packages/zeromq.mk +++ b/depends/packages/zeromq.mk @@ -7,6 +7,7 @@ $(package)_sha256_hash=e00b2967e074990d0538361cc79084a0a92892df2c6e7585da34e4c61 define $(package)_set_vars $(package)_config_opts=--without-documentation --disable-shared $(package)_config_opts_linux=--with-pic + $(package)_cxxflags=-std=c++11 endef define $(package)_config_cmds diff --git a/depends/patches/native_mac_alias/python3.patch b/depends/patches/native_mac_alias/python3.patch new file mode 100644 index 0000000000..1a32340be5 --- /dev/null +++ b/depends/patches/native_mac_alias/python3.patch @@ -0,0 +1,72 @@ +diff -dur a/mac_alias/alias.py b/mac_alias/alias.py +--- a/mac_alias/alias.py 2015-10-19 12:12:48.000000000 +0200 ++++ b/mac_alias/alias.py 2016-04-03 12:13:12.037159417 +0200 +@@ -243,10 +243,10 @@ + alias = Alias() + alias.appinfo = appinfo + +- alias.volume = VolumeInfo (volname.replace('/',':'), ++ alias.volume = VolumeInfo (volname.decode().replace('/',':'), + voldate, fstype, disktype, + volattrs, volfsid) +- alias.target = TargetInfo (kind, filename.replace('/',':'), ++ alias.target = TargetInfo (kind, filename.decode().replace('/',':'), + folder_cnid, cnid, + crdate, creator_code, type_code) + alias.target.levels_from = levels_from +@@ -261,9 +261,9 @@ + b.read(1) + + if tag == TAG_CARBON_FOLDER_NAME: +- alias.target.folder_name = value.replace('/',':') ++ alias.target.folder_name = value.decode().replace('/',':') + elif tag == TAG_CNID_PATH: +- alias.target.cnid_path = struct.unpack(b'>%uI' % (length // 4), ++ alias.target.cnid_path = struct.unpack('>%uI' % (length // 4), + value) + elif tag == TAG_CARBON_PATH: + alias.target.carbon_path = value +@@ -298,9 +298,9 @@ + alias.target.creation_date \ + = mac_epoch + datetime.timedelta(seconds=seconds) + elif tag == TAG_POSIX_PATH: +- alias.target.posix_path = value ++ alias.target.posix_path = value.decode() + elif tag == TAG_POSIX_PATH_TO_MOUNTPOINT: +- alias.volume.posix_path = value ++ alias.volume.posix_path = value.decode() + elif tag == TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE: + alias.volume.disk_image_alias = Alias.from_bytes(value) + elif tag == TAG_USER_HOME_LENGTH_PREFIX: +@@ -422,13 +422,13 @@ + # (so doing so is ridiculous, and nothing could rely on it). + b.write(struct.pack(b'>h28pI2shI64pII4s4shhI2s10s', + self.target.kind, +- carbon_volname, voldate, ++ carbon_volname, int(voldate), + self.volume.fs_type, + self.volume.disk_type, + self.target.folder_cnid, + carbon_filename, + self.target.cnid, +- crdate, ++ int(crdate), + self.target.creator_code, + self.target.type_code, + self.target.levels_from, +@@ -449,12 +449,12 @@ + + b.write(struct.pack(b'>hhQhhQ', + TAG_HIGH_RES_VOLUME_CREATION_DATE, +- 8, long(voldate * 65536), ++ 8, int(voldate * 65536), + TAG_HIGH_RES_CREATION_DATE, +- 8, long(crdate * 65536))) ++ 8, int(crdate * 65536))) + + if self.target.cnid_path: +- cnid_path = struct.pack(b'>%uI' % len(self.target.cnid_path), ++ cnid_path = struct.pack('>%uI' % len(self.target.cnid_path), + *self.target.cnid_path) + b.write(struct.pack(b'>hh', TAG_CNID_PATH, + len(cnid_path))) diff --git a/doc/build-unix.md b/doc/build-unix.md index dc754fc733..27c57088af 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -124,6 +124,24 @@ libqrencode (optional) can be installed with: Once these are installed, they will be found by configure and a bitcoin-qt executable will be built by default. +Dependency Build Instructions: Fedora +------------------------------------- +Build requirements: + + sudo dnf install libtool make autoconf automake openssl-devel libevent-devel boost-devel libdb4-devel libdb4-cxx-devel + +Optional: + + sudo dnf install miniupnpc-devel + +To build with Qt 5 (recommended) you need the following: + + sudo dnf install qt5-qttools-devel qtr5-qtbase-devel protobuf-devel + +libqrencode (optional) can be installed with: + + sudo dnf install qrencode-devel + Notes ----- The release is built with GCC and then "strip bitcoind" to strip the debug diff --git a/doc/gitian-building.md b/doc/gitian-building.md index 3126218518..13f8ad316b 100644 --- a/doc/gitian-building.md +++ b/doc/gitian-building.md @@ -310,7 +310,7 @@ Clone the git repositories for bitcoin and Gitian. ```bash git clone https://github.com/devrandom/gitian-builder.git git clone https://github.com/bitcoin/bitcoin -git clone https://github.com/bitcoin/gitian.sigs.git +git clone https://github.com/bitcoin-core/gitian.sigs.git ``` Setting up the Gitian image @@ -441,7 +441,7 @@ Then when building, override the remote URLs that gbuild would otherwise pull fr ```bash cd /some/root/path/ -git clone https://github.com/bitcoin/bitcoin-detached-sigs.git +git clone https://github.com/bitcoin-core/bitcoin-detached-sigs.git BTCPATH=/some/root/path/bitcoin.git SIGPATH=/some/root/path/bitcoin-detached-sigs.git @@ -473,5 +473,5 @@ Uploading signatures --------------------- After building and signing you can push your signatures (both the `.assert` and `.assert.sig` files) to the -[bitcoin/gitian.sigs](https://github.com/bitcoin/gitian.sigs/) repository, or if that's not possible create a pull +[bitcoin-core/gitian.sigs](https://github.com/bitcoin-core/gitian.sigs/) repository, or if that's not possible create a pull request. You can also mail the files to Wladimir (laanwj@gmail.com) and he will commit them. diff --git a/doc/release-notes.md b/doc/release-notes.md index 73ad13e722..7d44b8cda9 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -1,6 +1,21 @@ (note: this is a temporary file, to be added-to by anybody, and moved to release-notes at release time) +Bitcoin Core version *version* is now available from: + + <https://bitcoin.org/bin/bitcoin-core-*version*/> + +This is a new major version release, including new features, various bugfixes +and performance improvements, as well as updated translations. + +Please report bugs using the issue tracker at github: + + <https://github.com/bitcoin/bitcoin/issues> + +To receive security and update notifications, please subscribe to: + + <https://bitcoincore.org/en/list/announcements/join/> + Notable changes =============== @@ -28,6 +43,19 @@ RPC low-level changes 32-bit and 64-bit platforms, and the txids were missing in the hashed data. This has been fixed, but this means that the output will be different than from previous versions. +C++11 and Python 3 +------------------- + +Various code modernizations have been done. The Bitcoin Core code base has +started using C++11. This means that a C++11-capable compiler is now needed for +building. Effectively this means GCC 4.7 or higher, or Clang 3.3 or higher. + +When cross-compiling for a target that doesn't have C++11 libraries, configure with +`./configure --enable-glibc-back-compat ... LDFLAGS=-static-libstdc++`. + +For running the functional tests in `qa/rpc-tests`, Python3.4 or higher is now +required. + 0.13.0 Change log ================= diff --git a/doc/release-notes/release-notes-0.12.0.md b/doc/release-notes/release-notes-0.12.0.md index 135cd68a7c..1b7bd06ece 100644 --- a/doc/release-notes/release-notes-0.12.0.md +++ b/doc/release-notes/release-notes-0.12.0.md @@ -61,7 +61,7 @@ Signature validation using libsecp256k1 --------------------------------------- ECDSA signatures inside Bitcoin transactions now use validation using -[libsecp256k1](https://github.com/bitcoin/secp256k1) instead of OpenSSL. +[libsecp256k1](https://github.com/bitcoin-core/secp256k1) instead of OpenSSL. Depending on the platform, this means a significant speedup for raw signature validation speed. The advantage is largest on x86_64, where validation is over diff --git a/doc/release-process.md b/doc/release-process.md index 5a6ac8482b..3bfcc38177 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -1,181 +1,228 @@ Release Process ==================== -* Update translations (ping wumpus, Diapolo or tcatm on IRC) see [translation_process.md](https://github.com/bitcoin/bitcoin/blob/master/doc/translation_process.md#syncing-with-transifex) +Before every release candidate: + +* Update translations (ping wumpus on IRC) see [translation_process.md](https://github.com/bitcoin/bitcoin/blob/master/doc/translation_process.md#syncing-with-transifex) + +Before every minor and major release: + * Update [bips.md](bips.md) to account for changes since the last release. -* Update hardcoded [seeds](/contrib/seeds) +* Update version in sources (see below) +* Write release notes (see below) + +Before every major release: -* * * +* Update hardcoded [seeds](/contrib/seeds/README.md), see [this pull request](https://github.com/bitcoin/bitcoin/pull/7415) for an example. + +### First time / New builders -###First time / New builders Check out the source code in the following directory hierarchy. - cd /path/to/your/toplevel/build - git clone https://github.com/bitcoin/gitian.sigs.git - git clone https://github.com/bitcoin/bitcoin-detached-sigs.git - git clone https://github.com/devrandom/gitian-builder.git - git clone https://github.com/bitcoin/bitcoin.git + cd /path/to/your/toplevel/build + git clone https://github.com/bitcoin-core/gitian.sigs.git + git clone https://github.com/bitcoin-core/bitcoin-detached-sigs.git + git clone https://github.com/devrandom/gitian-builder.git + git clone https://github.com/bitcoin/bitcoin.git + +### Bitcoin maintainers/release engineers, update version in sources + +Update the following: + +- `configure.ac`: + - `_CLIENT_VERSION_MAJOR` + - `_CLIENT_VERSION_MINOR` + - `_CLIENT_VERSION_REVISION` + - Don't forget to set `_CLIENT_VERSION_IS_RELEASE` to `true` +- `src/clientversion.h`: (this mirrors `configure.ac` - see issue #3539) + - `CLIENT_VERSION_MAJOR` + - `CLIENT_VERSION_MINOR` + - `CLIENT_VERSION_REVISION` + - Don't forget to set `CLIENT_VERSION_IS_RELEASE` to `true` +- `doc/README.md` and `doc/README_windows.txt` +- `doc/Doxyfile`: `PROJECT_NUMBER` contains the full version +- `contrib/gitian-descriptors/*.yml`: usually one'd want to do this on master after branching off the release - but be sure to at least do it before a new major release -###Bitcoin maintainers/release engineers, update (commit) version in sources +Write release notes. git shortlog helps a lot, for example: - pushd ./bitcoin - contrib/verifysfbinaries/verify.sh - configure.ac - doc/README* - doc/Doxyfile - contrib/gitian-descriptors/*.yml - src/clientversion.h (change CLIENT_VERSION_IS_RELEASE to true) + git shortlog --no-merges v(current version, e.g. 0.7.2)..v(new version, e.g. 0.8.0) - # tag version in git +(or ping @wumpus on IRC, he has specific tooling to generate the list of merged pulls +and sort them into categories based on labels) - git tag -s v(new version, e.g. 0.8.0) +Generate list of authors: - # write release notes. git shortlog helps a lot, for example: + git log --format='%aN' "$*" | sort -ui | sed -e 's/^/- /' - git shortlog --no-merges v(current version, e.g. 0.7.2)..v(new version, e.g. 0.8.0) - popd +Tag version (or release candidate) in git -* * * + git tag -s v(new version, e.g. 0.8.0) -###Setup and perform Gitian builds +### Setup and perform Gitian builds - Setup Gitian descriptors: +Setup Gitian descriptors: - pushd ./bitcoin - export SIGNER=(your Gitian key, ie bluematt, sipa, etc) - export VERSION=(new version, e.g. 0.8.0) - git fetch - git checkout v${VERSION} - popd + pushd ./bitcoin + export SIGNER=(your Gitian key, ie bluematt, sipa, etc) + export VERSION=(new version, e.g. 0.8.0) + git fetch + git checkout v${VERSION} + popd - Ensure your gitian.sigs are up-to-date if you wish to gverify your builds against other Gitian signatures. +Ensure your gitian.sigs are up-to-date if you wish to gverify your builds against other Gitian signatures. - pushd ./gitian.sigs - git pull - popd + pushd ./gitian.sigs + git pull + popd - Ensure gitian-builder is up-to-date to take advantage of new caching features (`e9741525c` or later is recommended). +Ensure gitian-builder is up-to-date to take advantage of new caching features (`e9741525c` or later is recommended). - pushd ./gitian-builder - git pull + pushd ./gitian-builder + git pull + popd -###Fetch and create inputs: (first time, or when dependency versions change) +### Fetch and create 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 + pushd ./gitian-builder + 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 + popd - Register and download the Apple SDK: see [OS X readme](README_osx.txt) for details. +Register and download the Apple SDK: see [OS X readme](README_osx.txt) for details. - https://developer.apple.com/devcenter/download.action?path=/Developer_Tools/xcode_6.1.1/xcode_6.1.1.dmg +https://developer.apple.com/devcenter/download.action?path=/Developer_Tools/xcode_6.1.1/xcode_6.1.1.dmg - Using a Mac, create a tarball for the 10.9 SDK and copy it to the inputs directory: +Using a Mac, create a tarball for the 10.9 SDK and copy it to the inputs directory: - tar -C /Volumes/Xcode/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ -czf MacOSX10.9.sdk.tar.gz MacOSX10.9.sdk + tar -C /Volumes/Xcode/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ -czf MacOSX10.9.sdk.tar.gz MacOSX10.9.sdk -###Optional: Seed the Gitian sources cache and offline git repositories +### Optional: Seed the Gitian sources cache and offline git repositories By default, Gitian will fetch source files as needed. To cache them ahead of time: - make -C ../bitcoin/depends download SOURCES_PATH=`pwd`/cache/common + pushd ./gitian-builder + make -C ../bitcoin/depends download SOURCES_PATH=`pwd`/cache/common + popd Only missing files will be fetched, so this is safe to re-run for each build. NOTE: Offline builds must use the --url flag to ensure Gitian fetches only from local URLs. For example: -``` -./bin/gbuild --url bitcoin=/path/to/bitcoin,signature=/path/to/sigs {rest of arguments} -``` + + pushd ./gitian-builder + ./bin/gbuild --url bitcoin=/path/to/bitcoin,signature=/path/to/sigs {rest of arguments} + popd + The gbuild invocations below <b>DO NOT DO THIS</b> by default. -###Build and sign Bitcoin Core for Linux, Windows, and OS X: +### Build and sign Bitcoin Core for Linux, Windows, and OS X: - ./bin/gbuild --commit bitcoin=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml - ./bin/gsign --signer $SIGNER --release ${VERSION}-linux --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml + pushd ./gitian-builder + ./bin/gbuild --commit bitcoin=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml + ./bin/gsign --signer $SIGNER --release ${VERSION}-linux --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml 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-unsigned --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win.yml + ./bin/gbuild --commit bitcoin=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-win.yml + ./bin/gsign --signer $SIGNER --release ${VERSION}-win-unsigned --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win.yml mv build/out/bitcoin-*-win-unsigned.tar.gz inputs/bitcoin-win-unsigned.tar.gz mv build/out/bitcoin-*.zip build/out/bitcoin-*.exe ../ - ./bin/gbuild --commit bitcoin=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml - ./bin/gsign --signer $SIGNER --release ${VERSION}-osx-unsigned --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml + ./bin/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-*-osx-unsigned.tar.gz inputs/bitcoin-osx-unsigned.tar.gz mv build/out/bitcoin-*.tar.gz build/out/bitcoin-*.dmg ../ + popd - Build output expected: +Build output expected: - 1. source tarball (bitcoin-${VERSION}.tar.gz) - 2. linux 32-bit and 64-bit dist tarballs (bitcoin-${VERSION}-linux[32|64].tar.gz) - 3. windows 32-bit and 64-bit unsigned installers and dist zips (bitcoin-${VERSION}-win[32|64]-setup-unsigned.exe, bitcoin-${VERSION}-win[32|64].zip) - 4. OS X unsigned installer and dist tarball (bitcoin-${VERSION}-osx-unsigned.dmg, bitcoin-${VERSION}-osx64.tar.gz) - 5. Gitian signatures (in gitian.sigs/${VERSION}-<linux|{win,osx}-unsigned>/(your Gitian key)/ + 1. source tarball (`bitcoin-${VERSION}.tar.gz`) + 2. linux 32-bit and 64-bit dist tarballs (`bitcoin-${VERSION}-linux[32|64].tar.gz`) + 3. windows 32-bit and 64-bit unsigned installers and dist zips (`bitcoin-${VERSION}-win[32|64]-setup-unsigned.exe`, `bitcoin-${VERSION}-win[32|64].zip`) + 4. OS X unsigned installer and dist tarball (`bitcoin-${VERSION}-osx-unsigned.dmg`, `bitcoin-${VERSION}-osx64.tar.gz`) + 5. Gitian signatures (in `gitian.sigs/${VERSION}-<linux|{win,osx}-unsigned>/(your Gitian key)/`) -###Verify other gitian builders signatures to your own. (Optional) +### Verify other gitian builders signatures to your own. (Optional) - Add other gitian builders keys to your gpg keyring +Add other gitian builders keys to your gpg keyring - gpg --import ../bitcoin/contrib/gitian-keys/*.pgp + gpg --import bitcoin/contrib/gitian-keys/*.pgp - Verify the signatures +Verify the signatures - ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-linux ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml - ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-win-unsigned ../bitcoin/contrib/gitian-descriptors/gitian-win.yml - ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-osx-unsigned ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml + pushd ./gitian-builder + ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-linux ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml + ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-win-unsigned ../bitcoin/contrib/gitian-descriptors/gitian-win.yml + ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-osx-unsigned ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml + popd - popd - -###Next steps: +### Next steps: Commit your signature to gitian.sigs: - pushd gitian.sigs - git add ${VERSION}-linux/${SIGNER} - git add ${VERSION}-win-unsigned/${SIGNER} - git add ${VERSION}-osx-unsigned/${SIGNER} - git commit -a - git push # Assuming you can push to the gitian.sigs tree - popd - - Wait for Windows/OS X detached signatures: - Once the Windows/OS X 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](https://github.com/bitcoin/bitcoin-detached-sigs) repository, which can be combined with the unsigned apps to create signed binaries. - - Create (and optionally verify) the signed OS X binary: - - pushd ./gitian-builder - ./bin/gbuild -i --commit signature=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml - ./bin/gsign --signer $SIGNER --release ${VERSION}-osx-signed --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml - ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-osx-signed ../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml - mv build/out/bitcoin-osx-signed.dmg ../bitcoin-${VERSION}-osx.dmg - popd - - Create (and optionally verify) the signed Windows binaries: - - pushd ./gitian-builder - ./bin/gbuild -i --commit signature=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml - ./bin/gsign --signer $SIGNER --release ${VERSION}-win-signed --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml - ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-win-signed ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml - mv build/out/bitcoin-*win64-setup.exe ../bitcoin-${VERSION}-win64-setup.exe - mv build/out/bitcoin-*win32-setup.exe ../bitcoin-${VERSION}-win32-setup.exe - popd + pushd gitian.sigs + git add ${VERSION}-linux/${SIGNER} + git add ${VERSION}-win-unsigned/${SIGNER} + git add ${VERSION}-osx-unsigned/${SIGNER} + git commit -a + git push # Assuming you can push to the gitian.sigs tree + popd -Commit your signature for the signed OS X/Windows binaries: +Wait for Windows/OS X detached signatures: + +- Once the Windows/OS X 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](https://github.com/bitcoin-core/bitcoin-detached-sigs) repository, which can be combined with the unsigned apps to create signed binaries. + +Create (and optionally verify) the signed OS X binary: - 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 + pushd ./gitian-builder + ./bin/gbuild -i --commit signature=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml + ./bin/gsign --signer $SIGNER --release ${VERSION}-osx-signed --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml + ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-osx-signed ../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml + mv build/out/bitcoin-osx-signed.dmg ../bitcoin-${VERSION}-osx.dmg + popd -------------------------------------------------------------------------- +Create (and optionally verify) the signed Windows binaries: + + pushd ./gitian-builder + ./bin/gbuild -i --commit signature=v${VERSION} ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml + ./bin/gsign --signer $SIGNER --release ${VERSION}-win-signed --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml + ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-win-signed ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml + mv build/out/bitcoin-*win64-setup.exe ../bitcoin-${VERSION}-win64-setup.exe + mv build/out/bitcoin-*win32-setup.exe ../bitcoin-${VERSION}-win32-setup.exe + popd + +Commit your signature for the signed OS X/Windows binaries: + + pushd gitian.sigs + git add ${VERSION}-osx-signed/${SIGNER} + git add ${VERSION}-win-signed/${SIGNER} + git commit -a + git push # Assuming you can push to the gitian.sigs tree + popd ### After 3 or more people have gitian-built and their results match: - Create `SHA256SUMS.asc` for the builds, and GPG-sign it: + ```bash sha256sum * > SHA256SUMS +``` + +The list of files should be: +``` +bitcoin-${VERSION}-linux32.tar.gz +bitcoin-${VERSION}-linux64.tar.gz +bitcoin-${VERSION}-osx64.tar.gz +bitcoin-${VERSION}-osx.dmg +bitcoin-${VERSION}.tar.gz +bitcoin-${VERSION}-win32-setup.exe +bitcoin-${VERSION}-win32.zip +bitcoin-${VERSION}-win64-setup.exe +bitcoin-${VERSION}-win64.zip +``` + +- GPG-sign it, delete the unsigned file: +``` gpg --digest-algo sha256 --clearsign SHA256SUMS # outputs SHA256SUMS.asc rm SHA256SUMS ``` @@ -185,6 +232,15 @@ Note: check that SHA256SUMS itself doesn't end up in SHA256SUMS, which is a spur - Upload zips and installers, as well as `SHA256SUMS.asc` from last step, to the bitcoin.org server into `/var/www/bin/bitcoin-core-${VERSION}` +- A `.torrent` will appear in the directory after a few minutes. Optionally help seed this torrent. To get the `magnet:` URI use: +```bash +transmission-show -m <torrent file> +``` +Insert the magnet URI into the announcement sent to mailing lists. This permits +people without access to `bitcoin.org` to download the binary distribution. +Also put it into the `optional_magnetlink:` slot in the YAML file for +bitcoin.org (see below for bitcoin.org update instructions). + - Update bitcoin.org version - First, check to see if the Bitcoin.org maintainers have prepared a @@ -202,16 +258,18 @@ Note: check that SHA256SUMS itself doesn't end up in SHA256SUMS, which is a spur - Announce the release: - - Release sticky on bitcointalk: https://bitcointalk.org/index.php?board=1.0 + - bitcoin-dev and bitcoin-core-dev mailing list + + - Bitcoin Core announcements list https://bitcoincore.org/en/list/announcements/join/ - - Bitcoin-development mailing list + - bitcoincore.org blog post - Update title of #bitcoin on Freenode IRC - - Optionally reddit /r/Bitcoin, ... but this will usually sort out itself + - Optionally twitter, reddit /r/Bitcoin, ... but this will usually sort out itself -- Notify BlueMatt so that he can start building [the PPAs](https://launchpad.net/~bitcoin/+archive/ubuntu/bitcoin) + - Notify BlueMatt so that he can start building [the PPAs](https://launchpad.net/~bitcoin/+archive/ubuntu/bitcoin) -- Add release notes for the new version to the directory `doc/release-notes` in git master + - Add release notes for the new version to the directory `doc/release-notes` in git master -- Celebrate + - Celebrate diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index c0637209e3..26142c35e9 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -123,6 +123,7 @@ testScripts = [ 'abandonconflict.py', 'p2p-versionbits-warning.py', 'importprunedfunds.py', + 'signmessages.py' ] if ENABLE_ZMQ: testScripts.append('zmq_test.py') diff --git a/qa/rpc-tests/signmessages.py b/qa/rpc-tests/signmessages.py new file mode 100755 index 0000000000..ff22f35300 --- /dev/null +++ b/qa/rpc-tests/signmessages.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python2 +# Copyright (c) 2016 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * + + +class SignMessagesTest(BitcoinTestFramework): + """Tests RPC commands for signing and verifying messages.""" + + def setup_chain(self): + print('Initializing test directory ' + self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 1) + + def setup_network(self, split=False): + self.nodes = start_nodes(1, self.options.tmpdir) + self.is_network_split = False + + def run_test(self): + message = 'This is just a test message' + + # Test the signing with a privkey + privKey = 'cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N' + address = 'mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB' + signature = self.nodes[0].signmessagewithprivkey(privKey, message) + + # Verify the message + assert(self.nodes[0].verifymessage(address, signature, message)) + + # Test the signing with an address with wallet + address = self.nodes[0].getnewaddress() + signature = self.nodes[0].signmessage(address, message) + + # Verify the message + assert(self.nodes[0].verifymessage(address, signature, message)) + +if __name__ == '__main__': + SignMessagesTest().main() diff --git a/qa/rpc-tests/smartfees.py b/qa/rpc-tests/smartfees.py index 2c064ad8a0..7239e5a0da 100755 --- a/qa/rpc-tests/smartfees.py +++ b/qa/rpc-tests/smartfees.py @@ -7,6 +7,7 @@ # Test fee estimation code # +from collections import OrderedDict from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -22,7 +23,7 @@ SCRIPT_SIG = ["0451025175", "0451025275"] def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee_increment): ''' Create and send a transaction with a random fee. - The transaction pays to a trival P2SH script, and assumes that its inputs + The transaction pays to a trivial P2SH script, and assumes that its inputs are of the same form. The function takes a list of confirmed outputs and unconfirmed outputs and attempts to use the confirmed list first for its inputs. @@ -49,10 +50,10 @@ def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee if total_in <= amount + fee: raise RuntimeError("Insufficient funds: need %d, have %d"%(amount+fee, total_in)) outputs = {} - outputs[P2SH_1] = total_in - amount - fee - outputs[P2SH_2] = amount + outputs = OrderedDict([(P2SH_1, total_in - amount - fee), + (P2SH_2, amount)]) rawtx = from_node.createrawtransaction(inputs, outputs) - # Createrawtransaction constructions a transaction that is ready to be signed + # createrawtransaction constructs a transaction that is ready to be signed. # These transactions don't need to be signed, but we still have to insert the ScriptSig # that will satisfy the ScriptPubKey. completetx = rawtx[0:10] @@ -78,12 +79,10 @@ def split_inputs(from_node, txins, txouts, initial_split = False): ''' prevtxout = txins.pop() inputs = [] - outputs = {} inputs.append({ "txid" : prevtxout["txid"], "vout" : prevtxout["vout"] }) half_change = satoshi_round(prevtxout["amount"]/2) rem_change = prevtxout["amount"] - half_change - Decimal("0.00001000") - outputs[P2SH_1] = half_change - outputs[P2SH_2] = rem_change + outputs = OrderedDict([(P2SH_1, half_change), (P2SH_2, rem_change)]) rawtx = from_node.createrawtransaction(inputs, outputs) # If this is the initial split we actually need to sign the transaction # Otherwise we just need to insert the property ScriptSig @@ -224,7 +223,7 @@ class EstimateFeeTest(BitcoinTestFramework): sync_mempools(self.nodes[0:3],.1) mined = mining_node.getblock(mining_node.generate(1)[0],True)["tx"] sync_blocks(self.nodes[0:3],.1) - #update which txouts are confirmed + # update which txouts are confirmed newmem = [] for utx in self.memutxo: if utx["txid"] in mined: diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index 5ee5b13275..30aecfd445 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -93,7 +93,7 @@ def deser_uint256(f): def ser_uint256(u): - rs = "" + rs = b"" for i in xrange(8): rs += struct.pack("<I", u & 0xFFFFFFFFL) u >>= 32 @@ -191,7 +191,7 @@ def deser_string_vector(f): def ser_string_vector(l): - r = "" + r = b"" if len(l) < 253: r = struct.pack("B", len(l)) elif len(l) < 0x10000: @@ -624,7 +624,7 @@ class CAlert(object): self.vchSig = deser_string(f) def serialize(self): - r = "" + r = b"" r += ser_string(self.vchMsg) r += ser_string(self.vchSig) return r @@ -983,25 +983,28 @@ class msg_headers(object): class msg_reject(object): command = b"reject" + REJECT_MALFORMED = 1 def __init__(self): self.message = b"" self.code = 0 - self.reason = "" + self.reason = b"" self.data = 0L def deserialize(self, f): self.message = deser_string(f) self.code = struct.unpack("<B", f.read(1))[0] self.reason = deser_string(f) - if (self.message == "block" or self.message == "tx"): + if (self.code != self.REJECT_MALFORMED and + (self.message == b"block" or self.message == b"tx")): self.data = deser_uint256(f) def serialize(self): r = ser_string(self.message) r += struct.pack("<B", self.code) r += ser_string(self.reason) - if (self.message == "block" or self.message == "tx"): + if (self.code != self.REJECT_MALFORMED and + (self.message == b"block" or self.message == b"tx")): r += ser_uint256(self.data) return r diff --git a/qa/rpc-tests/test_framework/test_framework.py b/qa/rpc-tests/test_framework/test_framework.py index 3b08cd1384..18ecf64b06 100755 --- a/qa/rpc-tests/test_framework/test_framework.py +++ b/qa/rpc-tests/test_framework/test_framework.py @@ -146,7 +146,7 @@ class BitcoinTestFramework(object): print("key not found: "+ str(e)) traceback.print_tb(sys.exc_info()[2]) except Exception as e: - print("Unexpected exception caught during testing: "+str(e)) + print("Unexpected exception caught during testing: " + repr(e)) traceback.print_tb(sys.exc_info()[2]) if not self.options.noshutdown: diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index 27891f7f4c..baa1ed6790 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -487,7 +487,7 @@ def assert_array_result(object_array, to_match, expected, should_not_find = Fals in object_array """ if should_not_find == True: - expected = { } + assert_equal(expected, { }) num_matched = 0 for item in object_array: all_match = True diff --git a/src/Makefile.am b/src/Makefile.am index c1912dafc5..3c056386fa 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,6 +3,7 @@ DIST_SUBDIRS = secp256k1 univalue AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) AM_CXXFLAGS = $(HARDENED_CXXFLAGS) AM_CPPFLAGS = $(HARDENED_CPPFLAGS) +EXTRA_LIBRARIES = if EMBEDDED_UNIVALUE LIBUNIVALUE = univalue/libunivalue.la @@ -13,21 +14,6 @@ else LIBUNIVALUE = $(UNIVALUE_LIBS) endif -if EMBEDDED_LEVELDB -LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/include -LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/helpers/memenv -LIBLEVELDB += $(builddir)/leveldb/libleveldb.a -LIBMEMENV += $(builddir)/leveldb/libmemenv.a - -# NOTE: This dependency is not strictly necessary, but without it make may try to build both in parallel, which breaks the LevelDB build system in a race -$(LIBLEVELDB): $(LIBMEMENV) - -$(LIBLEVELDB) $(LIBMEMENV): - @echo "Building LevelDB ..." && $(MAKE) -C $(@D) $(@F) CXX="$(CXX)" \ - CC="$(CC)" PLATFORM=$(TARGET_OS) AR="$(AR)" $(LEVELDB_TARGET_FLAGS) \ - OPT="$(AM_CXXFLAGS) $(PIE_FLAGS) $(CXXFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) -D__STDC_LIMIT_MACROS" -endif - BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS) @@ -49,7 +35,7 @@ $(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*) # Make is not made aware of per-object dependencies to avoid limiting building parallelization # But to build the less dependent modules first, we manually select their order here: -EXTRA_LIBRARIES = \ +EXTRA_LIBRARIES += \ crypto/libbitcoin_crypto.a \ libbitcoin_util.a \ libbitcoin_common.a \ @@ -482,6 +468,10 @@ endif @test -f $(PROTOC) $(AM_V_GEN) $(PROTOC) --cpp_out=$(@D) --proto_path=$(<D) $< +if EMBEDDED_LEVELDB +include Makefile.leveldb.include +endif + if ENABLE_TESTS include Makefile.test.include endif diff --git a/src/Makefile.leveldb.include b/src/Makefile.leveldb.include new file mode 100644 index 0000000000..88bb0c1932 --- /dev/null +++ b/src/Makefile.leveldb.include @@ -0,0 +1,78 @@ +LIBLEVELDB_INT = leveldb/libleveldb.a +LIBMEMENV_INT = leveldb/libmemenv.a + +EXTRA_LIBRARIES += $(LIBLEVELDB_INT) +EXTRA_LIBRARIES += $(LIBMEMENV_INT) + +LIBLEVELDB += $(LIBLEVELDB_INT) +LIBMEMENV += $(LIBMEMENV_INT) + +LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/include +LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/helpers/memenv + +LEVELDB_CPPFLAGS_INT = +LEVELDB_CPPFLAGS_INT += -I$(srcdir)/leveldb +LEVELDB_CPPFLAGS_INT += $(LEVELDB_TARGET_FLAGS) +LEVELDB_CPPFLAGS_INT += -DLEVELDB_ATOMIC_PRESENT +LEVELDB_CPPFLAGS_INT += -D__STDC_LIMIT_MACROS + +if TARGET_WINDOWS +LEVELDB_CPPFLAGS_INT += -DLEVELDB_PLATFORM_WINDOWS -DWINVER=0x0500 -D__USE_MINGW_ANSI_STDIO=1 +else +LEVELDB_CPPFLAGS_INT += -DLEVELDB_PLATFORM_POSIX +endif + +leveldb_libleveldb_a_CPPFLAGS = $(AM_CPPFLAGS) $(LEVELDB_CPPFLAGS_INT) $(LEVELDB_CPPFLAGS) +leveldb_libleveldb_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) + +leveldb_libleveldb_a_SOURCES= +leveldb_libleveldb_a_SOURCES += leveldb/db/builder.cc +leveldb_libleveldb_a_SOURCES += leveldb/db/c.cc +leveldb_libleveldb_a_SOURCES += leveldb/db/dbformat.cc +leveldb_libleveldb_a_SOURCES += leveldb/db/db_impl.cc +leveldb_libleveldb_a_SOURCES += leveldb/db/db_iter.cc +leveldb_libleveldb_a_SOURCES += leveldb/db/dumpfile.cc +leveldb_libleveldb_a_SOURCES += leveldb/db/filename.cc +leveldb_libleveldb_a_SOURCES += leveldb/db/log_reader.cc +leveldb_libleveldb_a_SOURCES += leveldb/db/log_writer.cc +leveldb_libleveldb_a_SOURCES += leveldb/db/memtable.cc +leveldb_libleveldb_a_SOURCES += leveldb/db/repair.cc +leveldb_libleveldb_a_SOURCES += leveldb/db/table_cache.cc +leveldb_libleveldb_a_SOURCES += leveldb/db/version_edit.cc +leveldb_libleveldb_a_SOURCES += leveldb/db/version_set.cc +leveldb_libleveldb_a_SOURCES += leveldb/db/write_batch.cc +leveldb_libleveldb_a_SOURCES += leveldb/table/block_builder.cc +leveldb_libleveldb_a_SOURCES += leveldb/table/block.cc +leveldb_libleveldb_a_SOURCES += leveldb/table/filter_block.cc +leveldb_libleveldb_a_SOURCES += leveldb/table/format.cc +leveldb_libleveldb_a_SOURCES += leveldb/table/iterator.cc +leveldb_libleveldb_a_SOURCES += leveldb/table/merger.cc +leveldb_libleveldb_a_SOURCES += leveldb/table/table_builder.cc +leveldb_libleveldb_a_SOURCES += leveldb/table/table.cc +leveldb_libleveldb_a_SOURCES += leveldb/table/two_level_iterator.cc +leveldb_libleveldb_a_SOURCES += leveldb/util/arena.cc +leveldb_libleveldb_a_SOURCES += leveldb/util/bloom.cc +leveldb_libleveldb_a_SOURCES += leveldb/util/cache.cc +leveldb_libleveldb_a_SOURCES += leveldb/util/coding.cc +leveldb_libleveldb_a_SOURCES += leveldb/util/comparator.cc +leveldb_libleveldb_a_SOURCES += leveldb/util/crc32c.cc +leveldb_libleveldb_a_SOURCES += leveldb/util/env.cc +leveldb_libleveldb_a_SOURCES += leveldb/util/env_posix.cc +leveldb_libleveldb_a_SOURCES += leveldb/util/env_win.cc +leveldb_libleveldb_a_SOURCES += leveldb/util/filter_policy.cc +leveldb_libleveldb_a_SOURCES += leveldb/util/hash.cc +leveldb_libleveldb_a_SOURCES += leveldb/util/histogram.cc +leveldb_libleveldb_a_SOURCES += leveldb/util/logging.cc +leveldb_libleveldb_a_SOURCES += leveldb/util/options.cc +leveldb_libleveldb_a_SOURCES += leveldb/util/status.cc + +if TARGET_WINDOWS +leveldb_libleveldb_a_SOURCES += leveldb/util/env_win.cc +leveldb_libleveldb_a_SOURCES += leveldb/port/port_win.cc +else +leveldb_libleveldb_a_SOURCES += leveldb/port/port_posix.cc +endif + +leveldb_libmemenv_a_CPPFLAGS = $(leveldb_libleveldb_a_CPPFLAGS) +leveldb_libmemenv_a_CXXFLAGS = $(leveldb_libleveldb_a_CXXFLAGS) +leveldb_libmemenv_a_SOURCES = leveldb/helpers/memenv/memenv.cc diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 8443fe697b..3b39919441 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -424,11 +424,11 @@ ui_%.h: %.ui $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(UIC) -o $@ $< || (echo "Error creating $@"; false) %.moc: %.cpp - $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(MOC) $(QT_INCLUDES) $(MOC_DEFS) $< | \ + $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(MOC) $(DEFAULT_INCLUDES) $(QT_INCLUDES) $(MOC_DEFS) $< | \ $(SED) -e '/^\*\*.*Created:/d' -e '/^\*\*.*by:/d' > $@ moc_%.cpp: %.h - $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(MOC) $(QT_INCLUDES) $(MOC_DEFS) $< | \ + $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(MOC) $(DEFAULT_INCLUDES) $(QT_INCLUDES) $(MOC_DEFS) $< | \ $(SED) -e '/^\*\*.*Created:/d' -e '/^\*\*.*by:/d' > $@ %.qm: %.ts diff --git a/src/base58.cpp b/src/base58.cpp index d81c26092c..d1d60a6f1d 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -177,7 +177,7 @@ bool CBase58Data::SetString(const char* psz, unsigned int nVersionBytes) vchData.resize(vchTemp.size() - nVersionBytes); if (!vchData.empty()) memcpy(&vchData[0], &vchTemp[nVersionBytes], vchData.size()); - memory_cleanse(&vchTemp[0], vchData.size()); + memory_cleanse(&vchTemp[0], vchTemp.size()); return true; } diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 49935699fd..a04101d3ed 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -314,7 +314,7 @@ int main(int argc, char* argv[]) SetupEnvironment(); if (!SetupNetworking()) { fprintf(stderr, "Error: Initializing networking failed\n"); - exit(1); + return EXIT_FAILURE; } try { diff --git a/src/chain.h b/src/chain.h index 5b9605a80b..017d4fe457 100644 --- a/src/chain.h +++ b/src/chain.h @@ -54,7 +54,7 @@ struct CDiskBlockPos }; -enum BlockStatus { +enum BlockStatus: uint32_t { //! Unused. BLOCK_VALID_UNKNOWN = 0, diff --git a/src/compressor.h b/src/compressor.h index 4a72090830..fa702f0dfa 100644 --- a/src/compressor.h +++ b/src/compressor.h @@ -86,8 +86,14 @@ public: return; } nSize -= nSpecialScripts; - script.resize(nSize); - s >> REF(CFlatData(script)); + if (nSize > MAX_SCRIPT_SIZE) { + // Overly long script, replace with a short invalid one + script << OP_RETURN; + s.ignore(nSize); + } else { + script.resize(nSize); + s >> REF(CFlatData(script)); + } } }; diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp index 1907e2fa78..09c68fbe55 100644 --- a/src/dbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -15,20 +15,6 @@ #include <memenv.h> #include <stdint.h> -void HandleError(const leveldb::Status& status) throw(dbwrapper_error) -{ - if (status.ok()) - return; - LogPrintf("%s\n", status.ToString()); - if (status.IsCorruption()) - throw dbwrapper_error("Database corrupted"); - if (status.IsIOError()) - throw dbwrapper_error("Database I/O error"); - if (status.IsNotFound()) - throw dbwrapper_error("Database entry missing"); - throw dbwrapper_error("Unknown database error"); -} - static leveldb::Options GetOptions(size_t nCacheSize) { leveldb::Options options; @@ -61,13 +47,13 @@ CDBWrapper::CDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, b if (fWipe) { LogPrintf("Wiping LevelDB in %s\n", path.string()); leveldb::Status result = leveldb::DestroyDB(path.string(), options); - HandleError(result); + dbwrapper_private::HandleError(result); } TryCreateDirectory(path); LogPrintf("Opening LevelDB in %s\n", path.string()); } leveldb::Status status = leveldb::DB::Open(options, path.string(), &pdb); - HandleError(status); + dbwrapper_private::HandleError(status); LogPrintf("Opened LevelDB successfully\n"); // The base-case obfuscation key, which is a noop. @@ -84,10 +70,10 @@ CDBWrapper::CDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, b Write(OBFUSCATE_KEY_KEY, new_key); obfuscate_key = new_key; - LogPrintf("Wrote new obfuscate key for %s: %s\n", path.string(), GetObfuscateKeyHex()); + LogPrintf("Wrote new obfuscate key for %s: %s\n", path.string(), HexStr(obfuscate_key)); } - LogPrintf("Using obfuscation key for %s: %s\n", path.string(), GetObfuscateKeyHex()); + LogPrintf("Using obfuscation key for %s: %s\n", path.string(), HexStr(obfuscate_key)); } CDBWrapper::~CDBWrapper() @@ -102,10 +88,10 @@ CDBWrapper::~CDBWrapper() options.env = NULL; } -bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync) throw(dbwrapper_error) +bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync) { leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch); - HandleError(status); + dbwrapper_private::HandleError(status); return true; } @@ -136,17 +122,30 @@ bool CDBWrapper::IsEmpty() return !(it->Valid()); } -const std::vector<unsigned char>& CDBWrapper::GetObfuscateKey() const +CDBIterator::~CDBIterator() { delete piter; } +bool CDBIterator::Valid() { return piter->Valid(); } +void CDBIterator::SeekToFirst() { piter->SeekToFirst(); } +void CDBIterator::Next() { piter->Next(); } + +namespace dbwrapper_private { + +void HandleError(const leveldb::Status& status) { - return obfuscate_key; + if (status.ok()) + return; + LogPrintf("%s\n", status.ToString()); + if (status.IsCorruption()) + throw dbwrapper_error("Database corrupted"); + if (status.IsIOError()) + throw dbwrapper_error("Database I/O error"); + if (status.IsNotFound()) + throw dbwrapper_error("Database entry missing"); + throw dbwrapper_error("Unknown database error"); } -std::string CDBWrapper::GetObfuscateKeyHex() const +const std::vector<unsigned char>& GetObfuscateKey(const CDBWrapper &w) { - return HexStr(obfuscate_key); + return w.obfuscate_key; } -CDBIterator::~CDBIterator() { delete piter; } -bool CDBIterator::Valid() { return piter->Valid(); } -void CDBIterator::SeekToFirst() { piter->SeekToFirst(); } -void CDBIterator::Next() { piter->Next(); } +}; diff --git a/src/dbwrapper.h b/src/dbwrapper.h index 5e7313f7eb..a0779d3ab9 100644 --- a/src/dbwrapper.h +++ b/src/dbwrapper.h @@ -23,7 +23,23 @@ public: dbwrapper_error(const std::string& msg) : std::runtime_error(msg) {} }; -void HandleError(const leveldb::Status& status) throw(dbwrapper_error); +class CDBWrapper; + +/** These should be considered an implementation detail of the specific database. + */ +namespace dbwrapper_private { + +/** Handle database error by throwing dbwrapper_error exception. + */ +void HandleError(const leveldb::Status& status); + +/** Work around circular dependency, as well as for testing in dbwrapper_tests. + * Database obfuscation should be considered an implementation detail of the + * specific database. + */ +const std::vector<unsigned char>& GetObfuscateKey(const CDBWrapper &w); + +}; /** Batch of changes queued to be written to a CDBWrapper */ class CDBBatch @@ -31,14 +47,14 @@ class CDBBatch friend class CDBWrapper; private: + const CDBWrapper &parent; leveldb::WriteBatch batch; - const std::vector<unsigned char> *obfuscate_key; public: /** - * @param[in] obfuscate_key If passed, XOR data with this key. + * @param[in] parent CDBWrapper that this batch is to be submitted to */ - CDBBatch(const std::vector<unsigned char> *obfuscate_key) : obfuscate_key(obfuscate_key) { }; + CDBBatch(const CDBWrapper &parent) : parent(parent) { }; template <typename K, typename V> void Write(const K& key, const V& value) @@ -51,7 +67,7 @@ public: CDataStream ssValue(SER_DISK, CLIENT_VERSION); ssValue.reserve(ssValue.GetSerializeSize(value)); ssValue << value; - ssValue.Xor(*obfuscate_key); + ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent)); leveldb::Slice slValue(&ssValue[0], ssValue.size()); batch.Put(slKey, slValue); @@ -72,17 +88,17 @@ public: class CDBIterator { private: + const CDBWrapper &parent; leveldb::Iterator *piter; - const std::vector<unsigned char> *obfuscate_key; public: /** + * @param[in] parent Parent CDBWrapper instance. * @param[in] piterIn The original leveldb iterator. - * @param[in] obfuscate_key If passed, XOR data with this key. */ - CDBIterator(leveldb::Iterator *piterIn, const std::vector<unsigned char>* obfuscate_key) : - piter(piterIn), obfuscate_key(obfuscate_key) { }; + CDBIterator(const CDBWrapper &parent, leveldb::Iterator *piterIn) : + parent(parent), piter(piterIn) { }; ~CDBIterator(); bool Valid(); @@ -118,7 +134,7 @@ public: leveldb::Slice slValue = piter->value(); try { CDataStream ssValue(slValue.data(), slValue.data() + slValue.size(), SER_DISK, CLIENT_VERSION); - ssValue.Xor(*obfuscate_key); + ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent)); ssValue >> value; } catch (const std::exception&) { return false; @@ -134,6 +150,7 @@ public: class CDBWrapper { + friend const std::vector<unsigned char>& dbwrapper_private::GetObfuscateKey(const CDBWrapper &w); private: //! custom environment this database is using (may be NULL in case of default environment) leveldb::Env* penv; @@ -180,7 +197,7 @@ public: ~CDBWrapper(); template <typename K, typename V> - bool Read(const K& key, V& value) const throw(dbwrapper_error) + bool Read(const K& key, V& value) const { CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(ssKey.GetSerializeSize(key)); @@ -193,7 +210,7 @@ public: if (status.IsNotFound()) return false; LogPrintf("LevelDB read failure: %s\n", status.ToString()); - HandleError(status); + dbwrapper_private::HandleError(status); } try { CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION); @@ -206,15 +223,15 @@ public: } template <typename K, typename V> - bool Write(const K& key, const V& value, bool fSync = false) throw(dbwrapper_error) + bool Write(const K& key, const V& value, bool fSync = false) { - CDBBatch batch(&obfuscate_key); + CDBBatch batch(*this); batch.Write(key, value); return WriteBatch(batch, fSync); } template <typename K> - bool Exists(const K& key) const throw(dbwrapper_error) + bool Exists(const K& key) const { CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(ssKey.GetSerializeSize(key)); @@ -227,20 +244,20 @@ public: if (status.IsNotFound()) return false; LogPrintf("LevelDB read failure: %s\n", status.ToString()); - HandleError(status); + dbwrapper_private::HandleError(status); } return true; } template <typename K> - bool Erase(const K& key, bool fSync = false) throw(dbwrapper_error) + bool Erase(const K& key, bool fSync = false) { - CDBBatch batch(&obfuscate_key); + CDBBatch batch(*this); batch.Erase(key); return WriteBatch(batch, fSync); } - bool WriteBatch(CDBBatch& batch, bool fSync = false) throw(dbwrapper_error); + bool WriteBatch(CDBBatch& batch, bool fSync = false); // not available for LevelDB; provide for compatibility with BDB bool Flush() @@ -248,32 +265,21 @@ public: return true; } - bool Sync() throw(dbwrapper_error) + bool Sync() { - CDBBatch batch(&obfuscate_key); + CDBBatch batch(*this); return WriteBatch(batch, true); } CDBIterator *NewIterator() { - return new CDBIterator(pdb->NewIterator(iteroptions), &obfuscate_key); + return new CDBIterator(*this, pdb->NewIterator(iteroptions)); } /** * Return true if the database managed by this class contains no entries. */ bool IsEmpty(); - - /** - * Accessor for obfuscate_key. - */ - const std::vector<unsigned char>& GetObfuscateKey() const; - - /** - * Return the obfuscate_key as a hex-formatted string. - */ - std::string GetObfuscateKeyHex() const; - }; #endif // BITCOIN_DBWRAPPER_H diff --git a/src/httpserver.cpp b/src/httpserver.cpp index ce1accb046..812940eaf9 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -36,7 +36,6 @@ #include <boost/algorithm/string/case_conv.hpp> // for to_lower() #include <boost/foreach.hpp> -#include <boost/scoped_ptr.hpp> /** Maximum size of http request (request line + headers) */ static const size_t MAX_HEADERS_SIZE = 8192; @@ -45,8 +44,8 @@ static const size_t MAX_HEADERS_SIZE = 8192; class HTTPWorkItem : public HTTPClosure { public: - HTTPWorkItem(HTTPRequest* req, const std::string &path, const HTTPRequestHandler& func): - req(req), path(path), func(func) + HTTPWorkItem(std::unique_ptr<HTTPRequest> req, const std::string &path, const HTTPRequestHandler& func): + req(std::move(req)), path(path), func(func) { } void operator()() @@ -54,7 +53,7 @@ public: func(req.get(), path); } - boost::scoped_ptr<HTTPRequest> req; + std::unique_ptr<HTTPRequest> req; private: std::string path; @@ -71,8 +70,7 @@ private: /** Mutex protects entire object */ CWaitableCriticalSection cs; CConditionVariable cond; - /* XXX in C++11 we can use std::unique_ptr here and avoid manual cleanup */ - std::deque<WorkItem*> queue; + std::deque<std::unique_ptr<WorkItem>> queue; bool running; size_t maxDepth; int numThreads; @@ -101,15 +99,11 @@ public: numThreads(0) { } - /*( Precondition: worker threads have all stopped + /** Precondition: worker threads have all stopped * (call WaitExit) */ ~WorkQueue() { - while (!queue.empty()) { - delete queue.front(); - queue.pop_front(); - } } /** Enqueue a work item */ bool Enqueue(WorkItem* item) @@ -118,7 +112,7 @@ public: if (queue.size() >= maxDepth) { return false; } - queue.push_back(item); + queue.emplace_back(std::unique_ptr<WorkItem>(item)); cond.notify_one(); return true; } @@ -127,18 +121,17 @@ public: { ThreadCounter count(*this); while (running) { - WorkItem* i = 0; + std::unique_ptr<WorkItem> i; { boost::unique_lock<boost::mutex> lock(cs); while (running && queue.empty()) cond.wait(lock); if (!running) break; - i = queue.front(); + i = std::move(queue.front()); queue.pop_front(); } (*i)(); - delete i; } } /** Interrupt and exit loops */ @@ -252,7 +245,7 @@ static std::string RequestMethodString(HTTPRequest::RequestMethod m) /** HTTP request callback */ static void http_request_cb(struct evhttp_request* req, void* arg) { - std::auto_ptr<HTTPRequest> hreq(new HTTPRequest(req)); + std::unique_ptr<HTTPRequest> hreq(new HTTPRequest(req)); LogPrint("http", "Received a %s request for %s from %s\n", RequestMethodString(hreq->GetRequestMethod()), hreq->GetURI(), hreq->GetPeer().ToString()); @@ -288,12 +281,14 @@ static void http_request_cb(struct evhttp_request* req, void* arg) // Dispatch to worker thread if (i != iend) { - std::auto_ptr<HTTPWorkItem> item(new HTTPWorkItem(hreq.release(), path, i->handler)); + std::unique_ptr<HTTPWorkItem> item(new HTTPWorkItem(std::move(hreq), path, i->handler)); assert(workQueue); if (workQueue->Enqueue(item.get())) item.release(); /* if true, queue took ownership */ - else + else { + LogPrintf("WARNING: request rejected because http work queue depth exceeded, it can be increased with the -rpcworkqueue= setting\n"); item->req->WriteReply(HTTP_INTERNAL, "Work queue depth exceeded"); + } } else { hreq->WriteReply(HTTP_NOTFOUND); } diff --git a/src/init.cpp b/src/init.cpp index 038b28cc3a..b06f448a00 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1138,10 +1138,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (mapArgs.count("-externalip")) { BOOST_FOREACH(const std::string& strAddr, mapMultiArgs["-externalip"]) { - CService addrLocal(strAddr, GetListenPort(), fNameLookup); - if (!addrLocal.IsValid()) + CService addrLocal; + if (Lookup(strAddr.c_str(), addrLocal, GetListenPort(), fNameLookup) && addrLocal.IsValid()) + AddLocal(addrLocal, LOCAL_MANUAL); + else return InitError(ResolveErrMsg("externalip", strAddr)); - AddLocal(CService(strAddr, GetListenPort(), fNameLookup), LOCAL_MANUAL); } } diff --git a/src/keystore.cpp b/src/keystore.cpp index cc8a573367..d568a74350 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -19,6 +19,7 @@ bool CBasicKeyStore::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) con { CKey key; if (!GetKey(address, key)) { + LOCK(cs_KeyStore); WatchKeyMap::const_iterator it = mapWatchKeys.find(address); if (it != mapWatchKeys.end()) { vchPubKeyOut = it->second; diff --git a/src/main.cpp b/src/main.cpp index a94d52f895..d2b7c6bc4f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1468,7 +1468,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::P if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it int nHeight = -1; { - CCoinsViewCache &view = *pcoinsTip; + const CCoinsViewCache& view = *pcoinsTip; const CCoins* coins = view.AccessCoins(hash); if (coins) nHeight = coins->nHeight; @@ -1583,7 +1583,7 @@ bool IsInitialBlockDownload() if (lockIBDState) return false; bool state = (chainActive.Height() < pindexBestHeader->nHeight - 24 * 6 || - pindexBestHeader->GetBlockTime() < GetTime() - nMaxTipAge); + std::max(chainActive.Tip()->GetBlockTime(), pindexBestHeader->GetBlockTime()) < GetTime() - nMaxTipAge); if (!state) lockIBDState = true; return state; @@ -2221,9 +2221,9 @@ static int64_t nTimeIndex = 0; static int64_t nTimeCallbacks = 0; static int64_t nTimeTotal = 0; -bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck) +bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, + CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck) { - const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); int64_t nTimeStart = GetTimeMicros(); @@ -2570,8 +2570,7 @@ void PruneAndFlush() { } /** Update chainActive and related internal data structures. */ -void static UpdateTip(CBlockIndex *pindexNew) { - const CChainParams& chainParams = Params(); +void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) { chainActive.SetTip(pindexNew); // New best block @@ -2629,13 +2628,13 @@ void static UpdateTip(CBlockIndex *pindexNew) { } /** Disconnect chainActive's tip. You probably want to call mempool.removeForReorg and manually re-limit mempool size after this, with cs_main held. */ -bool static DisconnectTip(CValidationState& state, const Consensus::Params& consensusParams) +bool static DisconnectTip(CValidationState& state, const CChainParams& chainparams) { CBlockIndex *pindexDelete = chainActive.Tip(); assert(pindexDelete); // Read block from disk. CBlock block; - if (!ReadBlockFromDisk(block, pindexDelete, consensusParams)) + if (!ReadBlockFromDisk(block, pindexDelete, chainparams.GetConsensus())) return AbortNode(state, "Failed to read block"); // Apply the block atomically to the chain state. int64_t nStart = GetTimeMicros(); @@ -2668,7 +2667,7 @@ bool static DisconnectTip(CValidationState& state, const Consensus::Params& cons // block that were added back and cleans up the mempool state. mempool.UpdateTransactionsFromBlock(vHashUpdate); // Update chainActive and related variables. - UpdateTip(pindexDelete->pprev); + UpdateTip(pindexDelete->pprev, chainparams); // Let wallets know transactions went from 1-confirmed to // 0-confirmed or conflicted: BOOST_FOREACH(const CTransaction &tx, block.vtx) { @@ -2704,7 +2703,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001); { CCoinsViewCache view(pcoinsTip); - bool rv = ConnectBlock(*pblock, state, pindexNew, view); + bool rv = ConnectBlock(*pblock, state, pindexNew, view, chainparams); GetMainSignals().BlockChecked(*pblock, state); if (!rv) { if (state.IsInvalid()) @@ -2727,7 +2726,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, list<CTransaction> txConflicted; mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted, !IsInitialBlockDownload()); // Update chainActive & related variables. - UpdateTip(pindexNew); + UpdateTip(pindexNew, chainparams); // Tell wallet about transactions that went from mempool // to conflicted: BOOST_FOREACH(const CTransaction &tx, txConflicted) { @@ -2828,7 +2827,7 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c // Disconnect active blocks which are no longer in the best chain. bool fBlocksDisconnected = false; while (chainActive.Tip() && chainActive.Tip() != pindexFork) { - if (!DisconnectTip(state, chainparams.GetConsensus())) + if (!DisconnectTip(state, chainparams)) return false; fBlocksDisconnected = true; } @@ -2973,7 +2972,7 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, return true; } -bool InvalidateBlock(CValidationState& state, const Consensus::Params& consensusParams, CBlockIndex *pindex) +bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) { AssertLockHeld(cs_main); @@ -2989,7 +2988,7 @@ bool InvalidateBlock(CValidationState& state, const Consensus::Params& consensus setBlockIndexCandidates.erase(pindexWalk); // ActivateBestChain considers blocks already in chainActive // unconditionally valid already, so force disconnect away from it. - if (!DisconnectTip(state, consensusParams)) { + if (!DisconnectTip(state, chainparams)) { mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); return false; } @@ -3372,7 +3371,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state if (ppindex) *ppindex = pindex; if (pindex->nStatus & BLOCK_FAILED_MASK) - return state.Invalid(error("%s: block is marked invalid", __func__), 0, "duplicate"); + return state.Invalid(error("%s: block %s is marked invalid", __func__, hash.ToString()), 0, "duplicate"); return true; } @@ -3524,7 +3523,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); if (!ContextualCheckBlock(block, state, pindexPrev)) return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state)); - if (!ConnectBlock(block, state, &indexDummy, viewNew, true)) + if (!ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true)) return false; assert(state.IsValid()); @@ -3892,7 +3891,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, CBlock block; if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus())) return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); - if (!ConnectBlock(block, state, pindex, coins)) + if (!ConnectBlock(block, state, pindex, coins, chainparams)) return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); } } @@ -4557,12 +4556,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vRecv >> LIMITED_STRING(pfrom->strSubVer, MAX_SUBVERSION_LENGTH); pfrom->cleanSubVer = SanitizeString(pfrom->strSubVer); } - if (!vRecv.empty()) + if (!vRecv.empty()) { vRecv >> pfrom->nStartingHeight; - if (!vRecv.empty()) - vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message - else - pfrom->fRelayTxes = true; + } + { + LOCK(pfrom->cs_filter); + if (!vRecv.empty()) + vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message + else + pfrom->fRelayTxes = true; + } // Disconnect if we connected to ourself if (nNonce == nLocalHostNonce && nNonce > 1) @@ -5235,34 +5238,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fDisconnect = true; return true; } - LOCK2(cs_main, pfrom->cs_filter); - std::vector<uint256> vtxid; - mempool.queryHashes(vtxid); - vector<CInv> vInv; - BOOST_FOREACH(uint256& hash, vtxid) { - CInv inv(MSG_TX, hash); - if (pfrom->pfilter) { - CTransaction tx; - bool fInMemPool = mempool.lookup(hash, tx); - if (!fInMemPool) continue; // another thread removed since queryHashes, maybe... - if (!pfrom->pfilter->IsRelevantAndUpdate(tx)) continue; - } - if (pfrom->minFeeFilter) { - CFeeRate feeRate; - mempool.lookupFeeRate(hash, feeRate); - LOCK(pfrom->cs_feeFilter); - if (feeRate.GetFeePerK() < pfrom->minFeeFilter) - continue; - } - vInv.push_back(inv); - if (vInv.size() == MAX_INV_SZ) { - pfrom->PushMessage(NetMsgType::INV, vInv); - vInv.clear(); - } - } - if (vInv.size() > 0) - pfrom->PushMessage(NetMsgType::INV, vInv); + LOCK(pfrom->cs_inventory); + pfrom->fSendMempool = true; } @@ -5350,12 +5328,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, CBloomFilter filter; vRecv >> filter; + LOCK(pfrom->cs_filter); + if (!filter.IsWithinSizeConstraints()) // There is no excuse for sending a too-large filter Misbehaving(pfrom->GetId(), 100); else { - LOCK(pfrom->cs_filter); delete pfrom->pfilter; pfrom->pfilter = new CBloomFilter(filter); pfrom->pfilter->UpdateEmptyFull(); @@ -5560,6 +5539,22 @@ bool ProcessMessages(CNode* pfrom) return fOk; } +class CompareInvMempoolOrder +{ + CTxMemPool *mp; +public: + CompareInvMempoolOrder(CTxMemPool *mempool) + { + mp = mempool; + } + + bool operator()(std::set<uint256>::iterator a, std::set<uint256>::iterator b) + { + /* As std::make_heap produces a max-heap, we want the entries with the + * fewest ancestors/highest fee to sort later. */ + return mp->CompareDepthAndScore(*b, *a); + } +}; bool SendMessages(CNode* pto) { @@ -5718,7 +5713,21 @@ bool SendMessages(CNode* pto) fRevertToInv = true; break; } - assert(pBestIndex == NULL || pindex->pprev == pBestIndex); + if (pBestIndex != NULL && pindex->pprev != pBestIndex) { + // This means that the list of blocks to announce don't + // connect to each other. + // This shouldn't really be possible to hit during + // regular operation (because reorgs should take us to + // a chain that has some block not on the prior chain, + // which should be caught by the prior check), but one + // way this could happen is by using invalidateblock / + // reconsiderblock repeatedly on the tip, causing it to + // be added multiple times to vBlockHashesToAnnounce. + // Robustly deal with this rare situation by reverting + // to an inv. + fRevertToInv = true; + break; + } pBestIndex = pindex; if (fFoundStartingHeader) { // add this to the headers message @@ -5785,49 +5794,127 @@ bool SendMessages(CNode* pto) // Message: inventory // vector<CInv> vInv; - vector<CInv> vInvWait; { + LOCK(pto->cs_inventory); + vInv.reserve(std::max<size_t>(pto->vInventoryBlockToSend.size(), INVENTORY_BROADCAST_MAX)); + + // Add blocks + BOOST_FOREACH(const uint256& hash, pto->vInventoryBlockToSend) { + vInv.push_back(CInv(MSG_BLOCK, hash)); + if (vInv.size() == MAX_INV_SZ) { + pto->PushMessage(NetMsgType::INV, vInv); + vInv.clear(); + } + } + pto->vInventoryBlockToSend.clear(); + + // Check whether periodic sends should happen bool fSendTrickle = pto->fWhitelisted; if (pto->nNextInvSend < nNow) { fSendTrickle = true; - pto->nNextInvSend = PoissonNextSend(nNow, AVG_INVENTORY_BROADCAST_INTERVAL); + // Use half the delay for outbound peers, as there is less privacy concern for them. + pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> !pto->fInbound); + } + + // Time to send but the peer has requested we not relay transactions. + if (fSendTrickle) { + LOCK(pto->cs_filter); + if (!pto->fRelayTxes) pto->setInventoryTxToSend.clear(); } - LOCK(pto->cs_inventory); - vInv.reserve(std::min<size_t>(1000, pto->vInventoryToSend.size())); - vInvWait.reserve(pto->vInventoryToSend.size()); - BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend) - { - if (inv.type == MSG_TX && pto->filterInventoryKnown.contains(inv.hash)) - continue; - // trickle out tx inv to protect privacy - if (inv.type == MSG_TX && !fSendTrickle) + // Respond to BIP35 mempool requests + if (fSendTrickle && pto->fSendMempool) { + std::vector<uint256> vtxid; + mempool.queryHashes(vtxid); + pto->fSendMempool = false; + CAmount filterrate = 0; { - // 1/4 of tx invs blast to all immediately - static uint256 hashSalt; - if (hashSalt.IsNull()) - hashSalt = GetRandHash(); - uint256 hashRand = ArithToUint256(UintToArith256(inv.hash) ^ UintToArith256(hashSalt)); - hashRand = Hash(BEGIN(hashRand), END(hashRand)); - bool fTrickleWait = ((UintToArith256(hashRand) & 3) != 0); + LOCK(pto->cs_feeFilter); + filterrate = pto->minFeeFilter; + } - if (fTrickleWait) - { - vInvWait.push_back(inv); - continue; + LOCK(pto->cs_filter); + + BOOST_FOREACH(const uint256& hash, vtxid) { + CInv inv(MSG_TX, hash); + pto->setInventoryTxToSend.erase(hash); + if (filterrate) { + CFeeRate feeRate; + mempool.lookupFeeRate(hash, feeRate); + if (feeRate.GetFeePerK() < filterrate) + continue; + } + if (pto->pfilter) { + CTransaction tx; + bool fInMemPool = mempool.lookup(hash, tx); + if (!fInMemPool) continue; // another thread removed since queryHashes, maybe... + if (!pto->pfilter->IsRelevantAndUpdate(tx)) continue; + } + pto->filterInventoryKnown.insert(hash); + vInv.push_back(inv); + if (vInv.size() == MAX_INV_SZ) { + pto->PushMessage(NetMsgType::INV, vInv); + vInv.clear(); } } + } - pto->filterInventoryKnown.insert(inv.hash); - - vInv.push_back(inv); - if (vInv.size() >= 1000) + // Determine transactions to relay + if (fSendTrickle) { + // Produce a vector with all candidates for sending + vector<std::set<uint256>::iterator> vInvTx; + vInvTx.reserve(pto->setInventoryTxToSend.size()); + for (std::set<uint256>::iterator it = pto->setInventoryTxToSend.begin(); it != pto->setInventoryTxToSend.end(); it++) { + vInvTx.push_back(it); + } + CAmount filterrate = 0; { - pto->PushMessage(NetMsgType::INV, vInv); - vInv.clear(); + LOCK(pto->cs_feeFilter); + filterrate = pto->minFeeFilter; + } + // Topologically and fee-rate sort the inventory we send for privacy and priority reasons. + // A heap is used so that not all items need sorting if only a few are being sent. + CompareInvMempoolOrder compareInvMempoolOrder(&mempool); + std::make_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder); + // No reason to drain out at many times the network's capacity, + // especially since we have many peers and some will draw much shorter delays. + unsigned int nRelayedTransactions = 0; + LOCK(pto->cs_filter); + while (!vInvTx.empty() && nRelayedTransactions < INVENTORY_BROADCAST_MAX) { + // Fetch the top element from the heap + std::pop_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder); + std::set<uint256>::iterator it = vInvTx.back(); + vInvTx.pop_back(); + uint256 hash = *it; + // Remove it from the to-be-sent set + pto->setInventoryTxToSend.erase(it); + // Check if not in the filter already + if (pto->filterInventoryKnown.contains(hash)) { + continue; + } + // Not in the mempool anymore? don't bother sending it. + CFeeRate feeRate; + if (!mempool.lookupFeeRate(hash, feeRate)) { + continue; + } + if (filterrate && feeRate.GetFeePerK() < filterrate) { + continue; + } + if (pto->pfilter) { + CTransaction tx; + if (!mempool.lookup(hash, tx)) continue; + if (!pto->pfilter->IsRelevantAndUpdate(tx)) continue; + } + // Send + vInv.push_back(CInv(MSG_TX, hash)); + nRelayedTransactions++; + if (vInv.size() == MAX_INV_SZ) { + pto->PushMessage(NetMsgType::INV, vInv); + vInv.clear(); + } + pto->filterInventoryKnown.insert(hash); } } - pto->vInventoryToSend = vInvWait; } if (!vInv.empty()) pto->PushMessage(NetMsgType::INV, vInv); diff --git a/src/main.h b/src/main.h index 0962f44e94..71d44979ad 100644 --- a/src/main.h +++ b/src/main.h @@ -99,9 +99,12 @@ static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111; static const unsigned int AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL = 24 * 24 * 60; /** Average delay between peer address broadcasts in seconds. */ static const unsigned int AVG_ADDRESS_BROADCAST_INTERVAL = 30; -/** Average delay between trickled inventory broadcasts in seconds. - * Blocks, whitelisted receivers, and a random 25% of transactions bypass this. */ -static const unsigned int AVG_INVENTORY_BROADCAST_INTERVAL = 5; +/** Average delay between trickled inventory transmissions in seconds. + * Blocks and whitelisted receivers bypass this, outbound peers get half this delay. */ +static const unsigned int INVENTORY_BROADCAST_INTERVAL = 5; +/** Maximum number of inventory items to send per transmission. + * Limits the impact of low-fee transaction floods. */ +static const unsigned int INVENTORY_BROADCAST_MAX = 7 * INVENTORY_BROADCAST_INTERVAL; /** Average delay between feefilter broadcasts in seconds. */ static const unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60; /** Maximum feefilter broadcast delay after significant change. */ @@ -458,7 +461,8 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn /** Apply the effects of this block (with given index) on the UTXO set represented by coins. * Validity checks that depend on the UTXO set are also done; ConnectBlock() * can fail if those validity checks fail (among other reasons). */ -bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck = false); +bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, + const CChainParams& chainparams, bool fJustCheck = false); /** Undo the effects of this block (with given index) on the UTXO set represented by coins. * In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean @@ -536,7 +540,7 @@ public: CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator); /** Mark a block as invalid. */ -bool InvalidateBlock(CValidationState& state, const Consensus::Params& consensusParams, CBlockIndex *pindex); +bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex); /** Remove invalidity status from a block and its descendants. */ bool ReconsiderBlock(CValidationState& state, CBlockIndex *pindex); diff --git a/src/miner.cpp b/src/miner.cpp index ef8fd4db43..eaf29a767b 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -74,7 +74,7 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& scriptPubKeyIn) { // Create new block - auto_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate()); + std::unique_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate()); if(!pblocktemplate.get()) return NULL; CBlock *pblock = &pblocktemplate->block; // pointer for convenience diff --git a/src/net.cpp b/src/net.cpp index f294e4c667..6642ef6515 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -877,6 +877,14 @@ public: } }; +/** Try to find a connection to evict when the node is full. + * Extreme care must be taken to avoid opening the node to attacker + * triggered network partitioning. + * The strategy used here is to protect a small number of peers + * for each of several distinct characteristics which are difficult + * to forge. In order to partition a node the attacker must be + * simultaneously better at all of them than honest peers. + */ static bool AttemptToEvictConnection(bool fPreferNewConnection) { std::vector<CNodeRef> vEvictionCandidates; { @@ -905,7 +913,7 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) { if (vEvictionCandidates.empty()) return false; - // Protect the 8 nodes with the best ping times. + // Protect the 8 nodes with the lowest minimum ping time. // An attacker cannot manipulate this metric without physically moving nodes closer to the target. std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeMinPingTime); vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(8, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end()); @@ -913,7 +921,7 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) { if (vEvictionCandidates.empty()) return false; // Protect the half of the remaining nodes which have been connected the longest. - // This replicates the existing implicit behavior. + // This replicates the non-eviction implicit behavior, and precludes attacks that start later. std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeTimeConnected); vEvictionCandidates.erase(vEvictionCandidates.end() - static_cast<int>(vEvictionCandidates.size() / 2), vEvictionCandidates.end()); @@ -941,6 +949,7 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) { vEvictionCandidates = mapAddrCounts[naMostConnections]; // Do not disconnect peers if there is only one unprotected connection from their network group. + // This step excessively favors netgroup diversity, and should be removed once more protective criteria are established. if (vEvictionCandidates.size() <= 1) // unless we prefer the new connection (for whitelisted peers) if (!fPreferNewConnection) @@ -1443,7 +1452,7 @@ void ThreadDNSAddressSeed() } else { vector<CNetAddr> vIPs; vector<CAddress> vAdd; - if (LookupHost(seed.host.c_str(), vIPs)) + if (LookupHost(seed.host.c_str(), vIPs, 0, true)) { BOOST_FOREACH(const CNetAddr& ip, vIPs) { @@ -1454,7 +1463,15 @@ void ThreadDNSAddressSeed() found++; } } - addrman.Add(vAdd, CNetAddr(seed.name, true)); + // TODO: The seed name resolve may fail, yielding an IP of [::], which results in + // addrman assigning the same source to results from different seeds. + // This should switch to a hard-coded stable dummy IP for each seed name, so that the + // resolve is not required at all. + if (!vIPs.empty()) { + CService seedSource; + Lookup(seed.name.c_str(), seedSource, 0, true); + addrman.Add(vAdd, seedSource); + } } } @@ -1884,7 +1901,7 @@ void static Discover(boost::thread_group& threadGroup) if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) { vector<CNetAddr> vaddr; - if (LookupHost(pszHostName, vaddr)) + if (LookupHost(pszHostName, vaddr, 0, true)) { BOOST_FOREACH (const CNetAddr &addr, vaddr) { @@ -2071,20 +2088,7 @@ void RelayTransaction(const CTransaction& tx, CFeeRate feerate) LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) { - if(!pnode->fRelayTxes) - continue; - { - LOCK(pnode->cs_feeFilter); - if (feerate.GetFeePerK() < pnode->minFeeFilter) - continue; - } - LOCK(pnode->cs_filter); - if (pnode->pfilter) - { - if (pnode->pfilter->IsRelevantAndUpdate(tx)) - pnode->PushInventory(inv); - } else - pnode->PushInventory(inv); + pnode->PushInventory(inv); } } @@ -2370,6 +2374,7 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa hashContinue = uint256(); nStartingHeight = -1; filterInventoryKnown.reset(); + fSendMempool = false; fGetAddr = false; nNextLocalAddrSend = 0; nNextAddrSend = 0; @@ -2626,9 +2631,10 @@ void DumpBanlist() CBanDB bandb; banmap_t banmap; + CNode::SetBannedSetDirty(false); CNode::GetBanned(banmap); - if (bandb.Write(banmap)) - CNode::SetBannedSetDirty(false); + if (!bandb.Write(banmap)) + CNode::SetBannedSetDirty(true); LogPrint("net", "Flushed %d banned node ips/subnets to banlist.dat %dms\n", banmap.size(), GetTimeMillis() - nStart); @@ -357,7 +357,7 @@ public: // a) it allows us to not relay tx invs before receiving the peer's version message // b) the peer may tell us in its version message that we should not relay tx invs // unless it loads a bloom filter. - bool fRelayTxes; + bool fRelayTxes; //protected by cs_filter bool fSentAddr; CSemaphoreGrant grantOutbound; CCriticalSection cs_filter; @@ -397,7 +397,13 @@ public: // inventory based relay CRollingBloomFilter filterInventoryKnown; - std::vector<CInv> vInventoryToSend; + // Set of transaction ids we still have to announce. + // They are sorted by the mempool before relay, so the order is not important. + std::set<uint256> setInventoryTxToSend; + // List of block ids we still have announce. + // There is no final sorting before sending, as they are always sent immediately + // and in the order requested. + std::vector<uint256> vInventoryBlockToSend; CCriticalSection cs_inventory; std::set<uint256> setAskFor; std::multimap<int64_t, CInv> mapAskFor; @@ -405,6 +411,8 @@ public: // Used for headers announcements - unfiltered blocks to relay // Also protected by cs_inventory std::vector<uint256> vBlockHashesToAnnounce; + // Used for BIP35 mempool sending, also protected by cs_inventory + bool fSendMempool; // Ping time measurement: // The pong reply we're expecting, or 0 if no pong expected. @@ -517,11 +525,13 @@ public: void PushInventory(const CInv& inv) { - { - LOCK(cs_inventory); - if (inv.type == MSG_TX && filterInventoryKnown.contains(inv.hash)) - return; - vInventoryToSend.push_back(inv); + LOCK(cs_inventory); + if (inv.type == MSG_TX) { + if (!filterInventoryKnown.contains(inv.hash)) { + setInventoryTxToSend.insert(inv.hash); + } + } else if (inv.type == MSG_BLOCK) { + vInventoryBlockToSend.push_back(inv.hash); } } diff --git a/src/netbase.cpp b/src/netbase.cpp index 281c6bcb7f..b44a8b16e2 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -614,10 +614,12 @@ bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest proxyType nameProxy; GetNameProxy(nameProxy); - CService addrResolved(CNetAddr(strDest, fNameLookup && !HaveNameProxy()), port); - if (addrResolved.IsValid()) { - addr = addrResolved; - return ConnectSocket(addr, hSocketRet, nTimeout); + CService addrResolved; + if (Lookup(strDest.c_str(), addrResolved, port, fNameLookup && !HaveNameProxy())) { + if (addrResolved.IsValid()) { + addr = addrResolved; + return ConnectSocket(addr, hSocketRet, nTimeout); + } } addr = CService("0.0.0.0:0"); @@ -686,19 +688,19 @@ CNetAddr::CNetAddr(const struct in6_addr& ipv6Addr, const uint32_t scope) scopeId = scope; } -CNetAddr::CNetAddr(const char *pszIp, bool fAllowLookup) +CNetAddr::CNetAddr(const char *pszIp) { Init(); std::vector<CNetAddr> vIP; - if (LookupHost(pszIp, vIP, 1, fAllowLookup)) + if (LookupHost(pszIp, vIP, 1, false)) *this = vIP[0]; } -CNetAddr::CNetAddr(const std::string &strIp, bool fAllowLookup) +CNetAddr::CNetAddr(const std::string &strIp) { Init(); std::vector<CNetAddr> vIP; - if (LookupHost(strIp.c_str(), vIP, 1, fAllowLookup)) + if (LookupHost(strIp.c_str(), vIP, 1, false)) *this = vIP[0]; } @@ -1121,35 +1123,35 @@ bool CService::SetSockAddr(const struct sockaddr *paddr) } } -CService::CService(const char *pszIpPort, bool fAllowLookup) +CService::CService(const char *pszIpPort) { Init(); CService ip; - if (Lookup(pszIpPort, ip, 0, fAllowLookup)) + if (Lookup(pszIpPort, ip, 0, false)) *this = ip; } -CService::CService(const char *pszIpPort, int portDefault, bool fAllowLookup) +CService::CService(const char *pszIpPort, int portDefault) { Init(); CService ip; - if (Lookup(pszIpPort, ip, portDefault, fAllowLookup)) + if (Lookup(pszIpPort, ip, portDefault, false)) *this = ip; } -CService::CService(const std::string &strIpPort, bool fAllowLookup) +CService::CService(const std::string &strIpPort) { Init(); CService ip; - if (Lookup(strIpPort.c_str(), ip, 0, fAllowLookup)) + if (Lookup(strIpPort.c_str(), ip, 0, false)) *this = ip; } -CService::CService(const std::string &strIpPort, int portDefault, bool fAllowLookup) +CService::CService(const std::string &strIpPort, int portDefault) { Init(); CService ip; - if (Lookup(strIpPort.c_str(), ip, portDefault, fAllowLookup)) + if (Lookup(strIpPort.c_str(), ip, portDefault, false)) *this = ip; } @@ -1243,7 +1245,7 @@ CSubNet::CSubNet(): memset(netmask, 0, sizeof(netmask)); } -CSubNet::CSubNet(const std::string &strSubnet, bool fAllowLookup) +CSubNet::CSubNet(const std::string &strSubnet) { size_t slash = strSubnet.find_last_of('/'); std::vector<CNetAddr> vIP; @@ -1253,7 +1255,7 @@ CSubNet::CSubNet(const std::string &strSubnet, bool fAllowLookup) memset(netmask, 255, sizeof(netmask)); std::string strAddress = strSubnet.substr(0, slash); - if (LookupHost(strAddress.c_str(), vIP, 1, fAllowLookup)) + if (LookupHost(strAddress.c_str(), vIP, 1, false)) { network = vIP[0]; if (slash != strSubnet.npos) diff --git a/src/netbase.h b/src/netbase.h index db736154fa..65187a17cf 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -49,8 +49,8 @@ class CNetAddr public: CNetAddr(); CNetAddr(const struct in_addr& ipv4Addr); - explicit CNetAddr(const char *pszIp, bool fAllowLookup = false); - explicit CNetAddr(const std::string &strIp, bool fAllowLookup = false); + explicit CNetAddr(const char *pszIp); + explicit CNetAddr(const std::string &strIp); void Init(); void SetIP(const CNetAddr& ip); @@ -119,7 +119,7 @@ class CSubNet public: CSubNet(); - explicit CSubNet(const std::string &strSubnet, bool fAllowLookup = false); + explicit CSubNet(const std::string &strSubnet); //constructor for single ip subnet (<ipv4>/32 or <ipv6>/128) explicit CSubNet(const CNetAddr &addr); @@ -154,10 +154,10 @@ class CService : public CNetAddr CService(const CNetAddr& ip, unsigned short port); CService(const struct in_addr& ipv4Addr, unsigned short port); CService(const struct sockaddr_in& addr); - explicit CService(const char *pszIpPort, int portDefault, bool fAllowLookup = false); - explicit CService(const char *pszIpPort, bool fAllowLookup = false); - explicit CService(const std::string& strIpPort, int portDefault, bool fAllowLookup = false); - explicit CService(const std::string& strIpPort, bool fAllowLookup = false); + explicit CService(const char *pszIpPort, int portDefault); + explicit CService(const char *pszIpPort); + explicit CService(const std::string& strIpPort, int portDefault); + explicit CService(const std::string& strIpPort); void Init(); void SetPort(unsigned short portIn); unsigned short GetPort() const; @@ -206,9 +206,9 @@ bool GetProxy(enum Network net, proxyType &proxyInfoOut); bool IsProxy(const CNetAddr &addr); bool SetNameProxy(const proxyType &addrProxy); bool HaveNameProxy(); -bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions = 0, bool fAllowLookup = true); -bool Lookup(const char *pszName, CService& addr, int portDefault = 0, bool fAllowLookup = true); -bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault = 0, bool fAllowLookup = true, unsigned int nMaxSolutions = 0); +bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup); +bool Lookup(const char *pszName, CService& addr, int portDefault, bool fAllowLookup); +bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions); bool LookupNumeric(const char *pszName, CService& addr, int portDefault = 0); bool ConnectSocket(const CService &addr, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed = 0); bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout, bool *outProxyConnectionFailed = 0); diff --git a/src/pow.cpp b/src/pow.cpp index 058404f357..1db3b69293 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -64,9 +64,7 @@ unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nF // Retarget const arith_uint256 bnPowLimit = UintToArith256(params.powLimit); arith_uint256 bnNew; - arith_uint256 bnOld; bnNew.SetCompact(pindexLast->nBits); - bnOld = bnNew; bnNew *= nActualTimespan; bnNew /= params.nPowTargetTimespan; diff --git a/src/qt/forms/transactiondescdialog.ui b/src/qt/forms/transactiondescdialog.ui index 5ae1e12856..3a698cfd1d 100644 --- a/src/qt/forms/transactiondescdialog.ui +++ b/src/qt/forms/transactiondescdialog.ui @@ -11,7 +11,7 @@ </rect> </property> <property name="windowTitle"> - <string>Transaction details</string> + <string notr="true">Transaction details</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> diff --git a/src/qt/transactiondescdialog.cpp b/src/qt/transactiondescdialog.cpp index f7b6995b23..65adcc4f02 100644 --- a/src/qt/transactiondescdialog.cpp +++ b/src/qt/transactiondescdialog.cpp @@ -14,6 +14,7 @@ TransactionDescDialog::TransactionDescDialog(const QModelIndex &idx, QWidget *pa ui(new Ui::TransactionDescDialog) { ui->setupUi(this); + setWindowTitle(tr("Details for %1").arg(idx.data(TransactionTableModel::TxIDRole).toString())); QString desc = idx.data(TransactionTableModel::LongDescriptionRole).toString(); ui->detailText->setHtml(desc); } diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index a352228c36..eb6111e682 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -478,8 +478,9 @@ void TransactionView::showDetails() QModelIndexList selection = transactionView->selectionModel()->selectedRows(); if(!selection.isEmpty()) { - TransactionDescDialog dlg(selection.at(0)); - dlg.exec(); + TransactionDescDialog *dlg = new TransactionDescDialog(selection.at(0)); + dlg->setAttribute(Qt::WA_DeleteOnClose); + dlg->show(); } } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index ce230d6aed..a0d0e70442 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -572,7 +572,7 @@ void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vect if (!wallet->mapWallet.count(outpoint.hash)) continue; int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); if (nDepth < 0) continue; - COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true); + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true, true); vOutputs.push_back(out); } } @@ -599,7 +599,7 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) if (!wallet->mapWallet.count(outpoint.hash)) continue; int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); if (nDepth < 0) continue; - COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true); + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true, true); if (outpoint.n < out.tx->vout.size() && wallet->IsMine(out.tx->vout[outpoint.n]) == ISMINE_SPENDABLE) vCoins.push_back(out); } @@ -611,7 +611,7 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) while (wallet->IsChange(cout.tx->vout[cout.i]) && cout.tx->vin.size() > 0 && wallet->IsMine(cout.tx->vin[0])) { if (!wallet->mapWallet.count(cout.tx->vin[0].prevout.hash)) break; - cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0, true); + cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0, true, true); } CTxDestination address; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 5de2394e24..6960415e2e 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -942,7 +942,7 @@ UniValue invalidateblock(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); CBlockIndex* pblockindex = mapBlockIndex[hash]; - InvalidateBlock(state, Params().GetConsensus(), pblockindex); + InvalidateBlock(state, Params(), pblockindex); } if (state.IsValid()) { diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index b63ee22889..9a7d9d53a0 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -111,7 +111,7 @@ UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nG UniValue blockHashes(UniValue::VARR); while (nHeight < nHeightEnd) { - auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlock(Params(), coinbaseScript->reserveScript)); + std::unique_ptr<CBlockTemplate> pblocktemplate(CreateNewBlock(Params(), coinbaseScript->reserveScript)); if (!pblocktemplate.get()) throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); CBlock *pblock = &pblocktemplate->block; diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index e8a099b445..09f5185781 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -366,6 +366,48 @@ UniValue verifymessage(const UniValue& params, bool fHelp) return (pubkey.GetID() == keyID); } +UniValue signmessagewithprivkey(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 2) + throw runtime_error( + "signmessagewithprivkey \"privkey\" \"message\"\n" + "\nSign a message with the private key of an address\n" + "\nArguments:\n" + "1. \"privkey\" (string, required) The private key to sign the message with.\n" + "2. \"message\" (string, required) The message to create a signature of.\n" + "\nResult:\n" + "\"signature\" (string) The signature of the message encoded in base 64\n" + "\nExamples:\n" + "\nCreate the signature\n" + + HelpExampleCli("signmessagewithprivkey", "\"privkey\" \"my message\"") + + "\nVerify the signature\n" + + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"signature\" \"my message\"") + + "\nAs json rpc\n" + + HelpExampleRpc("signmessagewithprivkey", "\"privkey\", \"my message\"") + ); + + string strPrivkey = params[0].get_str(); + string strMessage = params[1].get_str(); + + CBitcoinSecret vchSecret; + bool fGood = vchSecret.SetString(strPrivkey); + if (!fGood) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); + CKey key = vchSecret.GetKey(); + if (!key.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range"); + + CHashWriter ss(SER_GETHASH, 0); + ss << strMessageMagic; + ss << strMessage; + + vector<unsigned char> vchSig; + if (!key.SignCompact(ss.GetHash(), vchSig)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); + + return EncodeBase64(&vchSig[0], vchSig.size()); +} + UniValue setmocktime(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) @@ -404,6 +446,7 @@ static const CRPCCommand commands[] = { "util", "validateaddress", &validateaddress, true }, /* uses wallet if enabled */ { "util", "createmultisig", &createmultisig, true }, { "util", "verifymessage", &verifymessage, true }, + { "util", "signmessagewithprivkey", &signmessagewithprivkey, true }, /* Not shown in help */ { "hidden", "setmocktime", &setmocktime, true }, diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index ce14d034ce..320091b9c4 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -347,6 +347,7 @@ UniValue getaddednodeinfo(const UniValue& params, bool fHelp) obj.push_back(Pair("connected", false)); UniValue addresses(UniValue::VARR); obj.push_back(Pair("addresses", addresses)); + ret.push_back(obj); } } diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 9c47f7c6c9..fd4a5674cf 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -247,7 +247,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un vector<bool> vfExec; vector<valtype> altstack; set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); - if (script.size() > 10000) + if (script.size() > MAX_SCRIPT_SIZE) return set_error(serror, SCRIPT_ERR_SCRIPT_SIZE); int nOpCount = 0; bool fRequireMinimal = (flags & SCRIPT_VERIFY_MINIMALDATA) != 0; diff --git a/src/script/script.h b/src/script/script.h index bdbd340bcc..a2941ce901 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -27,6 +27,9 @@ static const int MAX_OPS_PER_SCRIPT = 201; // Maximum number of public keys per multisig static const int MAX_PUBKEYS_PER_MULTISIG = 20; +// Maximum script length in bytes +static const int MAX_SCRIPT_SIZE = 10000; + // Threshold for nLockTime: below this value it is interpreted as block number, // otherwise as UNIX timestamp. static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC @@ -630,7 +633,7 @@ public: */ bool IsUnspendable() const { - return (size() > 0 && *begin() == OP_RETURN); + return (size() > 0 && *begin() == OP_RETURN) || (size() > MAX_SCRIPT_SIZE); } void clear() diff --git a/src/streams.h b/src/streams.h index 0fc6135a6a..ed14f3f412 100644 --- a/src/streams.h +++ b/src/streams.h @@ -240,7 +240,9 @@ public: CDataStream& ignore(int nSize) { // Ignore from the beginning of the buffer - assert(nSize >= 0); + if (nSize < 0) { + throw std::ios_base::failure("CDataStream::ignore(): nSize negative"); + } unsigned int nReadPosNext = nReadPos + nSize; if (nReadPosNext >= vch.size()) { @@ -404,6 +406,20 @@ public: return (*this); } + CAutoFile& ignore(size_t nSize) + { + if (!file) + throw std::ios_base::failure("CAutoFile::ignore: file handle is NULL"); + unsigned char data[4096]; + while (nSize > 0) { + size_t nNow = std::min<size_t>(nSize, sizeof(data)); + if (fread(data, 1, nNow, file) != nNow) + throw std::ios_base::failure(feof(file) ? "CAutoFile::ignore: end of file" : "CAutoFile::read: fread failed"); + nSize -= nNow; + } + return (*this); + } + CAutoFile& write(const char* pch, size_t nSize) { if (!file) diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index 48e3c8ed8e..129ce04e0b 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -4,7 +4,9 @@ #include "coins.h" #include "random.h" +#include "script/standard.h" #include "uint256.h" +#include "utilstrencodings.h" #include "test/test_bitcoin.h" #include "main.h" #include "consensus/validation.h" @@ -345,4 +347,73 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test) BOOST_CHECK(spent_a_duplicate_coinbase); } +BOOST_AUTO_TEST_CASE(ccoins_serialization) +{ + // Good example + CDataStream ss1(ParseHex("0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e"), SER_DISK, CLIENT_VERSION); + CCoins cc1; + ss1 >> cc1; + BOOST_CHECK_EQUAL(cc1.nVersion, 1); + BOOST_CHECK_EQUAL(cc1.fCoinBase, false); + BOOST_CHECK_EQUAL(cc1.nHeight, 203998); + BOOST_CHECK_EQUAL(cc1.vout.size(), 2); + BOOST_CHECK_EQUAL(cc1.IsAvailable(0), false); + BOOST_CHECK_EQUAL(cc1.IsAvailable(1), true); + BOOST_CHECK_EQUAL(cc1.vout[1].nValue, 60000000000ULL); + BOOST_CHECK_EQUAL(HexStr(cc1.vout[1].scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("816115944e077fe7c803cfa57f29b36bf87c1d35")))))); + + // Good example + CDataStream ss2(ParseHex("0109044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b"), SER_DISK, CLIENT_VERSION); + CCoins cc2; + ss2 >> cc2; + BOOST_CHECK_EQUAL(cc2.nVersion, 1); + BOOST_CHECK_EQUAL(cc2.fCoinBase, true); + BOOST_CHECK_EQUAL(cc2.nHeight, 120891); + BOOST_CHECK_EQUAL(cc2.vout.size(), 17); + for (int i = 0; i < 17; i++) { + BOOST_CHECK_EQUAL(cc2.IsAvailable(i), i == 4 || i == 16); + } + BOOST_CHECK_EQUAL(cc2.vout[4].nValue, 234925952); + BOOST_CHECK_EQUAL(HexStr(cc2.vout[4].scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("61b01caab50f1b8e9c50a5057eb43c2d9563a4ee")))))); + BOOST_CHECK_EQUAL(cc2.vout[16].nValue, 110397); + BOOST_CHECK_EQUAL(HexStr(cc2.vout[16].scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("8c988f1a4a4de2161e0f50aac7f17e7f9555caa4")))))); + + // Smallest possible example + CDataStream ssx(SER_DISK, CLIENT_VERSION); + BOOST_CHECK_EQUAL(HexStr(ssx.begin(), ssx.end()), ""); + + CDataStream ss3(ParseHex("0002000600"), SER_DISK, CLIENT_VERSION); + CCoins cc3; + ss3 >> cc3; + BOOST_CHECK_EQUAL(cc3.nVersion, 0); + BOOST_CHECK_EQUAL(cc3.fCoinBase, false); + BOOST_CHECK_EQUAL(cc3.nHeight, 0); + BOOST_CHECK_EQUAL(cc3.vout.size(), 1); + BOOST_CHECK_EQUAL(cc3.IsAvailable(0), true); + BOOST_CHECK_EQUAL(cc3.vout[0].nValue, 0); + BOOST_CHECK_EQUAL(cc3.vout[0].scriptPubKey.size(), 0); + + // scriptPubKey that ends beyond the end of the stream + CDataStream ss4(ParseHex("0002000800"), SER_DISK, CLIENT_VERSION); + try { + CCoins cc4; + ss4 >> cc4; + BOOST_CHECK_MESSAGE(false, "We should have thrown"); + } catch (const std::ios_base::failure& e) { + } + + // Very large scriptPubKey (3*10^9 bytes) past the end of the stream + CDataStream tmp(SER_DISK, CLIENT_VERSION); + uint64_t x = 3000000000ULL; + tmp << VARINT(x); + BOOST_CHECK_EQUAL(HexStr(tmp.begin(), tmp.end()), "8a95c0bb00"); + CDataStream ss5(ParseHex("0002008a95c0bb0000"), SER_DISK, CLIENT_VERSION); + try { + CCoins cc5; + ss5 >> cc5; + BOOST_CHECK_MESSAGE(false, "We should have thrown"); + } catch (const std::ios_base::failure& e) { + } +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/data/script_tests.json b/src/test/data/script_tests.json index e69cc9e413..e75b7825ed 100644 --- a/src/test/data/script_tests.json +++ b/src/test/data/script_tests.json @@ -1098,7 +1098,7 @@ "NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG", "P2SH,STRICTENC", "OP_COUNT", -"Fails due to 201 sig op limit"], +"Fails due to 201 script operation limit"], ["1", "NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY", diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index e399315870..081d57831d 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -39,7 +39,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper) uint256 res; // Ensure that we're doing real obfuscation when obfuscate=true - BOOST_CHECK(obfuscate != is_null_key(dbw.GetObfuscateKey())); + BOOST_CHECK(obfuscate != is_null_key(dbwrapper_private::GetObfuscateKey(dbw))); BOOST_CHECK(dbw.Write(key, in)); BOOST_CHECK(dbw.Read(key, res)); @@ -64,7 +64,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_batch) uint256 in3 = GetRandHash(); uint256 res; - CDBBatch batch(&dbw.GetObfuscateKey()); + CDBBatch batch(dbw); batch.Write(key, in); batch.Write(key2, in2); @@ -156,7 +156,7 @@ BOOST_AUTO_TEST_CASE(existing_data_no_obfuscate) BOOST_CHECK_EQUAL(res2.ToString(), in.ToString()); BOOST_CHECK(!odbw.IsEmpty()); // There should be existing data - BOOST_CHECK(is_null_key(odbw.GetObfuscateKey())); // The key should be an empty string + BOOST_CHECK(is_null_key(dbwrapper_private::GetObfuscateKey(odbw))); // The key should be an empty string uint256 in2 = GetRandHash(); uint256 res3; @@ -193,7 +193,7 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex) // Check that the key/val we wrote with unobfuscated wrapper doesn't exist uint256 res2; BOOST_CHECK(!odbw.Read(key, res2)); - BOOST_CHECK(!is_null_key(odbw.GetObfuscateKey())); + BOOST_CHECK(!is_null_key(dbwrapper_private::GetObfuscateKey(odbw))); uint256 in2 = GetRandHash(); uint256 res3; diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index ab6485081c..469862518c 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -124,6 +124,11 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); delete pblocktemplate; + const CAmount BLOCKSUBSIDY = 50*COIN; + const CAmount LOWFEE = CENT; + const CAmount HIGHFEE = COIN; + const CAmount HIGHERFEE = 4*COIN; + // block sigops > limit: 1000 CHECKMULTISIG + 1 tx.vin.resize(1); // NOTE: OP_NOP is used to force 20 SigOps for the CHECKMULTISIG @@ -131,28 +136,28 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].prevout.hash = txFirst[0]->GetHash(); tx.vin[0].prevout.n = 0; tx.vout.resize(1); - tx.vout[0].nValue = 5000000000LL; + tx.vout[0].nValue = BLOCKSUBSIDY; for (unsigned int i = 0; i < 1001; ++i) { - tx.vout[0].nValue -= 1000000; + tx.vout[0].nValue -= LOWFEE; hash = tx.GetHash(); bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase // If we don't set the # of sig ops in the CTxMemPoolEntry, template creation fails - mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error); mempool.clear(); tx.vin[0].prevout.hash = txFirst[0]->GetHash(); - tx.vout[0].nValue = 5000000000LL; + tx.vout[0].nValue = BLOCKSUBSIDY; for (unsigned int i = 0; i < 1001; ++i) { - tx.vout[0].nValue -= 1000000; + tx.vout[0].nValue -= LOWFEE; hash = tx.GetHash(); bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase // If we do set the # of sig ops in the CTxMemPoolEntry, template creation passes - mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOps(20).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOps(20).FromTx(tx)); tx.vin[0].prevout.hash = hash; } BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); @@ -167,13 +172,13 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].scriptSig << vchData << OP_DROP; tx.vin[0].scriptSig << OP_1; tx.vin[0].prevout.hash = txFirst[0]->GetHash(); - tx.vout[0].nValue = 5000000000LL; + tx.vout[0].nValue = BLOCKSUBSIDY; for (unsigned int i = 0; i < 128; ++i) { - tx.vout[0].nValue -= 10000000; + tx.vout[0].nValue -= LOWFEE; hash = tx.GetHash(); bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase - mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); @@ -182,24 +187,24 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // orphan in mempool, template creation fails hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx)); BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error); mempool.clear(); // child with higher priority than parent tx.vin[0].scriptSig = CScript() << OP_1; tx.vin[0].prevout.hash = txFirst[1]->GetHash(); - tx.vout[0].nValue = 4900000000LL; + tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(100000000LL).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vin[0].prevout.hash = hash; tx.vin.resize(2); tx.vin[1].scriptSig = CScript() << OP_1; tx.vin[1].prevout.hash = txFirst[0]->GetHash(); tx.vin[1].prevout.n = 0; - tx.vout[0].nValue = 5900000000LL; + tx.vout[0].nValue = tx.vout[0].nValue+BLOCKSUBSIDY-HIGHERFEE; //First txn output + fresh coinbase - new txn fee hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(400000000LL).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -211,7 +216,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = 0; hash = tx.GetHash(); // give it a fee so it'll get mined - mempool.addUnchecked(hash, entry.Fee(100000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error); mempool.clear(); @@ -219,29 +224,29 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].prevout.hash = txFirst[0]->GetHash(); tx.vin[0].prevout.n = 0; tx.vin[0].scriptSig = CScript() << OP_1; - tx.vout[0].nValue = 4900000000LL; + tx.vout[0].nValue = BLOCKSUBSIDY-LOWFEE; script = CScript() << OP_0; tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script)); hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(10000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vin[0].prevout.hash = hash; tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(script.begin(), script.end()); - tx.vout[0].nValue -= 1000000; + tx.vout[0].nValue -= LOWFEE; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error); mempool.clear(); // double spend txn pair in mempool, template creation fails tx.vin[0].prevout.hash = txFirst[0]->GetHash(); tx.vin[0].scriptSig = CScript() << OP_1; - tx.vout[0].nValue = 4900000000LL; + tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE; tx.vout[0].scriptPubKey = CScript() << OP_1; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vout[0].scriptPubKey = CScript() << OP_2; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error); mempool.clear(); @@ -298,11 +303,11 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].nSequence = chainActive.Tip()->nHeight + 1; // txFirst[0] is the 2nd block prevheights[0] = baseheight + 1; tx.vout.resize(1); - tx.vout[0].nValue = 4900000000LL; + tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE; tx.vout[0].scriptPubKey = CScript() << OP_1; tx.nLockTime = 0; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(hash, entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK(CheckFinalTx(tx, flags)); // Locktime passes BOOST_CHECK(!TestSequenceLocks(tx, flags)); // Sequence locks fail BOOST_CHECK(SequenceLocks(tx, flags, &prevheights, CreateBlockIndex(chainActive.Tip()->nHeight + 2))); // Sequence locks pass on 2nd block diff --git a/src/tinyformat.h b/src/tinyformat.h index 73d49a1fe4..c6ec0419b3 100644 --- a/src/tinyformat.h +++ b/src/tinyformat.h @@ -113,7 +113,7 @@ namespace tfm = tinyformat; // Define for C++11 variadic templates which make the code shorter & more // general. If you don't define this, C++11 support is autodetected below. -// #define TINYFORMAT_USE_VARIADIC_TEMPLATES +#define TINYFORMAT_USE_VARIADIC_TEMPLATES //------------------------------------------------------------------------------ diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 1c7bc2dbee..47d834c7b4 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -438,7 +438,7 @@ void TorController::add_onion_cb(TorControlConnection& conn, const TorControlRep private_key = i->second; } - service = CService(service_id+".onion", GetListenPort(), false); + service = CService(service_id+".onion", GetListenPort()); LogPrintf("tor: Got service ID %s, advertising service %s\n", service_id, service.ToString()); if (WriteBinaryFile(GetPrivateKeyFile(), private_key)) { LogPrint("tor", "tor: Cached service private key to %s\n", GetPrivateKeyFile()); diff --git a/src/txdb.cpp b/src/txdb.cpp index 19ca178654..5fbaeb608a 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -49,7 +49,7 @@ uint256 CCoinsViewDB::GetBestBlock() const { } bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { - CDBBatch batch(&db.GetObfuscateKey()); + CDBBatch batch(db); size_t count = 0; size_t changed = 0; for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { @@ -139,7 +139,7 @@ void CCoinsViewDBCursor::Next() } bool CBlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo) { - CDBBatch batch(&GetObfuscateKey()); + CDBBatch batch(*this); for (std::vector<std::pair<int, const CBlockFileInfo*> >::const_iterator it=fileInfo.begin(); it != fileInfo.end(); it++) { batch.Write(make_pair(DB_BLOCK_FILES, it->first), *it->second); } @@ -155,7 +155,7 @@ bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) { } bool CBlockTreeDB::WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> >&vect) { - CDBBatch batch(&GetObfuscateKey()); + CDBBatch batch(*this); for (std::vector<std::pair<uint256,CDiskTxPos> >::const_iterator it=vect.begin(); it!=vect.end(); it++) batch.Write(make_pair(DB_TXINDEX, it->first), it->second); return WriteBatch(batch); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 52c7793118..a6070f5264 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -752,6 +752,31 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const assert(innerUsage == cachedInnerUsage); } +bool CTxMemPool::CompareDepthAndScore(const uint256& hasha, const uint256& hashb) +{ + LOCK(cs); + indexed_transaction_set::const_iterator i = mapTx.find(hasha); + if (i == mapTx.end()) return false; + indexed_transaction_set::const_iterator j = mapTx.find(hashb); + if (j == mapTx.end()) return true; + uint64_t counta = i->GetCountWithAncestors(); + uint64_t countb = j->GetCountWithAncestors(); + if (counta == countb) { + return CompareTxMemPoolEntryByScore()(*i, *j); + } + return counta < countb; +} + +namespace { +class DepthAndScoreComparator +{ + CTxMemPool *mp; +public: + DepthAndScoreComparator(CTxMemPool *mempool) : mp(mempool) {} + bool operator()(const uint256& a, const uint256& b) { return mp->CompareDepthAndScore(a, b); } +}; +} + void CTxMemPool::queryHashes(vector<uint256>& vtxid) { vtxid.clear(); @@ -760,6 +785,8 @@ void CTxMemPool::queryHashes(vector<uint256>& vtxid) vtxid.reserve(mapTx.size()); for (indexed_transaction_set::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) vtxid.push_back(mi->GetTx().GetHash()); + + std::sort(vtxid.begin(), vtxid.end(), DepthAndScoreComparator(this)); } bool CTxMemPool::lookup(uint256 hash, CTransaction& result) const @@ -885,7 +912,7 @@ bool CTxMemPool::HasNoInputsOf(const CTransaction &tx) const return true; } -CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView *baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } +CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) const { // If an entry in the mempool exists, always return that one, as it's guaranteed to never diff --git a/src/txmempool.h b/src/txmempool.h index de4ba0b371..bca8dd9791 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -511,6 +511,7 @@ public: std::list<CTransaction>& conflicts, bool fCurrentEstimate = true); void clear(); void _clear(); //lock free + bool CompareDepthAndScore(const uint256& hasha, const uint256& hashb); void queryHashes(std::vector<uint256>& vtxid); void pruneSpent(const uint256& hash, CCoins &coins); unsigned int GetTransactionsUpdated() const; @@ -672,10 +673,10 @@ private: class CCoinsViewMemPool : public CCoinsViewBacked { protected: - CTxMemPool &mempool; + const CTxMemPool& mempool; public: - CCoinsViewMemPool(CCoinsView *baseIn, CTxMemPool &mempoolIn); + CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn); bool GetCoins(const uint256 &txid, CCoins &coins) const; bool HaveCoins(const uint256 &txid) const; }; diff --git a/src/util.h b/src/util.h index ac099f1184..25c9b733e8 100644 --- a/src/util.h +++ b/src/util.h @@ -76,40 +76,33 @@ int LogPrintStr(const std::string &str); #define LogPrintf(...) LogPrint(NULL, __VA_ARGS__) -/** - * When we switch to C++11, this can be switched to variadic templates instead - * of this macro-based construction (see tinyformat.h). - */ -#define MAKE_ERROR_AND_LOG_FUNC(n) \ - /** Print to debug.log if -debug=category switch is given OR category is NULL. */ \ - template<TINYFORMAT_ARGTYPES(n)> \ - static inline int LogPrint(const char* category, const char* format, TINYFORMAT_VARARGS(n)) \ - { \ - if(!LogAcceptCategory(category)) return 0; \ - return LogPrintStr(tfm::format(format, TINYFORMAT_PASSARGS(n))); \ - } \ - /** Log error and return false */ \ - template<TINYFORMAT_ARGTYPES(n)> \ - static inline bool error(const char* format, TINYFORMAT_VARARGS(n)) \ - { \ - LogPrintStr("ERROR: " + tfm::format(format, TINYFORMAT_PASSARGS(n)) + "\n"); \ - return false; \ - } +template<typename T1, typename... Args> +static inline int LogPrint(const char* category, const char* fmt, const T1& v1, const Args&... args) +{ + if(!LogAcceptCategory(category)) return 0; \ + return LogPrintStr(tfm::format(fmt, v1, args...)); +} -TINYFORMAT_FOREACH_ARGNUM(MAKE_ERROR_AND_LOG_FUNC) +template<typename T1, typename... Args> +bool error(const char* fmt, const T1& v1, const Args&... args) +{ + LogPrintStr("ERROR: " + tfm::format(fmt, v1, args...) + "\n"); + return false; +} /** * Zero-arg versions of logging and error, these are not covered by - * TINYFORMAT_FOREACH_ARGNUM + * the variadic templates above (and don't take format arguments but + * bare strings). */ -static inline int LogPrint(const char* category, const char* format) +static inline int LogPrint(const char* category, const char* s) { if(!LogAcceptCategory(category)) return 0; - return LogPrintStr(format); + return LogPrintStr(s); } -static inline bool error(const char* format) +static inline bool error(const char* s) { - LogPrintStr(std::string("ERROR: ") + format + "\n"); + LogPrintStr(std::string("ERROR: ") + s + "\n"); return false; } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index d1e201ec11..623037e766 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2347,7 +2347,9 @@ UniValue listunspent(const UniValue& params, bool fHelp) " \"account\" : \"account\", (string) DEPRECATED. The associated account, or \"\" for the default account\n" " \"scriptPubKey\" : \"key\", (string) the script key\n" " \"amount\" : x.xxx, (numeric) the transaction amount in " + CURRENCY_UNIT + "\n" - " \"confirmations\" : n (numeric) The number of confirmations\n" + " \"confirmations\" : n, (numeric) The number of confirmations\n" + " \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n" + " \"solvable\" : xxx (bool) Whether we know how to spend this output, ignoring the lack of keys\n" " }\n" " ,...\n" "]\n" @@ -2424,6 +2426,7 @@ UniValue listunspent(const UniValue& params, bool fHelp) entry.push_back(Pair("amount",ValueFromAmount(nValue))); entry.push_back(Pair("confirmations",out.nDepth)); entry.push_back(Pair("spendable", out.fSpendable)); + entry.push_back(Pair("solvable", out.fSolvable)); results.push_back(entry); } @@ -2445,6 +2448,7 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp) "Note that all existing inputs must have their previous output transaction be in the wallet.\n" "Note that all inputs selected must be of standard form and P2SH scripts must be\n" "in the wallet using importaddress or addmultisigaddress (to calculate fees).\n" + "You can see whether this is the case by checking the \"solvable\" field in the listunspent output.\n" "Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n" "\nArguments:\n" "1. \"hexstring\" (string, required) The hex string of the raw transaction\n" diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index b759a6b2e8..387b223589 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -48,7 +48,7 @@ static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = fa wtx->fDebitCached = true; wtx->nDebitCached = 1; } - COutput output(wtx, nInput, nAge, true); + COutput output(wtx, nInput, nAge, true, true); vCoins.push_back(output); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 955a0a3d60..29d7138547 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1691,7 +1691,8 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i)))) vCoins.push_back(COutput(pcoin, i, nDepth, ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || - (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO))); + (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO), + (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO)); } } } @@ -2616,12 +2617,19 @@ bool CWallet::GetKeyFromPool(CPubKey& result) int64_t CWallet::GetOldestKeyPoolTime() { - int64_t nIndex = 0; - CKeyPool keypool; - ReserveKeyFromKeyPool(nIndex, keypool); - if (nIndex == -1) + LOCK(cs_wallet); + + // if the keypool is empty, return <NOW> + if (setKeyPool.empty()) return GetTime(); - ReturnKey(nIndex); + + // load oldest key from keypool, get time and return + CKeyPool keypool; + CWalletDB walletdb(strWalletFile); + int64_t nIndex = *(setKeyPool.begin()); + if (!walletdb.ReadPool(nIndex, keypool)) + throw runtime_error("GetOldestKeyPoolTime(): read oldest key in keypool failed"); + assert(keypool.vchPubKey.IsValid()); return keypool.nTime; } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index fa8740eb7f..c3bd343edd 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -414,10 +414,11 @@ public: int i; int nDepth; bool fSpendable; + bool fSolvable; - COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn) + COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn) { - tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; + tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; } std::string ToString() const; |