diff --git a/configure.ac b/configure.ac index 88f9c93e3..0c1b7300e 100644 --- a/configure.ac +++ b/configure.ac @@ -1,1283 +1,1285 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 0) define(_CLIENT_VERSION_MINOR, 15) define(_CLIENT_VERSION_REVISION, 0) define(_CLIENT_VERSION_BUILD, 0) define(_CLIENT_VERSION_IS_RELEASE, true) define(_COPYRIGHT_YEAR, 2017) define(_COPYRIGHT_HOLDERS,[The %s developers]) define(_COPYRIGHT_HOLDERS_SUBSTITUTION,[[Bitcoin]]) AC_INIT([Bitcoin ABC],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_REVISION],[https://github.com/Bitcoin-ABC/bitcoin-abc/issues],[bitcoin-abc],[https://bitcoinabc.org/]) AC_CONFIG_SRCDIR([src/validation.cpp]) AC_CONFIG_HEADERS([src/config/bitcoin-config.h]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([build-aux/m4]) BITCOIN_DAEMON_NAME=bitcoind BITCOIN_GUI_NAME=bitcoin-qt BITCOIN_CLI_NAME=bitcoin-cli BITCOIN_TX_NAME=bitcoin-tx AC_CANONICAL_HOST AH_TOP([#ifndef BITCOIN_BITCOIN_CONFIG_H]) AH_TOP([#define BITCOIN_BITCOIN_CONFIG_H]) AH_BOTTOM([#endif // BITCOIN_BITCOIN_CONFIG_H]) dnl faketime breaks configure and is only needed for make. Disable it here. unset FAKETIME dnl Automake init set-up and checks AM_INIT_AUTOMAKE([no-define subdir-objects foreign]) dnl faketime messes with timestamps and causes configure to be re-run. dnl --disable-maintainer-mode can be used to bypass this. AM_MAINTAINER_MODE([enable]) dnl make the compilation flags quiet unless V=1 is used m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) dnl Compiler checks (here before libtool). if test "x${CXXFLAGS+set}" = "xset"; then CXXFLAGS_overridden=yes else CXXFLAGS_overridden=no fi AC_PROG_CXX dnl By default, libtool for mingw refuses to link static libs into a dll for dnl fear of mixing pic/non-pic objects, and import/export complications. Since dnl we have those under control, re-enable that functionality. case $host in *mingw*) lt_cv_deplibs_check_method="pass_all" ;; esac dnl Require C++11 compiler (no GNU extensions) AX_CXX_COMPILE_STDCXX([11], [noext], [mandatory], [nodefault]) dnl Check if -latomic is required for CHECK_ATOMIC dnl Unless the user specified OBJCXX, force it to be the same as CXX. This ensures dnl that we get the same -std flags for both. m4_ifdef([AC_PROG_OBJCXX],[ if test "x${OBJCXX+set}" = "x"; then OBJCXX="${CXX}" fi AC_PROG_OBJCXX ]) dnl Libtool init checks. LT_INIT([pic-only]) dnl Check/return PATH for base programs. AC_PATH_TOOL(AR, ar) AC_PATH_TOOL(RANLIB, ranlib) AC_PATH_TOOL(STRIP, strip) AC_PATH_TOOL(GCOV, gcov) AC_PATH_PROG(LCOV, lcov) dnl Python 3.x is supported from 3.4 on (see https://github.com/bitcoin/bitcoin/issues/7893) AC_PATH_PROGS([PYTHON], [python3.6 python3.5 python3.4 python3 python2.7 python2 python]) AC_PATH_PROG(GENHTML, genhtml) AC_PATH_PROG([GIT], [git]) AC_PATH_PROG(CCACHE,ccache) AC_PATH_PROG(XGETTEXT,xgettext) AC_PATH_PROG(HEXDUMP,hexdump) AC_PATH_TOOL(READELF, readelf) AC_PATH_TOOL(CPPFILT, c++filt) AC_PATH_TOOL(OBJCOPY, objcopy) AC_ARG_VAR(PYTHONPATH, Augments the default search path for python module files) # Enable wallet AC_ARG_ENABLE([wallet], [AS_HELP_STRING([--disable-wallet], [disable wallet (enabled by default)])], [enable_wallet=$enableval], [enable_wallet=yes]) AC_ARG_WITH([miniupnpc], [AS_HELP_STRING([--with-miniupnpc], [enable UPNP (default is yes if libminiupnpc is found)])], [use_upnp=$withval], [use_upnp=auto]) AC_ARG_ENABLE([upnp-default], [AS_HELP_STRING([--enable-upnp-default], [if UPNP is enabled, turn it on at startup (default is no)])], [use_upnp_default=$enableval], [use_upnp_default=no]) AC_ARG_ENABLE(tests, AS_HELP_STRING([--disable-tests],[do not compile tests (default is to compile)]), [use_tests=$enableval], [use_tests=yes]) AC_ARG_ENABLE(gui-tests, AS_HELP_STRING([--disable-gui-tests],[do not compile GUI tests (default is to compile if GUI and tests enabled)]), [use_gui_tests=$enableval], [use_gui_tests=$use_tests]) AC_ARG_ENABLE(bench, AS_HELP_STRING([--disable-bench],[do not compile benchmarks (default is to compile)]), [use_bench=$enableval], [use_bench=yes]) AC_ARG_ENABLE([extended-rpc-tests], AS_HELP_STRING([--enable-extended-rpc-tests],[enable expensive RPC tests when using lcov (default no)]), [use_extended_rpc_tests=$enableval], [use_extended_rpc_tests=no]) AC_ARG_WITH([qrencode], [AS_HELP_STRING([--with-qrencode], [enable QR code support (default is yes if qt is enabled and libqrencode is found)])], [use_qr=$withval], [use_qr=auto]) AC_ARG_ENABLE([hardening], [AS_HELP_STRING([--disable-hardening], [do not attempt to harden the resulting executables (default is to harden)])], [use_hardening=$enableval], [use_hardening=yes]) AC_ARG_ENABLE([reduce-exports], [AS_HELP_STRING([--enable-reduce-exports], [attempt to reduce exported symbols in the resulting executables (default is no)])], [use_reduce_exports=$enableval], [use_reduce_exports=no]) AC_ARG_ENABLE([ccache], [AS_HELP_STRING([--disable-ccache], [do not use ccache for building (default is to use if found)])], [use_ccache=$enableval], [use_ccache=auto]) AC_ARG_ENABLE([lcov], [AS_HELP_STRING([--enable-lcov], [enable lcov testing (default is no)])], [use_lcov=yes], [use_lcov=no]) AC_ARG_ENABLE([glibc-back-compat], [AS_HELP_STRING([--enable-glibc-back-compat], [enable backwards compatibility with glibc])], [use_glibc_compat=$enableval], [use_glibc_compat=no]) AC_ARG_WITH([system-univalue], [AS_HELP_STRING([--with-system-univalue], [Build with system UniValue (default is no)])], [system_univalue=$withval], [system_univalue=no] ) AC_ARG_ENABLE([zmq], [AS_HELP_STRING([--disable-zmq], [disable ZMQ notifications])], [use_zmq=$enableval], [use_zmq=yes]) AC_ARG_WITH([protoc-bindir],[AS_HELP_STRING([--with-protoc-bindir=BIN_DIR],[specify protoc bin path])], [protoc_bin_path=$withval], []) AC_ARG_ENABLE(man, [AS_HELP_STRING([--disable-man], [do not install man pages (default is to install)])],, enable_man=yes) AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no) # Enable debug AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [use debug compiler flags and macros (default is no)])], [enable_debug=$enableval], [enable_debug=no]) # Enable ASAN AC_ARG_ENABLE([asan], [AS_HELP_STRING([--enable-asan], [enable address sanitizer compiler flags (implies --enable-debug, default is no)])], [enable_asan=$enableval], [enable_asan=no]) # Enable TSAN AC_ARG_ENABLE([tsan], [AS_HELP_STRING([--enable-tsan], [enable thread sanitizer compiler flags; requires 64-bit target architecture (implies --enable-debug, default is no)])], [enable_tsan=$enableval], [enable_tsan=no]) # Enable UBSAN AC_ARG_ENABLE([ubsan], [AS_HELP_STRING([--enable-ubsan], [enable undefined behavior sanitizer compiler flags (implies --enable-debug, default is no)])], [enable_ubsan=$enableval], [enable_ubsan=no]) # Turn warnings into errors AC_ARG_ENABLE([werror], [AS_HELP_STRING([--enable-werror], [Treat certain compiler warnings as errors (default is no)])], [enable_werror=$enableval], [enable_werror=no]) AC_LANG_PUSH([C++]) AX_CHECK_COMPILE_FLAG([-Werror],[CXXFLAG_WERROR="-Werror"],[CXXFLAG_WERROR=""]) if test "x$enable_asan" = xyes; then case $host in *mingw*) AC_MSG_ERROR("Windows builds cannot use ASAN") ;; esac enable_debug=yes CFLAGS="$CFLAGS -fsanitize=address -fno-omit-frame-pointer" CXXFLAGS="$CXXFLAGS -fsanitize=address -fno-omit-frame-pointer" LDFLAGS="$LDFLAGS -fsanitize=address" fi if test "x$enable_tsan" = xyes; then case $host in *mingw*) AC_MSG_ERROR("Windows builds cannot use TSAN") ;; esac enable_debug=yes CFLAGS="$CFLAGS -fsanitize=thread -fno-omit-frame-pointer" CXXFLAGS="$CXXFLAGS -fsanitize=thread -fno-omit-frame-pointer" LDFLAGS="$LDFLAGS -fsanitize=thread" fi if test "x$enable_ubsan" = xyes; then case $host in *mingw*) AC_MSG_ERROR("Windows builds cannot use UBSAN") ;; esac enable_debug=yes CFLAGS="$CFLAGS -fsanitize=undefined -fno-omit-frame-pointer" CXXFLAGS="$CXXFLAGS -fsanitize=undefined -fno-omit-frame-pointer" LDFLAGS="$LDFLAGS -fsanitize=undefined" fi if test "x$enable_debug" = xyes; then CPPFLAGS="$CPPFLAGS -DDEBUG -DDEBUG_LOCKORDER" if test "x$GCC" = xyes; then CFLAGS="$CFLAGS -g3 -O0" fi if test "x$GXX" = xyes; then CXXFLAGS="$CXXFLAGS -g3 -O0" fi fi ERROR_CXXFLAGS= if test "x$enable_werror" = "xyes"; then if test "x$CXXFLAG_WERROR" = "x"; then AC_MSG_ERROR("enable-werror set but -Werror is not usable") fi AX_CHECK_COMPILE_FLAG([-Werror=vla],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=vla"],,[[$CXXFLAG_WERROR]]) fi if test "x$CXXFLAGS_overridden" = "xno"; then 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([-Wvla],[CXXFLAGS="$CXXFLAGS -Wvla"],,[[$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" AC_ARG_WITH([utils], [AS_HELP_STRING([--with-utils], [build bitcoin-cli bitcoin-tx (default=yes)])], [build_bitcoin_utils=$withval], [build_bitcoin_utils=yes]) AC_ARG_WITH([libs], [AS_HELP_STRING([--with-libs], [build libraries (default=yes)])], [build_bitcoin_libs=$withval], [build_bitcoin_libs=yes]) AC_ARG_WITH([daemon], [AS_HELP_STRING([--with-daemon], [build bitcoind daemon (default=yes)])], [build_bitcoind=$withval], [build_bitcoind=yes]) use_pkgconfig=yes case $host in *mingw*) #pkgconfig does more harm than good with MinGW use_pkgconfig=no TARGET_OS=windows AC_CHECK_LIB([mingwthrd], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([kernel32], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([user32], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([gdi32], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([comdlg32], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([winspool], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([winmm], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([shell32], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([comctl32], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([ole32], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([oleaut32], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([uuid], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([rpcrt4], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([advapi32], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([ws2_32], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([mswsock], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([shlwapi], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([iphlpapi], [main],, AC_MSG_ERROR(lib missing)) AC_CHECK_LIB([crypt32], [main],, AC_MSG_ERROR(lib missing)) # -static is interpreted by libtool, where it has a different meaning. # In libtool-speak, it's -all-static. AX_CHECK_LINK_FLAG([[-static]],[LIBTOOL_APP_LDFLAGS="$LIBTOOL_APP_LDFLAGS -all-static"]) AC_PATH_PROG([MAKENSIS], [makensis], none) if test x$MAKENSIS = xnone; then AC_MSG_WARN("makensis not found. Cannot create installer.") fi AC_PATH_TOOL(WINDRES, windres, none) if test x$WINDRES = xnone; then AC_MSG_ERROR("windres not found") fi CPPFLAGS="$CPPFLAGS -D_MT -DWIN32 -D_WINDOWS -DBOOST_THREAD_USE_LIB" LEVELDB_TARGET_FLAGS="-DOS_WINDOWS" if test "x$CXXFLAGS_overridden" = "xno"; then CXXFLAGS="$CXXFLAGS -w" fi case $host in i?86-*) WINDOWS_BITS=32 ;; x86_64-*) WINDOWS_BITS=64 ;; *) AC_MSG_ERROR("Could not determine win32/win64 for installer") ;; esac AC_SUBST(WINDOWS_BITS) dnl libtool insists upon adding -nostdlib and a list of objects/libs to link against. dnl That breaks our ability to build dll's with static libgcc/libstdc++/libssp. Override dnl its command here, with the predeps/postdeps removed, and -static inserted. Postdeps are dnl also overridden to prevent their insertion later. dnl This should only affect dll's. archive_cmds_CXX="\$CC -shared \$libobjs \$deplibs \$compiler_flags -static -o \$output_objdir/\$soname \${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker \$lib" postdeps_CXX= ;; *darwin*) TARGET_OS=darwin LEVELDB_TARGET_FLAGS="-DOS_MACOSX" if test x$cross_compiling != xyes; then BUILD_OS=darwin AC_CHECK_PROG([PORT],port, port) if test x$PORT = xport; then dnl add default macports paths CPPFLAGS="$CPPFLAGS -isystem /opt/local/include" LIBS="$LIBS -L/opt/local/lib" if test -d /opt/local/include/db48; then CPPFLAGS="$CPPFLAGS -I/opt/local/include/db48" LIBS="$LIBS -L/opt/local/lib/db48" fi fi AC_PATH_PROGS([RSVG_CONVERT], [rsvg-convert rsvg],rsvg-convert) AC_CHECK_PROG([BREW],brew, brew) if test x$BREW = xbrew; then dnl These Homebrew packages may be keg-only, meaning that they won't be found dnl in expected paths because they may conflict with system files. Ask dnl Homebrew where each one is located, then adjust paths accordingly. dnl It's safe to add these paths even if the functionality is disabled by dnl the user (--without-wallet or --without-gui for example). openssl_prefix=`$BREW --prefix openssl 2>/dev/null` bdb_prefix=`$BREW --prefix berkeley-db4 2>/dev/null` qt5_prefix=`$BREW --prefix qt5 2>/dev/null` if test x$openssl_prefix != x; then PKG_CONFIG_PATH="$openssl_prefix/lib/pkgconfig:$PKG_CONFIG_PATH" export PKG_CONFIG_PATH fi if test x$bdb_prefix != x; then CPPFLAGS="$CPPFLAGS -I$bdb_prefix/include" LIBS="$LIBS -L$bdb_prefix/lib" fi if test x$qt5_prefix != x; then PKG_CONFIG_PATH="$qt5_prefix/lib/pkgconfig:$PKG_CONFIG_PATH" export PKG_CONFIG_PATH fi fi else case $build_os in *darwin*) BUILD_OS=darwin ;; *) AC_PATH_TOOL([INSTALLNAMETOOL], [install_name_tool], install_name_tool) AC_PATH_TOOL([OTOOL], [otool], otool) AC_PATH_PROGS([GENISOIMAGE], [genisoimage mkisofs],genisoimage) AC_PATH_PROGS([RSVG_CONVERT], [rsvg-convert rsvg],rsvg-convert) AC_PATH_PROGS([IMAGEMAGICK_CONVERT], [convert],convert) AC_PATH_PROGS([TIFFCP], [tiffcp],tiffcp) dnl libtool will try to strip the static lib, which is a problem for dnl cross-builds because strip attempts to call a hard-coded ld, dnl which may not exist in the path. Stripping the .a is not dnl necessary, so just disable it. old_striplib= ;; esac fi AX_CHECK_LINK_FLAG([[-Wl,-headerpad_max_install_names]], [LDFLAGS="$LDFLAGS -Wl,-headerpad_max_install_names"]) CPPFLAGS="$CPPFLAGS -DMAC_OSX" OBJCXXFLAGS="$CXXFLAGS" ;; *linux*) TARGET_OS=linux LEVELDB_TARGET_FLAGS="-DOS_LINUX" ;; *freebsd*) LEVELDB_TARGET_FLAGS="-DOS_FREEBSD" ;; *openbsd*) LEVELDB_TARGET_FLAGS="-DOS_OPENBSD" ;; *) OTHER_OS=`echo ${host_os} | awk '{print toupper($0)}'` AC_MSG_WARN([Guessing LevelDB OS as OS_${OTHER_OS}, please check whether this is correct, if not add an entry to configure.ac.]) LEVELDB_TARGET_FLAGS="-DOS_${OTHER_OS}" ;; esac if test x$use_pkgconfig = xyes; then m4_ifndef([PKG_PROG_PKG_CONFIG], [AC_MSG_ERROR(PKG_PROG_PKG_CONFIG macro not found. Please install pkg-config and re-run autogen.sh.)]) m4_ifdef([PKG_PROG_PKG_CONFIG], [ PKG_PROG_PKG_CONFIG if test x"$PKG_CONFIG" = "x"; then AC_MSG_ERROR(pkg-config not found.) fi ]) fi if test x$use_extended_rpc_tests != xno; then AC_SUBST(EXTENDED_RPC_TESTS, -extended) fi if test x$use_lcov = xyes; then if test x$LCOV = x; then AC_MSG_ERROR("lcov testing requested but lcov not found") fi if test x$GCOV = x; then AC_MSG_ERROR("lcov testing requested but gcov not found") fi if test x$PYTHON = x; then AC_MSG_ERROR("lcov testing requested but python not found") fi if test x$GENHTML = x; then AC_MSG_ERROR("lcov testing requested but genhtml not found") fi LCOV="$LCOV --gcov-tool=$GCOV" AX_CHECK_LINK_FLAG([[--coverage]], [LDFLAGS="$LDFLAGS --coverage"], [AC_MSG_ERROR("lcov testing requested but --coverage linker flag does not work")]) AX_CHECK_COMPILE_FLAG([--coverage],[CXXFLAGS="$CXXFLAGS --coverage"], [AC_MSG_ERROR("lcov testing requested but --coverage flag does not work")]) fi dnl Check for endianness AC_C_BIGENDIAN dnl Check for pthread compile/link requirements AX_PTHREAD # The following macro will add the necessary defines to bitcoin-config.h, but # they also need to be passed down to any subprojects. Pull the results out of # the cache and add them to CPPFLAGS. AC_SYS_LARGEFILE # detect POSIX or GNU variant of strerror_r AC_FUNC_STRERROR_R if test x$ac_cv_sys_file_offset_bits != x && test x$ac_cv_sys_file_offset_bits != xno && test x$ac_cv_sys_file_offset_bits != xunknown; then CPPFLAGS="$CPPFLAGS -D_FILE_OFFSET_BITS=$ac_cv_sys_file_offset_bits" fi if test x$ac_cv_sys_large_files != x && test x$ac_cv_sys_large_files != xno && test x$ac_cv_sys_large_files != xunknown; then CPPFLAGS="$CPPFLAGS -D_LARGE_FILES=$ac_cv_sys_large_files" fi AX_CHECK_LINK_FLAG([[-Wl,--large-address-aware]], [LDFLAGS="$LDFLAGS -Wl,--large-address-aware"]) AX_GCC_FUNC_ATTRIBUTE([visibility]) AX_GCC_FUNC_ATTRIBUTE([dllexport]) AX_GCC_FUNC_ATTRIBUTE([dllimport]) if test x$use_glibc_compat != xno; then #glibc absorbed clock_gettime in 2.17. librt (its previous location) is safe to link #in anyway for back-compat. AC_CHECK_LIB([rt],[clock_gettime],, AC_MSG_ERROR(lib missing)) #__fdelt_chk's params and return type have changed from long unsigned int to long int. # See which one is present here. AC_MSG_CHECKING(__fdelt_chk type) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#ifdef _FORTIFY_SOURCE #undef _FORTIFY_SOURCE #endif #define _FORTIFY_SOURCE 2 #include extern "C" long unsigned int __fdelt_warn(long unsigned int);]],[[]])], [ fdelt_type="long unsigned int"], [ fdelt_type="long int"]) AC_MSG_RESULT($fdelt_type) AC_DEFINE_UNQUOTED(FDELT_TYPE, $fdelt_type,[parameter and return value type for __fdelt_chk]) else AC_SEARCH_LIBS([clock_gettime],[rt]) fi if test x$TARGET_OS != xwindows; then # All windows code is PIC, forcing it on just adds useless compile warnings AX_CHECK_COMPILE_FLAG([-fPIC],[PIC_FLAGS="-fPIC"]) fi if test x$use_hardening != xno; then AX_CHECK_COMPILE_FLAG([-Wstack-protector],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -Wstack-protector"]) AX_CHECK_COMPILE_FLAG([-fstack-protector-all],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-protector-all"]) AX_CHECK_PREPROC_FLAG([-D_FORTIFY_SOURCE=2],[ AX_CHECK_PREPROC_FLAG([-U_FORTIFY_SOURCE],[ HARDENED_CPPFLAGS="$HARDENED_CPPFLAGS -U_FORTIFY_SOURCE" ]) HARDENED_CPPFLAGS="$HARDENED_CPPFLAGS -D_FORTIFY_SOURCE=2" ]) AX_CHECK_LINK_FLAG([[-Wl,--dynamicbase]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--dynamicbase"]) AX_CHECK_LINK_FLAG([[-Wl,--nxcompat]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--nxcompat"]) AX_CHECK_LINK_FLAG([[-Wl,--high-entropy-va]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--high-entropy-va"]) AX_CHECK_LINK_FLAG([[-Wl,-z,relro]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,relro"]) AX_CHECK_LINK_FLAG([[-Wl,-z,now]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,now"]) if test x$TARGET_OS != xwindows; then AX_CHECK_COMPILE_FLAG([-fPIE],[PIE_FLAGS="-fPIE"]) AX_CHECK_LINK_FLAG([[-pie]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -pie"]) fi case $host in *mingw*) AC_CHECK_LIB([ssp], [main],, AC_MSG_ERROR(lib missing)) ;; esac fi dnl this flag screws up non-darwin gcc even when the check fails. special-case it. if test x$TARGET_OS = xdarwin; then AX_CHECK_LINK_FLAG([[-Wl,-dead_strip]], [LDFLAGS="$LDFLAGS -Wl,-dead_strip"]) fi AC_CHECK_HEADERS([endian.h sys/endian.h byteswap.h stdio.h stdlib.h unistd.h strings.h sys/types.h sys/stat.h sys/select.h sys/prctl.h]) AC_CHECK_DECLS([strnlen]) # Check for daemon(3), unrelated to --with-daemon (although used by it) AC_CHECK_DECLS([daemon]) AC_CHECK_DECLS([le16toh, le32toh, le64toh, htole16, htole32, htole64, be16toh, be32toh, be64toh, htobe16, htobe32, htobe64],,, [#if HAVE_ENDIAN_H #include #elif HAVE_SYS_ENDIAN_H #include #endif]) AC_CHECK_DECLS([bswap_16, bswap_32, bswap_64],,, [#if HAVE_BYTESWAP_H #include #endif]) +AC_CHECK_DECLS([__builtin_clz, __builtin_clzl, __builtin_clzll]) + dnl Check for MSG_NOSIGNAL AC_MSG_CHECKING(for MSG_NOSIGNAL) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ int f = MSG_NOSIGNAL; ]])], [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_MSG_NOSIGNAL, 1,[Define this symbol if you have MSG_NOSIGNAL]) ], [ AC_MSG_RESULT(no)] ) dnl Check for mallopt(M_ARENA_MAX) (to set glibc arenas) AC_MSG_CHECKING(for mallopt M_ARENA_MAX) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ mallopt(M_ARENA_MAX, 1); ]])], [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_MALLOPT_ARENA_MAX, 1,[Define this symbol if you have mallopt with M_ARENA_MAX]) ], [ AC_MSG_RESULT(no)] ) AC_MSG_CHECKING([for visibility attribute]) AC_LINK_IFELSE([AC_LANG_SOURCE([ int foo_def( void ) __attribute__((visibility("default"))); int main(){} ])], [ AC_DEFINE(HAVE_VISIBILITY_ATTRIBUTE,1,[Define if the visibility attribute is supported.]) AC_MSG_RESULT(yes) ], [ AC_MSG_RESULT(no) if test x$use_reduce_exports = xyes; then AC_MSG_ERROR([Cannot find a working visibility attribute. Use --disable-reduce-exports.]) fi ] ) # Check for different ways of gathering OS randomness AC_MSG_CHECKING(for Linux getrandom syscall) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include #include #include ]], [[ syscall(SYS_getrandom, nullptr, 32, 0); ]])], [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYS_GETRANDOM, 1,[Define this symbol if the Linux getrandom system call is available]) ], [ AC_MSG_RESULT(no)] ) AC_MSG_CHECKING(for getentropy) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ getentropy(nullptr, 32) ]])], [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_GETENTROPY, 1,[Define this symbol if the BSD getentropy system call is available]) ], [ AC_MSG_RESULT(no)] ) AC_MSG_CHECKING(for sysctl KERN_ARND) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include #include ]], [[ static const int name[2] = {CTL_KERN, KERN_ARND}; sysctl(name, 2, nullptr, nullptr, nullptr, 0); ]])], [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYSCTL_ARND, 1,[Define this symbol if the BSD sysctl(KERN_ARND) is available]) ], [ AC_MSG_RESULT(no)] ) # Check for reduced exports if test x$use_reduce_exports = xyes; then AX_CHECK_COMPILE_FLAG([-fvisibility=hidden],[RE_CXXFLAGS="-fvisibility=hidden"], [AC_MSG_ERROR([Cannot set default symbol visibility. Use --disable-reduce-exports.])]) fi LEVELDB_CPPFLAGS= LIBLEVELDB= LIBMEMENV= AM_CONDITIONAL([EMBEDDED_LEVELDB],[true]) AC_SUBST(LEVELDB_CPPFLAGS) AC_SUBST(LIBLEVELDB) AC_SUBST(LIBMEMENV) if test x$enable_wallet != xno; then dnl Check for libdb_cxx only if wallet enabled BITCOIN_FIND_BDB48 fi dnl Check for libminiupnpc (optional) if test x$use_upnp != xno; then AC_CHECK_HEADERS( [miniupnpc/miniwget.h miniupnpc/miniupnpc.h miniupnpc/upnpcommands.h miniupnpc/upnperrors.h], [AC_CHECK_LIB([miniupnpc], [main],[MINIUPNPC_LIBS=-lminiupnpc], [have_miniupnpc=no])], [have_miniupnpc=no] ) fi BITCOIN_QT_INIT dnl sets $bitcoin_enable_qt, $bitcoin_enable_qt_test, $bitcoin_enable_qt_dbus BITCOIN_QT_CONFIGURE([$use_pkgconfig], [qt5]) if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnonononono; then use_boost=no else use_boost=yes fi if test x$use_boost = xyes; then dnl Minimum required Boost version define(MINIMUM_REQUIRED_BOOST, 1.47.0) dnl Check for boost libs AX_BOOST_BASE([MINIMUM_REQUIRED_BOOST]) AX_BOOST_SYSTEM AX_BOOST_FILESYSTEM AX_BOOST_PROGRAM_OPTIONS AX_BOOST_THREAD AX_BOOST_CHRONO if test x$use_reduce_exports = xyes; then AC_MSG_CHECKING([for working boost reduced exports]) TEMP_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$BOOST_CPPFLAGS $CPPFLAGS" AC_PREPROC_IFELSE([AC_LANG_PROGRAM([[ @%:@include ]], [[ #if BOOST_VERSION >= 104900 // Everything is okay #else # error Boost version is too old #endif ]])],[ AC_MSG_RESULT(yes) ],[ AC_MSG_ERROR([boost versions < 1.49 are known to be broken with reduced exports. Use --disable-reduce-exports.]) ]) CPPFLAGS="$TEMP_CPPFLAGS" fi fi if test x$use_reduce_exports = xyes; then CXXFLAGS="$CXXFLAGS $RE_CXXFLAGS" AX_CHECK_LINK_FLAG([[-Wl,--exclude-libs,ALL]], [RELDFLAGS="-Wl,--exclude-libs,ALL"]) fi if test x$use_tests = xyes; then if test x$HEXDUMP = x; then AC_MSG_ERROR(hexdump is required for tests) fi if test x$use_boost = xyes; then AX_BOOST_UNIT_TEST_FRAMEWORK dnl Determine if -DBOOST_TEST_DYN_LINK is needed AC_MSG_CHECKING([for dynamic linked boost test]) TEMP_LIBS="$LIBS" LIBS="$LIBS $BOOST_LDFLAGS $BOOST_UNIT_TEST_FRAMEWORK_LIB" TEMP_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" AC_LINK_IFELSE([AC_LANG_SOURCE([ #define BOOST_TEST_DYN_LINK #define BOOST_TEST_MAIN #include ])], [AC_MSG_RESULT(yes)] [TESTDEFS="$TESTDEFS -DBOOST_TEST_DYN_LINK"], [AC_MSG_RESULT(no)]) LIBS="$TEMP_LIBS" CPPFLAGS="$TEMP_CPPFLAGS" fi fi if test x$use_boost = xyes; then BOOST_LIBS="$BOOST_LDFLAGS $BOOST_SYSTEM_LIB $BOOST_FILESYSTEM_LIB $BOOST_PROGRAM_OPTIONS_LIB $BOOST_THREAD_LIB $BOOST_CHRONO_LIB" dnl If boost (prior to 1.57) was built without c++11, it emulated scoped enums dnl using c++98 constructs. Unfortunately, this implementation detail leaked into dnl the abi. This was fixed in 1.57. dnl When building against that installed version using c++11, the headers pick up dnl on the native c++11 scoped enum support and enable it, however it will fail to dnl link. This can be worked around by disabling c++11 scoped enums if linking will dnl fail. dnl BOOST_NO_SCOPED_ENUMS was changed to BOOST_NO_CXX11_SCOPED_ENUMS in 1.51. TEMP_LIBS="$LIBS" LIBS="$BOOST_LIBS $LIBS" TEMP_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" AC_MSG_CHECKING([for mismatched boost c++11 scoped enums]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include "boost/config.hpp" #include "boost/version.hpp" #if !defined(BOOST_NO_SCOPED_ENUMS) && !defined(BOOST_NO_CXX11_SCOPED_ENUMS) && BOOST_VERSION < 105700 #define BOOST_NO_SCOPED_ENUMS #define BOOST_NO_CXX11_SCOPED_ENUMS #define CHECK #endif #include "boost/filesystem.hpp" ]],[[ #if defined(CHECK) boost::filesystem::copy_file("foo", "bar"); #else choke; #endif ]])], [AC_MSG_RESULT(mismatched); BOOST_CPPFLAGS="$BOOST_CPPFLAGS -DBOOST_NO_SCOPED_ENUMS -DBOOST_NO_CXX11_SCOPED_ENUMS"], [AC_MSG_RESULT(ok)]) LIBS="$TEMP_LIBS" CPPFLAGS="$TEMP_CPPFLAGS" dnl Boost >= 1.50 uses sleep_for rather than the now-deprecated sleep, however dnl it was broken from 1.50 to 1.52 when backed by nanosleep. Use sleep_for if dnl a working version is available, else fall back to sleep. sleep was removed dnl after 1.56. dnl If neither is available, abort. TEMP_LIBS="$LIBS" LIBS="$BOOST_LIBS $LIBS" TEMP_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include ]],[[ #if BOOST_VERSION >= 105000 && (!defined(BOOST_HAS_NANOSLEEP) || BOOST_VERSION >= 105200) boost::this_thread::sleep_for(boost::chrono::milliseconds(0)); #else choke me #endif ]])], [boost_sleep=yes; AC_DEFINE(HAVE_WORKING_BOOST_SLEEP_FOR, 1, [Define this symbol if boost sleep_for works])], [boost_sleep=no]) LIBS="$TEMP_LIBS" CPPFLAGS="$TEMP_CPPFLAGS" if test x$boost_sleep != xyes; then TEMP_LIBS="$LIBS" LIBS="$BOOST_LIBS $LIBS" TEMP_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include #include ]],[[ #if BOOST_VERSION <= 105600 boost::this_thread::sleep(boost::posix_time::milliseconds(0)); #else choke me #endif ]])], [boost_sleep=yes; AC_DEFINE(HAVE_WORKING_BOOST_SLEEP, 1, [Define this symbol if boost sleep works])], [boost_sleep=no]) LIBS="$TEMP_LIBS" CPPFLAGS="$TEMP_CPPFLAGS" fi if test x$boost_sleep != xyes; then AC_MSG_ERROR(No working boost sleep implementation found.) fi fi if test x$use_pkgconfig = xyes; then : dnl m4_ifdef( [PKG_CHECK_MODULES], [ PKG_CHECK_MODULES([SSL], [libssl],, [AC_MSG_ERROR(openssl not found.)]) PKG_CHECK_MODULES([CRYPTO], [libcrypto],,[AC_MSG_ERROR(libcrypto not found.)]) BITCOIN_QT_CHECK([PKG_CHECK_MODULES([PROTOBUF], [protobuf], [have_protobuf=yes], [BITCOIN_QT_FAIL(libprotobuf not found)])]) if test x$use_qr != xno; then BITCOIN_QT_CHECK([PKG_CHECK_MODULES([QR], [libqrencode], [have_qrencode=yes], [have_qrencode=no])]) fi if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests != xnononono; then PKG_CHECK_MODULES([EVENT], [libevent],, [AC_MSG_ERROR(libevent not found.)]) if test x$TARGET_OS != xwindows; then PKG_CHECK_MODULES([EVENT_PTHREADS], [libevent_pthreads],, [AC_MSG_ERROR(libevent_pthreads not found.)]) fi fi if test "x$use_zmq" = "xyes"; then PKG_CHECK_MODULES([ZMQ],[libzmq >= 4], [AC_DEFINE([ENABLE_ZMQ],[1],[Define to 1 to enable ZMQ functions])], [AC_DEFINE([ENABLE_ZMQ],[0],[Define to 1 to enable ZMQ functions]) AC_MSG_WARN([libzmq version 4.x or greater not found, disabling]) use_zmq=no]) else AC_DEFINE_UNQUOTED([ENABLE_ZMQ],[0],[Define to 1 to enable ZMQ functions]) fi ] ) else AC_CHECK_HEADER([openssl/crypto.h],,AC_MSG_ERROR(libcrypto headers missing)) AC_CHECK_LIB([crypto], [main],CRYPTO_LIBS=-lcrypto, AC_MSG_ERROR(libcrypto missing)) AC_CHECK_HEADER([openssl/ssl.h],, AC_MSG_ERROR(libssl headers missing),) AC_CHECK_LIB([ssl], [main],SSL_LIBS=-lssl, AC_MSG_ERROR(libssl missing)) if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests != xnononono; then AC_CHECK_HEADER([event2/event.h],, AC_MSG_ERROR(libevent headers missing),) AC_CHECK_LIB([event],[main],EVENT_LIBS=-levent,AC_MSG_ERROR(libevent missing)) if test x$TARGET_OS != xwindows; then AC_CHECK_LIB([event_pthreads],[main],EVENT_PTHREADS_LIBS=-levent_pthreads,AC_MSG_ERROR(libevent_pthreads missing)) fi fi if test "x$use_zmq" = "xyes"; then AC_CHECK_HEADER([zmq.h], [AC_DEFINE([ENABLE_ZMQ],[1],[Define to 1 to enable ZMQ functions])], [AC_MSG_WARN([zmq.h not found, disabling zmq support]) use_zmq=no AC_DEFINE([ENABLE_ZMQ],[0],[Define to 1 to enable ZMQ functions])]) AC_CHECK_LIB([zmq],[zmq_ctx_shutdown],ZMQ_LIBS=-lzmq, [AC_MSG_WARN([libzmq >= 4.0 not found, disabling zmq support]) use_zmq=no AC_DEFINE([ENABLE_ZMQ],[0],[Define to 1 to enable ZMQ functions])]) else AC_DEFINE_UNQUOTED([ENABLE_ZMQ],[0],[Define to 1 to enable ZMQ functions]) fi if test "x$use_zmq" = "xyes"; then dnl Assume libzmq was built for static linking case $host in *mingw*) ZMQ_CFLAGS="$ZMQ_CFLAGS -DZMQ_STATIC" ;; esac fi BITCOIN_QT_CHECK(AC_CHECK_LIB([protobuf] ,[main],[PROTOBUF_LIBS=-lprotobuf], BITCOIN_QT_FAIL(libprotobuf not found))) if test x$use_qr != xno; then BITCOIN_QT_CHECK([AC_CHECK_LIB([qrencode], [main],[QR_LIBS=-lqrencode], [have_qrencode=no])]) BITCOIN_QT_CHECK([AC_CHECK_HEADER([qrencode.h],, have_qrencode=no)]) fi fi save_CXXFLAGS="${CXXFLAGS}" CXXFLAGS="${CXXFLAGS} ${CRYPTO_CFLAGS} ${SSL_CFLAGS}" AC_CHECK_DECLS([EVP_MD_CTX_new],,,[AC_INCLUDES_DEFAULT #include ]) CXXFLAGS="${save_CXXFLAGS}" dnl univalue check need_bundled_univalue=yes if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnonononono; then need_bundled_univalue=no else if test x$system_univalue != xno ; then found_univalue=no if test x$use_pkgconfig = xyes; then : #NOP m4_ifdef( [PKG_CHECK_MODULES], [ PKG_CHECK_MODULES([UNIVALUE],[libunivalue],[found_univalue=yes],[true]) ] ) else AC_CHECK_HEADER([univalue.h],[ AC_CHECK_LIB([univalue], [main],[ UNIVALUE_LIBS=-lunivalue found_univalue=yes ],[true]) ],[true]) fi if test x$found_univalue = xyes ; then system_univalue=yes need_bundled_univalue=no elif test x$system_univalue = xyes ; then AC_MSG_ERROR([univalue not found]) else system_univalue=no fi fi if test x$need_bundled_univalue = xyes ; then UNIVALUE_CFLAGS='-I$(srcdir)/univalue/include' UNIVALUE_LIBS='univalue/libunivalue.la' fi fi AM_CONDITIONAL([EMBEDDED_UNIVALUE],[test x$need_bundled_univalue = xyes]) AC_SUBST(UNIVALUE_CFLAGS) AC_SUBST(UNIVALUE_LIBS) BITCOIN_QT_PATH_PROGS([PROTOC], [protoc],$protoc_bin_path) AC_MSG_CHECKING([whether to build bitcoind]) AM_CONDITIONAL([BUILD_BITCOIND], [test x$build_bitcoind = xyes]) AC_MSG_RESULT($build_bitcoind) AC_MSG_CHECKING([whether to build utils (bitcoin-cli bitcoin-tx)]) AM_CONDITIONAL([BUILD_BITCOIN_UTILS], [test x$build_bitcoin_utils = xyes]) AC_MSG_RESULT($build_bitcoin_utils) AC_MSG_CHECKING([whether to build libraries]) AM_CONDITIONAL([BUILD_BITCOIN_LIBS], [test x$build_bitcoin_libs = xyes]) if test x$build_bitcoin_libs = xyes; then AC_DEFINE(HAVE_CONSENSUS_LIB, 1, [Define this symbol if the consensus lib has been built]) AC_CONFIG_FILES([libbitcoinconsensus.pc:libbitcoinconsensus.pc.in]) fi AC_MSG_RESULT($build_bitcoin_libs) AC_LANG_POP if test "x$use_ccache" != "xno"; then AC_MSG_CHECKING(if ccache should be used) if test x$CCACHE = x; then if test "x$use_ccache" = "xyes"; then AC_MSG_ERROR([ccache not found.]); else use_ccache=no fi else use_ccache=yes CC="$ac_cv_path_CCACHE $CC" CXX="$ac_cv_path_CCACHE $CXX" fi AC_MSG_RESULT($use_ccache) fi if test "x$use_ccache" = "xyes"; then AX_CHECK_PREPROC_FLAG([-Qunused-arguments],[CPPFLAGS="-Qunused-arguments $CPPFLAGS"]) fi dnl enable wallet AC_MSG_CHECKING([if wallet should be enabled]) if test x$enable_wallet != xno; then AC_MSG_RESULT(yes) AC_DEFINE_UNQUOTED([ENABLE_WALLET],[1],[Define to 1 to enable wallet functions]) else AC_MSG_RESULT(no) fi dnl enable upnp support AC_MSG_CHECKING([whether to build with support for UPnP]) if test x$have_miniupnpc = xno; then if test x$use_upnp = xyes; then AC_MSG_ERROR("UPnP requested but cannot be built. use --without-miniupnpc") fi AC_MSG_RESULT(no) else if test x$use_upnp != xno; then AC_MSG_RESULT(yes) AC_MSG_CHECKING([whether to build with UPnP enabled by default]) use_upnp=yes upnp_setting=0 if test x$use_upnp_default != xno; then use_upnp_default=yes upnp_setting=1 fi AC_MSG_RESULT($use_upnp_default) AC_DEFINE_UNQUOTED([USE_UPNP],[$upnp_setting],[UPnP support not compiled if undefined, otherwise value (0 or 1) determines default state]) if test x$TARGET_OS = xwindows; then MINIUPNPC_CPPFLAGS="-DSTATICLIB -DMINIUPNP_STATICLIB" fi else AC_MSG_RESULT(no) fi fi dnl these are only used when qt is enabled BUILD_TEST_QT="" if test x$bitcoin_enable_qt != xno; then dnl enable dbus support AC_MSG_CHECKING([whether to build GUI with support for D-Bus]) if test x$bitcoin_enable_qt_dbus != xno; then AC_DEFINE([USE_DBUS],[1],[Define if dbus support should be compiled in]) fi AC_MSG_RESULT($bitcoin_enable_qt_dbus) dnl enable qr support AC_MSG_CHECKING([whether to build GUI with support for QR codes]) if test x$have_qrencode = xno; then if test x$use_qr = xyes; then AC_MSG_ERROR("QR support requested but cannot be built. use --without-qrencode") fi AC_MSG_RESULT(no) else if test x$use_qr != xno; then AC_MSG_RESULT(yes) AC_DEFINE([USE_QRCODE],[1],[Define if QR support should be compiled in]) use_qr=yes else AC_MSG_RESULT(no) fi fi if test x$XGETTEXT = x; then AC_MSG_WARN("xgettext is required to update qt translations") fi AC_MSG_CHECKING([whether to build test_bitcoin-qt]) if test x$use_gui_tests$bitcoin_enable_qt_test = xyesyes; then AC_MSG_RESULT([yes]) BUILD_TEST_QT="yes" else AC_MSG_RESULT([no]) fi fi AM_CONDITIONAL([ENABLE_ZMQ], [test "x$use_zmq" = "xyes"]) AC_MSG_CHECKING([whether to build test_bitcoin]) if test x$use_tests = xyes; then AC_MSG_RESULT([yes]) BUILD_TEST="yes" else AC_MSG_RESULT([no]) BUILD_TEST="" fi AC_MSG_CHECKING([whether to reduce exports]) if test x$use_reduce_exports = xyes; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi if test x$build_bitcoin_utils$build_bitcoin_libs$build_bitcoind$bitcoin_enable_qt$use_bench$use_tests = xnononononono; then AC_MSG_ERROR([No targets! Please specify at least one of: --with-utils --with-libs --with-daemon --with-gui --enable-bench or --enable-tests]) fi AM_CONDITIONAL([TARGET_DARWIN], [test x$TARGET_OS = xdarwin]) AM_CONDITIONAL([BUILD_DARWIN], [test x$BUILD_OS = xdarwin]) AM_CONDITIONAL([TARGET_WINDOWS], [test x$TARGET_OS = xwindows]) AM_CONDITIONAL([ENABLE_WALLET],[test x$enable_wallet = xyes]) AM_CONDITIONAL([ENABLE_TESTS],[test x$BUILD_TEST = xyes]) AM_CONDITIONAL([ENABLE_QT],[test x$bitcoin_enable_qt = xyes]) AM_CONDITIONAL([ENABLE_QT_TESTS],[test x$BUILD_TEST_QT = xyes]) AM_CONDITIONAL([ENABLE_BENCH],[test x$use_bench = xyes]) AM_CONDITIONAL([USE_QRCODE], [test x$use_qr = xyes]) AM_CONDITIONAL([USE_LCOV],[test x$use_lcov = xyes]) AM_CONDITIONAL([GLIBC_BACK_COMPAT],[test x$use_glibc_compat = xyes]) AM_CONDITIONAL([HARDEN],[test x$use_hardening = xyes]) AC_DEFINE(CLIENT_VERSION_MAJOR, _CLIENT_VERSION_MAJOR, [Major version]) AC_DEFINE(CLIENT_VERSION_MINOR, _CLIENT_VERSION_MINOR, [Minor version]) AC_DEFINE(CLIENT_VERSION_REVISION, _CLIENT_VERSION_REVISION, [Build revision]) AC_DEFINE(CLIENT_VERSION_BUILD, _CLIENT_VERSION_BUILD, [Version Build]) AC_DEFINE(CLIENT_VERSION_IS_RELEASE, _CLIENT_VERSION_IS_RELEASE, [Version is release]) AC_DEFINE(COPYRIGHT_YEAR, _COPYRIGHT_YEAR, [Copyright year]) AC_DEFINE(COPYRIGHT_HOLDERS, "_COPYRIGHT_HOLDERS", [Copyright holder(s) before %s replacement]) AC_DEFINE(COPYRIGHT_HOLDERS_SUBSTITUTION, "_COPYRIGHT_HOLDERS_SUBSTITUTION", [Replacement for %s in copyright holders string]) define(_COPYRIGHT_HOLDERS_FINAL, [patsubst(_COPYRIGHT_HOLDERS, [%s], [_COPYRIGHT_HOLDERS_SUBSTITUTION])]) AC_DEFINE(COPYRIGHT_HOLDERS_FINAL, "_COPYRIGHT_HOLDERS_FINAL", [Copyright holder(s)]) AC_SUBST(CLIENT_VERSION_MAJOR, _CLIENT_VERSION_MAJOR) AC_SUBST(CLIENT_VERSION_MINOR, _CLIENT_VERSION_MINOR) AC_SUBST(CLIENT_VERSION_REVISION, _CLIENT_VERSION_REVISION) AC_SUBST(CLIENT_VERSION_BUILD, _CLIENT_VERSION_BUILD) AC_SUBST(CLIENT_VERSION_IS_RELEASE, _CLIENT_VERSION_IS_RELEASE) AC_SUBST(COPYRIGHT_YEAR, _COPYRIGHT_YEAR) AC_SUBST(COPYRIGHT_HOLDERS, "_COPYRIGHT_HOLDERS") AC_SUBST(COPYRIGHT_HOLDERS_SUBSTITUTION, "_COPYRIGHT_HOLDERS_SUBSTITUTION") AC_SUBST(COPYRIGHT_HOLDERS_FINAL, "_COPYRIGHT_HOLDERS_FINAL") AC_SUBST(BITCOIN_DAEMON_NAME) AC_SUBST(BITCOIN_GUI_NAME) AC_SUBST(BITCOIN_CLI_NAME) AC_SUBST(BITCOIN_TX_NAME) AC_SUBST(RELDFLAGS) AC_SUBST(ERROR_CXXFLAGS) AC_SUBST(HARDENED_CXXFLAGS) AC_SUBST(HARDENED_CPPFLAGS) AC_SUBST(HARDENED_LDFLAGS) AC_SUBST(PIC_FLAGS) AC_SUBST(PIE_FLAGS) AC_SUBST(LIBTOOL_APP_LDFLAGS) AC_SUBST(USE_UPNP) AC_SUBST(USE_QRCODE) AC_SUBST(BOOST_LIBS) AC_SUBST(TESTDEFS) AC_SUBST(LEVELDB_TARGET_FLAGS) AC_SUBST(MINIUPNPC_CPPFLAGS) AC_SUBST(MINIUPNPC_LIBS) AC_SUBST(CRYPTO_LIBS) AC_SUBST(SSL_LIBS) AC_SUBST(EVENT_LIBS) AC_SUBST(EVENT_PTHREADS_LIBS) AC_SUBST(ZMQ_LIBS) AC_SUBST(PROTOBUF_LIBS) AC_SUBST(QR_LIBS) AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi share/qt/Info.plist src/test/buildenv.py]) AC_CONFIG_FILES([qa/pull-tester/tests_config.py],[chmod +x qa/pull-tester/tests_config.py]) AC_CONFIG_FILES([contrib/devtools/split-debug.sh],[chmod +x contrib/devtools/split-debug.sh]) AC_CONFIG_LINKS([qa/pull-tester/rpc-tests.py:qa/pull-tester/rpc-tests.py]) dnl boost's m4 checks do something really nasty: they export these vars. As a dnl result, they leak into secp256k1's configure and crazy things happen. dnl Until this is fixed upstream and we've synced, we'll just un-export them. CPPFLAGS_TEMP="$CPPFLAGS" unset CPPFLAGS CPPFLAGS="$CPPFLAGS_TEMP" LDFLAGS_TEMP="$LDFLAGS" unset LDFLAGS LDFLAGS="$LDFLAGS_TEMP" LIBS_TEMP="$LIBS" unset LIBS LIBS="$LIBS_TEMP" PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH" unset PKG_CONFIG_PATH PKG_CONFIG_PATH="$PKGCONFIG_PATH_TEMP" PKGCONFIG_LIBDIR_TEMP="$PKG_CONFIG_LIBDIR" unset PKG_CONFIG_LIBDIR PKG_CONFIG_LIBDIR="$PKGCONFIG_LIBDIR_TEMP" if test x$need_bundled_univalue = xyes; then AC_CONFIG_SUBDIRS([src/univalue]) fi ac_configure_args="${ac_configure_args} --disable-shared --with-pic --with-bignum=no --enable-module-recovery" AC_CONFIG_SUBDIRS([src/secp256k1]) AC_OUTPUT dnl Taken from https://wiki.debian.org/RpathIssue case $host in *-*-linux-gnu) AC_MSG_RESULT([Fixing libtool for -rpath problems.]) sed < libtool > libtool-2 \ 's/^hardcode_libdir_flag_spec.*$'/'hardcode_libdir_flag_spec=" -D__LIBTOOL_IS_A_FOOL__ "/' mv libtool-2 libtool chmod 755 libtool ;; esac dnl Replace the BUILDDIR path with the correct Windows path if compiling on Native Windows case ${OS} in *Windows*) sed 's/BUILDDIR="\/\([[a-z]]\)/BUILDDIR="\1:/' qa/pull-tester/tests_config.py > qa/pull-tester/tests_config-2.py mv qa/pull-tester/tests_config-2.py qa/pull-tester/tests_config.py ;; esac echo echo "Options used to compile and link:" echo " with wallet = $enable_wallet" echo " with gui / qt = $bitcoin_enable_qt" if test x$bitcoin_enable_qt != xno; then echo " qt version = $bitcoin_qt_got_major_vers" echo " with qr = $use_qr" fi echo " with zmq = $use_zmq" echo " with test = $use_tests" echo " with bench = $use_bench" echo " with upnp = $use_upnp" echo " debug enabled = $enable_debug" echo " werror = $enable_werror" echo echo " sanitizers " echo " asan = $enable_asan" echo " tsan = $enable_tsan" echo " ubsan = $enable_ubsan" echo echo " target os = $TARGET_OS" echo " build os = $BUILD_OS" echo echo " CC = $CC" echo " CFLAGS = $CFLAGS" echo " CPPFLAGS = $CPPFLAGS" echo " CXX = $CXX" echo " CXXFLAGS = $CXXFLAGS" echo " LDFLAGS = $LDFLAGS" echo diff --git a/src/Makefile.am b/src/Makefile.am index 17a7dc92f..93aad2445 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,517 +1,519 @@ # Copyright (c) 2013-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. DIST_SUBDIRS = secp256k1 univalue AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) AM_CXXFLAGS = $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) AM_CPPFLAGS = $(HARDENED_CPPFLAGS) EXTRA_LIBRARIES = if EMBEDDED_UNIVALUE LIBUNIVALUE = univalue/libunivalue.la $(LIBUNIVALUE): $(wildcard univalue/lib/*) $(wildcard univalue/include/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) else LIBUNIVALUE = $(UNIVALUE_LIBS) endif BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS) BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include BITCOIN_INCLUDES += $(UNIVALUE_CFLAGS) LIBBITCOIN_SERVER=libbitcoin_server.a LIBBITCOIN_COMMON=libbitcoin_common.a LIBBITCOIN_CONSENSUS=libbitcoin_consensus.a LIBBITCOIN_CLI=libbitcoin_cli.a LIBBITCOIN_UTIL=libbitcoin_util.a LIBBITCOIN_CRYPTO=crypto/libbitcoin_crypto.a LIBBITCOINQT=qt/libbitcoinqt.a LIBSECP256K1=secp256k1/libsecp256k1.la if ENABLE_ZMQ LIBBITCOIN_ZMQ=libbitcoin_zmq.a endif if BUILD_BITCOIN_LIBS LIBBITCOINCONSENSUS=libbitcoinconsensus.la endif if ENABLE_WALLET LIBBITCOIN_WALLET=libbitcoin_wallet.a endif $(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) # 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 += \ $(LIBBITCOIN_CRYPTO) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_CONSENSUS) \ $(LIBBITCOIN_SERVER) \ $(LIBBITCOIN_CLI) \ $(LIBBITCOIN_WALLET) \ $(LIBBITCOIN_ZMQ) lib_LTLIBRARIES = $(LIBBITCOINCONSENSUS) bin_PROGRAMS = noinst_PROGRAMS = TESTS = BENCHMARKS = if BUILD_BITCOIND bin_PROGRAMS += bitcoind endif if BUILD_BITCOIN_UTILS bin_PROGRAMS += bitcoin-cli bitcoin-tx endif .PHONY: FORCE check-symbols check-security # bitcoin core # BITCOIN_CORE_H = \ addrdb.h \ addrman.h \ base58.h \ bloom.h \ blockencodings.h \ chain.h \ chainparams.h \ chainparamsbase.h \ chainparamsseeds.h \ checkpoints.h \ checkqueue.h \ clientversion.h \ coins.h \ compat.h \ compat/byteswap.h \ compat/endian.h \ compat/sanity.h \ compressor.h \ config.h \ consensus/consensus.h \ core_io.h \ core_memusage.h \ cuckoocache.h \ globals.h \ httprpc.h \ httpserver.h \ indirectmap.h \ init.h \ key.h \ keystore.h \ dbwrapper.h \ limitedmap.h \ memusage.h \ merkleblock.h \ miner.h \ net.h \ net_processing.h \ netaddress.h \ netbase.h \ netmessagemaker.h \ noui.h \ policy/fees.h \ policy/policy.h \ pow.h \ protocol.h \ random.h \ reverselock.h \ rpc/blockchain.h \ rpc/client.h \ rpc/misc.h \ rpc/protocol.h \ rpc/server.h \ rpc/register.h \ scheduler.h \ script/sigcache.h \ script/sign.h \ script/standard.h \ script/ismine.h \ streams.h \ support/allocators/secure.h \ support/allocators/zeroafterfree.h \ support/cleanse.h \ support/events.h \ support/lockedpool.h \ sync.h \ threadsafety.h \ threadinterrupt.h \ timedata.h \ torcontrol.h \ txdb.h \ txmempool.h \ ui_interface.h \ undo.h \ util.h \ utilmoneystr.h \ utiltime.h \ validation.h \ validationinterface.h \ versionbits.h \ wallet/coincontrol.h \ wallet/crypter.h \ wallet/db.h \ wallet/finaltx.h \ wallet/rpcdump.h \ wallet/rpcwallet.h \ wallet/wallet.h \ wallet/walletdb.h \ warnings.h \ zmq/zmqabstractnotifier.h \ zmq/zmqconfig.h\ zmq/zmqnotificationinterface.h \ zmq/zmqpublishnotifier.h obj/build.h: FORCE @$(MKDIR_P) $(builddir)/obj @$(top_srcdir)/share/genbuild.sh $(abs_top_builddir)/src/obj/build.h \ $(abs_top_srcdir) libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h # server: shared between bitcoind and bitcoin-qt libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS) libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_server_a_SOURCES = \ addrman.cpp \ addrdb.cpp \ bloom.cpp \ blockencodings.cpp \ chain.cpp \ checkpoints.cpp \ config.cpp \ globals.cpp \ httprpc.cpp \ httpserver.cpp \ init.cpp \ dbwrapper.cpp \ merkleblock.cpp \ miner.cpp \ net.cpp \ net_processing.cpp \ noui.cpp \ policy/fees.cpp \ policy/policy.cpp \ pow.cpp \ rest.cpp \ rpc/abc.cpp \ rpc/blockchain.cpp \ rpc/mining.cpp \ rpc/misc.cpp \ rpc/net.cpp \ rpc/rawtransaction.cpp \ rpc/server.cpp \ script/sigcache.cpp \ script/ismine.cpp \ timedata.cpp \ torcontrol.cpp \ txdb.cpp \ txmempool.cpp \ ui_interface.cpp \ validation.cpp \ validationinterface.cpp \ versionbits.cpp \ $(BITCOIN_CORE_H) if ENABLE_ZMQ libbitcoin_zmq_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(ZMQ_CFLAGS) libbitcoin_zmq_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_zmq_a_SOURCES = \ zmq/zmqabstractnotifier.cpp \ zmq/zmqnotificationinterface.cpp \ zmq/zmqpublishnotifier.cpp endif # wallet: shared between bitcoind and bitcoin-qt, but only linked # when wallet enabled libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_wallet_a_SOURCES = \ wallet/crypter.cpp \ wallet/db.cpp \ wallet/finaltx.cpp \ wallet/rpcdump.cpp \ wallet/rpcwallet.cpp \ wallet/wallet.cpp \ wallet/walletdb.cpp \ $(BITCOIN_CORE_H) # crypto primitives library crypto_libbitcoin_crypto_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_CONFIG_INCLUDES) crypto_libbitcoin_crypto_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) crypto_libbitcoin_crypto_a_SOURCES = \ crypto/aes.cpp \ crypto/aes.h \ + crypto/chacha20.h \ + crypto/chacha20.cpp \ crypto/common.h \ crypto/hmac_sha256.cpp \ crypto/hmac_sha256.h \ crypto/hmac_sha512.cpp \ crypto/hmac_sha512.h \ crypto/ripemd160.cpp \ crypto/ripemd160.h \ crypto/sha1.cpp \ crypto/sha1.h \ crypto/sha256.cpp \ crypto/sha256.h \ crypto/sha512.cpp \ crypto/sha512.h # consensus: shared between all executables that validate any consensus rules. libbitcoin_consensus_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_consensus_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_consensus_a_SOURCES = \ amount.h \ arith_uint256.cpp \ arith_uint256.h \ consensus/merkle.cpp \ consensus/merkle.h \ consensus/params.h \ consensus/validation.h \ hash.cpp \ hash.h \ prevector.h \ primitives/block.cpp \ primitives/block.h \ primitives/transaction.cpp \ primitives/transaction.h \ pubkey.cpp \ pubkey.h \ script/bitcoinconsensus.cpp \ script/interpreter.cpp \ script/interpreter.h \ script/script.cpp \ script/script.h \ script/script_error.cpp \ script/script_error.h \ serialize.h \ tinyformat.h \ uint256.cpp \ uint256.h \ utilstrencodings.cpp \ utilstrencodings.h \ version.h # common: shared between bitcoind, and bitcoin-qt and non-server tools libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_common_a_SOURCES = \ amount.cpp \ base58.cpp \ chainparams.cpp \ coins.cpp \ compressor.cpp \ core_read.cpp \ core_write.cpp \ key.cpp \ keystore.cpp \ netaddress.cpp \ netbase.cpp \ protocol.cpp \ scheduler.cpp \ script/sign.cpp \ script/standard.cpp \ warnings.cpp \ $(BITCOIN_CORE_H) # util: shared between all executables. # This library *must* be included to make sure that the glibc # backward-compatibility objects and their sanity checks are linked. libbitcoin_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_util_a_SOURCES = \ support/lockedpool.cpp \ chainparamsbase.cpp \ clientversion.cpp \ compat/glibc_sanity.cpp \ compat/glibcxx_sanity.cpp \ compat/strnlen.cpp \ random.cpp \ rpc/protocol.cpp \ support/cleanse.cpp \ sync.cpp \ threadinterrupt.cpp \ util.cpp \ utilmoneystr.cpp \ utilstrencodings.cpp \ utiltime.cpp \ $(BITCOIN_CORE_H) if GLIBC_BACK_COMPAT libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp endif # cli: shared between bitcoin-cli and bitcoin-qt libbitcoin_cli_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_cli_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_cli_a_SOURCES = \ rpc/client.cpp \ $(BITCOIN_CORE_H) nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h # # bitcoind binary # bitcoind_SOURCES = bitcoind.cpp bitcoind_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) bitcoind_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) bitcoind_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) if TARGET_WINDOWS bitcoind_SOURCES += bitcoind-res.rc endif bitcoind_LDADD = \ $(LIBBITCOIN_SERVER) \ $(LIBBITCOIN_COMMON) \ $(LIBUNIVALUE) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_WALLET) \ $(LIBBITCOIN_ZMQ) \ $(LIBBITCOIN_CONSENSUS) \ $(LIBBITCOIN_CRYPTO) \ $(LIBLEVELDB) \ $(LIBMEMENV) \ $(LIBSECP256K1) bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) # bitcoin-cli binary # bitcoin_cli_SOURCES = bitcoin-cli.cpp bitcoin_cli_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CFLAGS) bitcoin_cli_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) bitcoin_cli_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) if TARGET_WINDOWS bitcoin_cli_SOURCES += bitcoin-cli-res.rc endif bitcoin_cli_LDADD = \ $(LIBBITCOIN_CLI) \ $(LIBUNIVALUE) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CRYPTO) bitcoin_cli_LDADD += $(BOOST_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS) # # bitcoin-tx binary # bitcoin_tx_SOURCES = bitcoin-tx.cpp bitcoin_tx_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) bitcoin_tx_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) bitcoin_tx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) if TARGET_WINDOWS bitcoin_tx_SOURCES += bitcoin-tx-res.rc endif bitcoin_tx_LDADD = \ $(LIBUNIVALUE) \ $(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CONSENSUS) \ $(LIBBITCOIN_CRYPTO) \ $(LIBSECP256K1) bitcoin_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) # # bitcoinconsensus library # if BUILD_BITCOIN_LIBS include_HEADERS = script/bitcoinconsensus.h libbitcoinconsensus_la_SOURCES = $(crypto_libbitcoin_crypto_a_SOURCES) $(libbitcoin_consensus_a_SOURCES) if GLIBC_BACK_COMPAT libbitcoinconsensus_la_SOURCES += compat/glibc_compat.cpp endif libbitcoinconsensus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined $(RELDFLAGS) libbitcoinconsensus_la_LIBADD = $(LIBSECP256K1) libbitcoinconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL libbitcoinconsensus_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) endif # CTAES_DIST = crypto/ctaes/bench.c CTAES_DIST += crypto/ctaes/ctaes.c CTAES_DIST += crypto/ctaes/ctaes.h CTAES_DIST += crypto/ctaes/README.md CTAES_DIST += crypto/ctaes/test.c CLEANFILES = $(EXTRA_LIBRARIES) CLEANFILES += *.gcda *.gcno CLEANFILES += compat/*.gcda compat/*.gcno CLEANFILES += consensus/*.gcda consensus/*.gcno CLEANFILES += crypto/*.gcda crypto/*.gcno CLEANFILES += policy/*.gcda policy/*.gcno CLEANFILES += primitives/*.gcda primitives/*.gcno CLEANFILES += script/*.gcda script/*.gcno CLEANFILES += support/*.gcda support/*.gcno CLEANFILES += univalue/*.gcda univalue/*.gcno CLEANFILES += wallet/*.gcda wallet/*.gcno CLEANFILES += wallet/test/*.gcda wallet/test/*.gcno CLEANFILES += zmq/*.gcda zmq/*.gcno DISTCLEANFILES = obj/build.h EXTRA_DIST = $(CTAES_DIST) clean-local: -$(MAKE) -C secp256k1 clean -$(MAKE) -C univalue clean -rm -f leveldb/*/*.gcda leveldb/*/*.gcno leveldb/helpers/memenv/*.gcda leveldb/helpers/memenv/*.gcno -rm -rf test/__pycache__ .rc.o: @test -f $(WINDRES) ## FIXME: How to get the appropriate modulename_CPPFLAGS in here? $(AM_V_GEN) $(WINDRES) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(CPPFLAGS) -DWINDRES_PREPROC -i $< -o $@ .mm.o: $(AM_V_CXX) $(OBJCXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CXXFLAGS) $(QT_INCLUDES) $(AM_CXXFLAGS) $(PIE_FLAGS) $(CXXFLAGS) -c -o $@ $< check-symbols: $(bin_PROGRAMS) if GLIBC_BACK_COMPAT @echo "Checking glibc back compat..." $(AM_V_at) READELF=$(READELF) CPPFILT=$(CPPFILT) $(top_srcdir)/contrib/devtools/symbol-check.py < $(bin_PROGRAMS) endif check-security: $(bin_PROGRAMS) if HARDEN @echo "Checking binary security..." $(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) $(top_srcdir)/contrib/devtools/security-check.py < $(bin_PROGRAMS) endif %.pb.cc %.pb.h: %.proto @test -f $(PROTOC) $(AM_V_GEN) $(PROTOC) --cpp_out=$(@D) --proto_path=$( vchSourceGroupKey = src.GetGroup(); uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup() << vchSourceGroupKey) .GetHash() .GetCheapHash(); uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)) .GetHash() .GetCheapHash(); return hash2 % ADDRMAN_NEW_BUCKET_COUNT; } int CAddrInfo::GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const { uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << (fNew ? 'N' : 'K') << nBucket << GetKey()) .GetHash() .GetCheapHash(); return hash1 % ADDRMAN_BUCKET_SIZE; } bool CAddrInfo::IsTerrible(int64_t nNow) const { // never remove things tried in the last minute if (nLastTry && nLastTry >= nNow - 60) return false; // came in a flying DeLorean if (nTime > nNow + 10 * 60) return true; // not seen in recent history if (nTime == 0 || nNow - nTime > ADDRMAN_HORIZON_DAYS * 24 * 60 * 60) return true; // tried N times and never a success if (nLastSuccess == 0 && nAttempts >= ADDRMAN_RETRIES) return true; if (nNow - nLastSuccess > ADDRMAN_MIN_FAIL_DAYS * 24 * 60 * 60 && nAttempts >= ADDRMAN_MAX_FAILURES) // N successive failures in the last week return true; return false; } double CAddrInfo::GetChance(int64_t nNow) const { double fChance = 1.0; int64_t nSinceLastTry = std::max(nNow - nLastTry, 0); // deprioritize very recent attempts away if (nSinceLastTry < 60 * 10) fChance *= 0.01; // deprioritize 66% after each failed attempt, but at most 1/28th to avoid // the search taking forever or overly penalizing outages. fChance *= pow(0.66, std::min(nAttempts, 8)); return fChance; } CAddrInfo *CAddrMan::Find(const CNetAddr &addr, int *pnId) { std::map::iterator it = mapAddr.find(addr); if (it == mapAddr.end()) return nullptr; if (pnId) *pnId = (*it).second; std::map::iterator it2 = mapInfo.find((*it).second); if (it2 != mapInfo.end()) return &(*it2).second; return nullptr; } CAddrInfo *CAddrMan::Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId) { int nId = nIdCount++; mapInfo[nId] = CAddrInfo(addr, addrSource); mapAddr[addr] = nId; mapInfo[nId].nRandomPos = vRandom.size(); vRandom.push_back(nId); if (pnId) *pnId = nId; return &mapInfo[nId]; } void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2) { if (nRndPos1 == nRndPos2) return; assert(nRndPos1 < vRandom.size() && nRndPos2 < vRandom.size()); int nId1 = vRandom[nRndPos1]; int nId2 = vRandom[nRndPos2]; assert(mapInfo.count(nId1) == 1); assert(mapInfo.count(nId2) == 1); mapInfo[nId1].nRandomPos = nRndPos2; mapInfo[nId2].nRandomPos = nRndPos1; vRandom[nRndPos1] = nId2; vRandom[nRndPos2] = nId1; } void CAddrMan::Delete(int nId) { assert(mapInfo.count(nId) != 0); CAddrInfo &info = mapInfo[nId]; assert(!info.fInTried); assert(info.nRefCount == 0); SwapRandom(info.nRandomPos, vRandom.size() - 1); vRandom.pop_back(); mapAddr.erase(info); mapInfo.erase(nId); nNew--; } void CAddrMan::ClearNew(int nUBucket, int nUBucketPos) { // if there is an entry in the specified bucket, delete it. if (vvNew[nUBucket][nUBucketPos] != -1) { int nIdDelete = vvNew[nUBucket][nUBucketPos]; CAddrInfo &infoDelete = mapInfo[nIdDelete]; assert(infoDelete.nRefCount > 0); infoDelete.nRefCount--; vvNew[nUBucket][nUBucketPos] = -1; if (infoDelete.nRefCount == 0) { Delete(nIdDelete); } } } void CAddrMan::MakeTried(CAddrInfo &info, int nId) { // remove the entry from all new buckets for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) { int pos = info.GetBucketPosition(nKey, true, bucket); if (vvNew[bucket][pos] == nId) { vvNew[bucket][pos] = -1; info.nRefCount--; } } nNew--; assert(info.nRefCount == 0); // which tried bucket to move the entry to int nKBucket = info.GetTriedBucket(nKey); int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket); // first make space to add it (the existing tried entry there is moved to // new, deleting whatever is there). if (vvTried[nKBucket][nKBucketPos] != -1) { // find an item to evict int nIdEvict = vvTried[nKBucket][nKBucketPos]; assert(mapInfo.count(nIdEvict) == 1); CAddrInfo &infoOld = mapInfo[nIdEvict]; // Remove the to-be-evicted item from the tried set. infoOld.fInTried = false; vvTried[nKBucket][nKBucketPos] = -1; nTried--; // find which new bucket it belongs to int nUBucket = infoOld.GetNewBucket(nKey); int nUBucketPos = infoOld.GetBucketPosition(nKey, true, nUBucket); ClearNew(nUBucket, nUBucketPos); assert(vvNew[nUBucket][nUBucketPos] == -1); // Enter it into the new set again. infoOld.nRefCount = 1; vvNew[nUBucket][nUBucketPos] = nIdEvict; nNew++; } assert(vvTried[nKBucket][nKBucketPos] == -1); vvTried[nKBucket][nKBucketPos] = nId; nTried++; info.fInTried = true; } void CAddrMan::Good_(const CService &addr, int64_t nTime) { int nId; nLastGood = nTime; CAddrInfo *pinfo = Find(addr, &nId); // if not found, bail out if (!pinfo) return; CAddrInfo &info = *pinfo; // check whether we are talking about the exact same CService (including // same port) if (info != addr) return; // update info info.nLastSuccess = nTime; info.nLastTry = nTime; info.nAttempts = 0; // nTime is not updated here, to avoid leaking information about // currently-connected peers. // if it is already in the tried set, don't do anything else if (info.fInTried) return; // find a bucket it is in now int nRnd = RandomInt(ADDRMAN_NEW_BUCKET_COUNT); int nUBucket = -1; for (unsigned int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) { int nB = (n + nRnd) % ADDRMAN_NEW_BUCKET_COUNT; int nBpos = info.GetBucketPosition(nKey, true, nB); if (vvNew[nB][nBpos] == nId) { nUBucket = nB; break; } } // if no bucket is found, something bad happened; // TODO: maybe re-add the node, but for now, just bail out if (nUBucket == -1) return; LogPrint("addrman", "Moving %s to tried\n", addr.ToString()); // move nId to the tried tables MakeTried(info, nId); } bool CAddrMan::Add_(const CAddress &addr, const CNetAddr &source, int64_t nTimePenalty) { if (!addr.IsRoutable()) return false; bool fNew = false; int nId; CAddrInfo *pinfo = Find(addr, &nId); // Do not set a penalty for a source's self-announcement if (addr == source) { nTimePenalty = 0; } if (pinfo) { // periodically update nTime bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < 24 * 60 * 60); int64_t nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60); if (addr.nTime && (!pinfo->nTime || pinfo->nTime < addr.nTime - nUpdateInterval - nTimePenalty)) pinfo->nTime = std::max((int64_t)0, addr.nTime - nTimePenalty); // add services pinfo->nServices = ServiceFlags(pinfo->nServices | addr.nServices); // do not update if no new information is present if (!addr.nTime || (pinfo->nTime && addr.nTime <= pinfo->nTime)) return false; // do not update if the entry was already in the "tried" table if (pinfo->fInTried) return false; // do not update if the max reference count is reached if (pinfo->nRefCount == ADDRMAN_NEW_BUCKETS_PER_ADDRESS) return false; // stochastic test: previous nRefCount == N: 2^N times harder to // increase it int nFactor = 1; for (int n = 0; n < pinfo->nRefCount; n++) nFactor *= 2; if (nFactor > 1 && (RandomInt(nFactor) != 0)) return false; } else { pinfo = Create(addr, source, &nId); pinfo->nTime = std::max((int64_t)0, (int64_t)pinfo->nTime - nTimePenalty); nNew++; fNew = true; } int nUBucket = pinfo->GetNewBucket(nKey, source); int nUBucketPos = pinfo->GetBucketPosition(nKey, true, nUBucket); if (vvNew[nUBucket][nUBucketPos] != nId) { bool fInsert = vvNew[nUBucket][nUBucketPos] == -1; if (!fInsert) { CAddrInfo &infoExisting = mapInfo[vvNew[nUBucket][nUBucketPos]]; if (infoExisting.IsTerrible() || (infoExisting.nRefCount > 1 && pinfo->nRefCount == 0)) { // Overwrite the existing new table entry. fInsert = true; } } if (fInsert) { ClearNew(nUBucket, nUBucketPos); pinfo->nRefCount++; vvNew[nUBucket][nUBucketPos] = nId; } else { if (pinfo->nRefCount == 0) { Delete(nId); } } } return fNew; } void CAddrMan::Attempt_(const CService &addr, bool fCountFailure, int64_t nTime) { CAddrInfo *pinfo = Find(addr); // if not found, bail out if (!pinfo) return; CAddrInfo &info = *pinfo; // check whether we are talking about the exact same CService (including // same port) if (info != addr) return; // update info info.nLastTry = nTime; if (fCountFailure && info.nLastCountAttempt < nLastGood) { info.nLastCountAttempt = nTime; info.nAttempts++; } } CAddrInfo CAddrMan::Select_(bool newOnly) { if (size() == 0) return CAddrInfo(); if (newOnly && nNew == 0) return CAddrInfo(); // Use a 50% chance for choosing between tried and new table entries. if (!newOnly && (nTried > 0 && (nNew == 0 || RandomInt(2) == 0))) { // use a tried node double fChanceFactor = 1.0; while (1) { int nKBucket = RandomInt(ADDRMAN_TRIED_BUCKET_COUNT); int nKBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE); while (vvTried[nKBucket][nKBucketPos] == -1) { - nKBucket = (nKBucket + insecure_rand.rand32()) % - ADDRMAN_TRIED_BUCKET_COUNT; - nKBucketPos = (nKBucketPos + insecure_rand.rand32()) % - ADDRMAN_BUCKET_SIZE; + nKBucket = + (nKBucket + + insecure_rand.randbits(ADDRMAN_TRIED_BUCKET_COUNT_LOG2)) % + ADDRMAN_TRIED_BUCKET_COUNT; + nKBucketPos = + (nKBucketPos + + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % + ADDRMAN_BUCKET_SIZE; } int nId = vvTried[nKBucket][nKBucketPos]; assert(mapInfo.count(nId) == 1); CAddrInfo &info = mapInfo[nId]; if (RandomInt(1 << 30) < - fChanceFactor * info.GetChance() * (1 << 30)) + fChanceFactor * info.GetChance() * (1 << 30)) { return info; + } fChanceFactor *= 1.2; } } else { // use a new node double fChanceFactor = 1.0; while (1) { int nUBucket = RandomInt(ADDRMAN_NEW_BUCKET_COUNT); int nUBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE); while (vvNew[nUBucket][nUBucketPos] == -1) { - nUBucket = (nUBucket + insecure_rand.rand32()) % - ADDRMAN_NEW_BUCKET_COUNT; - nUBucketPos = (nUBucketPos + insecure_rand.rand32()) % - ADDRMAN_BUCKET_SIZE; + nUBucket = + (nUBucket + + insecure_rand.randbits(ADDRMAN_NEW_BUCKET_COUNT_LOG2)) % + ADDRMAN_NEW_BUCKET_COUNT; + nUBucketPos = + (nUBucketPos + + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % + ADDRMAN_BUCKET_SIZE; } int nId = vvNew[nUBucket][nUBucketPos]; assert(mapInfo.count(nId) == 1); CAddrInfo &info = mapInfo[nId]; if (RandomInt(1 << 30) < fChanceFactor * info.GetChance() * (1 << 30)) return info; fChanceFactor *= 1.2; } } } #ifdef DEBUG_ADDRMAN int CAddrMan::Check_() { std::set setTried; std::map mapNew; if (vRandom.size() != nTried + nNew) return -7; for (std::map::iterator it = mapInfo.begin(); it != mapInfo.end(); it++) { int n = (*it).first; CAddrInfo &info = (*it).second; if (info.fInTried) { if (!info.nLastSuccess) return -1; if (info.nRefCount) return -2; setTried.insert(n); } else { if (info.nRefCount < 0 || info.nRefCount > ADDRMAN_NEW_BUCKETS_PER_ADDRESS) return -3; if (!info.nRefCount) return -4; mapNew[n] = info.nRefCount; } if (mapAddr[info] != n) return -5; if (info.nRandomPos < 0 || info.nRandomPos >= vRandom.size() || vRandom[info.nRandomPos] != n) return -14; if (info.nLastTry < 0) return -6; if (info.nLastSuccess < 0) return -8; } if (setTried.size() != nTried) return -9; if (mapNew.size() != nNew) return -10; for (int n = 0; n < ADDRMAN_TRIED_BUCKET_COUNT; n++) { for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) { if (vvTried[n][i] != -1) { if (!setTried.count(vvTried[n][i])) return -11; if (mapInfo[vvTried[n][i]].GetTriedBucket(nKey) != n) return -17; if (mapInfo[vvTried[n][i]].GetBucketPosition(nKey, false, n) != i) return -18; setTried.erase(vvTried[n][i]); } } } for (int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) { for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) { if (vvNew[n][i] != -1) { if (!mapNew.count(vvNew[n][i])) return -12; if (mapInfo[vvNew[n][i]].GetBucketPosition(nKey, true, n) != i) return -19; if (--mapNew[vvNew[n][i]] == 0) mapNew.erase(vvNew[n][i]); } } } if (setTried.size()) return -13; if (mapNew.size()) return -15; if (nKey.IsNull()) return -16; return 0; } #endif void CAddrMan::GetAddr_(std::vector &vAddr) { unsigned int nNodes = ADDRMAN_GETADDR_MAX_PCT * vRandom.size() / 100; if (nNodes > ADDRMAN_GETADDR_MAX) nNodes = ADDRMAN_GETADDR_MAX; // gather a list of random nodes, skipping those of low quality for (unsigned int n = 0; n < vRandom.size(); n++) { if (vAddr.size() >= nNodes) break; int nRndPos = RandomInt(vRandom.size() - n) + n; SwapRandom(n, nRndPos); assert(mapInfo.count(vRandom[n]) == 1); const CAddrInfo &ai = mapInfo[vRandom[n]]; if (!ai.IsTerrible()) vAddr.push_back(ai); } } void CAddrMan::Connected_(const CService &addr, int64_t nTime) { CAddrInfo *pinfo = Find(addr); // if not found, bail out if (!pinfo) return; CAddrInfo &info = *pinfo; // check whether we are talking about the exact same CService (including // same port) if (info != addr) return; // update info int64_t nUpdateInterval = 20 * 60; if (nTime - info.nTime > nUpdateInterval) info.nTime = nTime; } void CAddrMan::SetServices_(const CService &addr, ServiceFlags nServices) { CAddrInfo *pinfo = Find(addr); // if not found, bail out if (!pinfo) return; CAddrInfo &info = *pinfo; // check whether we are talking about the exact same CService (including // same port) if (info != addr) return; // update info info.nServices = nServices; } int CAddrMan::RandomInt(int nMax) { return GetRandInt(nMax); } diff --git a/src/addrman.h b/src/addrman.h index 4c82db130..5f847bca7 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -1,612 +1,617 @@ // Copyright (c) 2012 Pieter Wuille // Copyright (c) 2012-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. #ifndef BITCOIN_ADDRMAN_H #define BITCOIN_ADDRMAN_H #include "netaddress.h" #include "protocol.h" #include "random.h" #include "sync.h" #include "timedata.h" #include "util.h" #include #include #include #include /** * Extended statistics about a CAddress */ class CAddrInfo : public CAddress { public: //! last try whatsoever by us (memory only) int64_t nLastTry; //! last counted attempt (memory only) int64_t nLastCountAttempt; private: //! where knowledge about this address first came from CNetAddr source; //! last successful connection by us int64_t nLastSuccess; //! connection attempts since last successful attempt int nAttempts; //! reference count in new sets (memory only) int nRefCount; //! in tried set? (memory only) bool fInTried; //! position in vRandom int nRandomPos; friend class CAddrMan; public: ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream &s, Operation ser_action) { READWRITE(*(CAddress *)this); READWRITE(source); READWRITE(nLastSuccess); READWRITE(nAttempts); } void Init() { nLastSuccess = 0; nLastTry = 0; nLastCountAttempt = 0; nAttempts = 0; nRefCount = 0; fInTried = false; nRandomPos = -1; } CAddrInfo(const CAddress &addrIn, const CNetAddr &addrSource) : CAddress(addrIn), source(addrSource) { Init(); } CAddrInfo() : CAddress(), source() { Init(); } //! Calculate in which "tried" bucket this entry belongs int GetTriedBucket(const uint256 &nKey) const; //! Calculate in which "new" bucket this entry belongs, given a certain //! source int GetNewBucket(const uint256 &nKey, const CNetAddr &src) const; //! Calculate in which "new" bucket this entry belongs, using its default //! source int GetNewBucket(const uint256 &nKey) const { return GetNewBucket(nKey, source); } //! Calculate in which position of a bucket to store this entry. int GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const; //! Determine whether the statistics about this entry are bad enough so that //! it can just be deleted bool IsTerrible(int64_t nNow = GetAdjustedTime()) const; //! Calculate the relative chance this entry should be given when selecting //! nodes to connect to double GetChance(int64_t nNow = GetAdjustedTime()) const; }; /** Stochastic address manager * * Design goals: * * Keep the address tables in-memory, and asynchronously dump the entire * table to peers.dat. * * Make sure no (localized) attacker can fill the entire table with his * nodes/addresses. * * To that end: * * Addresses are organized into buckets. * * Addresses that have not yet been tried go into 1024 "new" buckets. * * Based on the address range (/16 for IPv4) of the source of * information, 64 buckets are selected at random. * * The actual bucket is chosen from one of these, based on the range in * which the address itself is located. * * One single address can occur in up to 8 different buckets to increase * selection chances for addresses that * are seen frequently. The chance for increasing this multiplicity * decreases exponentially. * * When adding a new address to a full bucket, a randomly chosen entry * (with a bias favoring less recently seen * ones) is removed from it first. * * Addresses of nodes that are known to be accessible go into 256 "tried" * buckets. * * Each address range selects at random 8 of these buckets. * * The actual bucket is chosen from one of these, based on the full * address. * * When adding a new good address to a full bucket, a randomly chosen * entry (with a bias favoring less recently * tried ones) is evicted from it, back to the "new" buckets. * * Bucket selection is based on cryptographic hashing, using a * randomly-generated 256-bit key, which should not * be observable by adversaries. * * Several indexes are kept for high performance. Defining DEBUG_ADDRMAN * will introduce frequent (and expensive) * consistency checks for the entire data structure. */ //! total number of buckets for tried addresses -#define ADDRMAN_TRIED_BUCKET_COUNT 256 +#define ADDRMAN_TRIED_BUCKET_COUNT_LOG2 8 //! total number of buckets for new addresses -#define ADDRMAN_NEW_BUCKET_COUNT 1024 +#define ADDRMAN_NEW_BUCKET_COUNT_LOG2 10 //! maximum allowed number of entries in buckets for new and tried addresses -#define ADDRMAN_BUCKET_SIZE 64 +#define ADDRMAN_BUCKET_SIZE_LOG2 6 //! over how many buckets entries with tried addresses from a single group (/16 //! for IPv4) are spread #define ADDRMAN_TRIED_BUCKETS_PER_GROUP 8 //! over how many buckets entries with new addresses originating from a single //! group are spread #define ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP 64 //! in how many buckets for entries with new addresses a single address may //! occur #define ADDRMAN_NEW_BUCKETS_PER_ADDRESS 8 //! how old addresses can maximally be #define ADDRMAN_HORIZON_DAYS 30 //! after how many failed attempts we give up on a new node #define ADDRMAN_RETRIES 3 //! how many successive failures are allowed ... #define ADDRMAN_MAX_FAILURES 10 //! ... in at least this many days #define ADDRMAN_MIN_FAIL_DAYS 7 //! the maximum percentage of nodes to return in a getaddr call #define ADDRMAN_GETADDR_MAX_PCT 23 //! the maximum number of nodes to return in a getaddr call #define ADDRMAN_GETADDR_MAX 2500 +//! Convenience +#define ADDRMAN_TRIED_BUCKET_COUNT (1 << ADDRMAN_TRIED_BUCKET_COUNT_LOG2) +#define ADDRMAN_NEW_BUCKET_COUNT (1 << ADDRMAN_NEW_BUCKET_COUNT_LOG2) +#define ADDRMAN_BUCKET_SIZE (1 << ADDRMAN_BUCKET_SIZE_LOG2) + /** * Stochastical (IP) address manager */ class CAddrMan { private: //! critical section to protect the inner data structures mutable CCriticalSection cs; //! last used nId int nIdCount; //! table with information about all nIds std::map mapInfo; //! find an nId based on its network address std::map mapAddr; //! randomly-ordered vector of all nIds std::vector vRandom; // number of "tried" entries int nTried; //! list of "tried" buckets int vvTried[ADDRMAN_TRIED_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE]; //! number of (unique) "new" entries int nNew; //! list of "new" buckets int vvNew[ADDRMAN_NEW_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE]; //! last time Good was called (memory only) int64_t nLastGood; protected: //! secret key to randomize bucket select with uint256 nKey; //! Source of random numbers for randomization in inner loops FastRandomContext insecure_rand; //! Find an entry. CAddrInfo *Find(const CNetAddr &addr, int *pnId = nullptr); //! find an entry, creating it if necessary. //! nTime and nServices of the found node are updated, if necessary. CAddrInfo *Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = nullptr); //! Swap two elements in vRandom. void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2); //! Move an entry from the "new" table(s) to the "tried" table void MakeTried(CAddrInfo &info, int nId); //! Delete an entry. It must not be in tried, and have refcount 0. void Delete(int nId); //! Clear a position in a "new" table. This is the only place where entries //! are actually deleted. void ClearNew(int nUBucket, int nUBucketPos); //! Mark an entry "good", possibly moving it from "new" to "tried". void Good_(const CService &addr, int64_t nTime); //! Add an entry to the "new" table. bool Add_(const CAddress &addr, const CNetAddr &source, int64_t nTimePenalty); //! Mark an entry as attempted to connect. void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime); //! Select an address to connect to, if newOnly is set to true, only the new //! table is selected from. CAddrInfo Select_(bool newOnly); //! Wraps GetRandInt to allow tests to override RandomInt and make it //! determinismistic. virtual int RandomInt(int nMax); #ifdef DEBUG_ADDRMAN //! Perform consistency check. Returns an error code or zero. int Check_(); #endif //! Select several addresses at once. void GetAddr_(std::vector &vAddr); //! Mark an entry as currently-connected-to. void Connected_(const CService &addr, int64_t nTime); //! Update an entry's service bits. void SetServices_(const CService &addr, ServiceFlags nServices); public: /** * serialized format: * * version byte (currently 1) * * 0x20 + nKey (serialized as if it were a vector, for backward * compatibility) * * nNew * * nTried * * number of "new" buckets XOR 2**30 * * all nNew addrinfos in vvNew * * all nTried addrinfos in vvTried * * for each bucket: * * number of elements * * for each element: index * * 2**30 is xorred with the number of buckets to make addrman deserializer * v0 detect it as incompatible. This is necessary because it did not check * the version number on deserialization. * * Notice that vvTried, mapAddr and vVector are never encoded explicitly; * they are instead reconstructed from the other information. * * vvNew is serialized, but only used if ADDRMAN_UNKNOWN_BUCKET_COUNT didn't * change, otherwise it is reconstructed as well. * * This format is more complex, but significantly smaller (at most 1.5 MiB), * and supports changes to the ADDRMAN_ parameters without breaking the * on-disk structure. * * We don't use ADD_SERIALIZE_METHODS since the serialization and * deserialization code has very little in common. */ template void Serialize(Stream &s) const { LOCK(cs); uint8_t nVersion = 1; s << nVersion; s << uint8_t(32); s << nKey; s << nNew; s << nTried; int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30); s << nUBuckets; std::map mapUnkIds; int nIds = 0; for (std::map::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) { mapUnkIds[(*it).first] = nIds; const CAddrInfo &info = (*it).second; if (info.nRefCount) { // this means nNew was wrong, oh ow assert(nIds != nNew); s << info; nIds++; } } nIds = 0; for (std::map::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) { const CAddrInfo &info = (*it).second; if (info.fInTried) { // this means nTried was wrong, oh ow assert(nIds != nTried); s << info; nIds++; } } for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) { int nSize = 0; for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) { if (vvNew[bucket][i] != -1) nSize++; } s << nSize; for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) { if (vvNew[bucket][i] != -1) { int nIndex = mapUnkIds[vvNew[bucket][i]]; s << nIndex; } } } } template void Unserialize(Stream &s) { LOCK(cs); Clear(); uint8_t nVersion; s >> nVersion; uint8_t nKeySize; s >> nKeySize; if (nKeySize != 32) throw std::ios_base::failure( "Incorrect keysize in addrman deserialization"); s >> nKey; s >> nNew; s >> nTried; int nUBuckets = 0; s >> nUBuckets; if (nVersion != 0) { nUBuckets ^= (1 << 30); } if (nNew > ADDRMAN_NEW_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE) { throw std::ios_base::failure( "Corrupt CAddrMan serialization, nNew exceeds limit."); } if (nTried > ADDRMAN_TRIED_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE) { throw std::ios_base::failure( "Corrupt CAddrMan serialization, nTried exceeds limit."); } // Deserialize entries from the new table. for (int n = 0; n < nNew; n++) { CAddrInfo &info = mapInfo[n]; s >> info; mapAddr[info] = n; info.nRandomPos = vRandom.size(); vRandom.push_back(n); if (nVersion != 1 || nUBuckets != ADDRMAN_NEW_BUCKET_COUNT) { // In case the new table data cannot be used (nVersion unknown, // or bucket count wrong), immediately try to give them a // reference based on their primary source address. int nUBucket = info.GetNewBucket(nKey); int nUBucketPos = info.GetBucketPosition(nKey, true, nUBucket); if (vvNew[nUBucket][nUBucketPos] == -1) { vvNew[nUBucket][nUBucketPos] = n; info.nRefCount++; } } } nIdCount = nNew; // Deserialize entries from the tried table. int nLost = 0; for (int n = 0; n < nTried; n++) { CAddrInfo info; s >> info; int nKBucket = info.GetTriedBucket(nKey); int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket); if (vvTried[nKBucket][nKBucketPos] == -1) { info.nRandomPos = vRandom.size(); info.fInTried = true; vRandom.push_back(nIdCount); mapInfo[nIdCount] = info; mapAddr[info] = nIdCount; vvTried[nKBucket][nKBucketPos] = nIdCount; nIdCount++; } else { nLost++; } } nTried -= nLost; // Deserialize positions in the new table (if possible). for (int bucket = 0; bucket < nUBuckets; bucket++) { int nSize = 0; s >> nSize; for (int n = 0; n < nSize; n++) { int nIndex = 0; s >> nIndex; if (nIndex >= 0 && nIndex < nNew) { CAddrInfo &info = mapInfo[nIndex]; int nUBucketPos = info.GetBucketPosition(nKey, true, bucket); if (nVersion == 1 && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 && info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS) { info.nRefCount++; vvNew[bucket][nUBucketPos] = nIndex; } } } } // Prune new entries with refcount 0 (as a result of collisions). int nLostUnk = 0; for (std::map::const_iterator it = mapInfo.begin(); it != mapInfo.end();) { if (it->second.fInTried == false && it->second.nRefCount == 0) { std::map::const_iterator itCopy = it++; Delete(itCopy->first); nLostUnk++; } else { it++; } } if (nLost + nLostUnk > 0) { LogPrint("addrman", "addrman lost %i new and %i tried addresses " "due to collisions\n", nLostUnk, nLost); } Check(); } void Clear() { std::vector().swap(vRandom); nKey = GetRandHash(); for (size_t bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) { for (size_t entry = 0; entry < ADDRMAN_BUCKET_SIZE; entry++) { vvNew[bucket][entry] = -1; } } for (size_t bucket = 0; bucket < ADDRMAN_TRIED_BUCKET_COUNT; bucket++) { for (size_t entry = 0; entry < ADDRMAN_BUCKET_SIZE; entry++) { vvTried[bucket][entry] = -1; } } nIdCount = 0; nTried = 0; nNew = 0; // Initially at 1 so that "never" is strictly worse. nLastGood = 1; } CAddrMan() { Clear(); } ~CAddrMan() { nKey.SetNull(); } //! Return the number of (unique) addresses in all tables. size_t size() const { // TODO: Cache this in an atomic to avoid this overhead LOCK(cs); return vRandom.size(); } //! Consistency check void Check() { #ifdef DEBUG_ADDRMAN { LOCK(cs); int err; if ((err = Check_())) LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err); } #endif } //! Add a single address. bool Add(const CAddress &addr, const CNetAddr &source, int64_t nTimePenalty = 0) { LOCK(cs); bool fRet = false; Check(); fRet |= Add_(addr, source, nTimePenalty); Check(); if (fRet) LogPrint("addrman", "Added %s from %s: %i tried, %i new\n", addr.ToStringIPPort(), source.ToString(), nTried, nNew); return fRet; } //! Add multiple addresses. bool Add(const std::vector &vAddr, const CNetAddr &source, int64_t nTimePenalty = 0) { LOCK(cs); int nAdd = 0; Check(); for (std::vector::const_iterator it = vAddr.begin(); it != vAddr.end(); it++) nAdd += Add_(*it, source, nTimePenalty) ? 1 : 0; Check(); if (nAdd) LogPrint("addrman", "Added %i addresses from %s: %i tried, %i new\n", nAdd, source.ToString(), nTried, nNew); return nAdd > 0; } //! Mark an entry as accessible. void Good(const CService &addr, int64_t nTime = GetAdjustedTime()) { LOCK(cs); Check(); Good_(addr, nTime); Check(); } //! Mark an entry as connection attempted to. void Attempt(const CService &addr, bool fCountFailure, int64_t nTime = GetAdjustedTime()) { LOCK(cs); Check(); Attempt_(addr, fCountFailure, nTime); Check(); } /** * Choose an address to connect to. */ CAddrInfo Select(bool newOnly = false) { CAddrInfo addrRet; { LOCK(cs); Check(); addrRet = Select_(newOnly); Check(); } return addrRet; } //! Return a bunch of addresses, selected at random. std::vector GetAddr() { Check(); std::vector vAddr; { LOCK(cs); GetAddr_(vAddr); } Check(); return vAddr; } //! Mark an entry as currently-connected-to. void Connected(const CService &addr, int64_t nTime = GetAdjustedTime()) { LOCK(cs); Check(); Connected_(addr, nTime); Check(); } void SetServices(const CService &addr, ServiceFlags nServices) { LOCK(cs); Check(); SetServices_(addr, nServices); Check(); } }; #endif // BITCOIN_ADDRMAN_H diff --git a/src/bench/checkqueue.cpp b/src/bench/checkqueue.cpp index e4b4f9d86..608e23227 100644 --- a/src/bench/checkqueue.cpp +++ b/src/bench/checkqueue.cpp @@ -1,94 +1,94 @@ // Copyright (c) 2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "checkqueue.h" #include "bench.h" #include "prevector.h" #include "random.h" #include "util.h" #include "validation.h" #include #include // This Benchmark tests the CheckQueue with the lightest weight Checks, so it // should make any lock contention particularly visible static const int MIN_CORES = 2; static const size_t BATCHES = 101; static const size_t BATCH_SIZE = 30; static const int PREVECTOR_SIZE = 28; static const int QUEUE_BATCH_SIZE = 128; static void CCheckQueueSpeed(benchmark::State &state) { struct FakeJobNoWork { bool operator()() { return true; } void swap(FakeJobNoWork &x){}; }; CCheckQueue queue{QUEUE_BATCH_SIZE}; boost::thread_group tg; for (auto x = 0; x < std::max(MIN_CORES, GetNumCores()); ++x) { tg.create_thread([&] { queue.Thread(); }); } while (state.KeepRunning()) { CCheckQueueControl control(&queue); // We call Add a number of times to simulate the behavior of adding a // block of transactions at once. std::vector> vBatches(BATCHES); for (auto &vChecks : vBatches) { vChecks.resize(BATCH_SIZE); } for (auto &vChecks : vBatches) { // We can't make vChecks in the inner loop because we want to // measure the cost of getting the memory to each thread and we // might get the same memory control.Add(vChecks); } // control waits for completion by RAII, but it is done explicitly here // for clarity control.Wait(); } tg.interrupt_all(); tg.join_all(); } // This Benchmark tests the CheckQueue with a slightly realistic workload, where // checks all contain a prevector that is indirect 50% of the time and there is // a little bit of work done between calls to Add. static void CCheckQueueSpeedPrevectorJob(benchmark::State &state) { struct PrevectorJob { prevector p; PrevectorJob() {} PrevectorJob(FastRandomContext &insecure_rand) { - p.resize(insecure_rand.rand32() % (PREVECTOR_SIZE * 2)); + p.resize(insecure_rand.randrange(PREVECTOR_SIZE * 2)); } bool operator()() { return true; } void swap(PrevectorJob &x) { p.swap(x.p); }; }; CCheckQueue queue{QUEUE_BATCH_SIZE}; boost::thread_group tg; for (auto x = 0; x < std::max(MIN_CORES, GetNumCores()); ++x) { tg.create_thread([&] { queue.Thread(); }); } while (state.KeepRunning()) { // Make insecure_rand here so that each iteration is identical. FastRandomContext insecure_rand(true); CCheckQueueControl control(&queue); std::vector> vBatches(BATCHES); for (auto &vChecks : vBatches) { vChecks.reserve(BATCH_SIZE); for (size_t x = 0; x < BATCH_SIZE; ++x) vChecks.emplace_back(insecure_rand); control.Add(vChecks); } // control waits for completion by RAII, but it is done explicitly here // for clarity control.Wait(); } tg.interrupt_all(); tg.join_all(); } BENCHMARK(CCheckQueueSpeed); BENCHMARK(CCheckQueueSpeedPrevectorJob); diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp index 4e8a7d40e..ebafa2dfb 100644 --- a/src/bench/crypto_hash.cpp +++ b/src/bench/crypto_hash.cpp @@ -1,72 +1,95 @@ // 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. #include #include "bench.h" #include "bloom.h" #include "crypto/ripemd160.h" #include "crypto/sha1.h" #include "crypto/sha256.h" #include "crypto/sha512.h" #include "hash.h" +#include "random.h" #include "uint256.h" #include "utiltime.h" /* Number of bytes to hash per iteration */ static const uint64_t BUFFER_SIZE = 1000 * 1000; static void RIPEMD160(benchmark::State &state) { uint8_t hash[CRIPEMD160::OUTPUT_SIZE]; std::vector in(BUFFER_SIZE, 0); while (state.KeepRunning()) CRIPEMD160().Write(in.data(), in.size()).Finalize(hash); } static void SHA1(benchmark::State &state) { uint8_t hash[CSHA1::OUTPUT_SIZE]; std::vector in(BUFFER_SIZE, 0); while (state.KeepRunning()) CSHA1().Write(in.data(), in.size()).Finalize(hash); } static void SHA256(benchmark::State &state) { uint8_t hash[CSHA256::OUTPUT_SIZE]; std::vector in(BUFFER_SIZE, 0); while (state.KeepRunning()) CSHA256().Write(in.data(), in.size()).Finalize(hash); } static void SHA256_32b(benchmark::State &state) { std::vector in(32, 0); while (state.KeepRunning()) { for (int i = 0; i < 1000000; i++) { CSHA256().Write(in.data(), in.size()).Finalize(&in[0]); } } } static void SHA512(benchmark::State &state) { uint8_t hash[CSHA512::OUTPUT_SIZE]; std::vector in(BUFFER_SIZE, 0); while (state.KeepRunning()) CSHA512().Write(in.data(), in.size()).Finalize(hash); } static void SipHash_32b(benchmark::State &state) { uint256 x; while (state.KeepRunning()) { for (int i = 0; i < 1000000; i++) { *((uint64_t *)x.begin()) = SipHashUint256(0, i, x); } } } +static void FastRandom_32bit(benchmark::State &state) { + FastRandomContext rng(true); + uint32_t x; + while (state.KeepRunning()) { + for (int i = 0; i < 1000000; i++) { + x += rng.rand32(); + } + } +} + +static void FastRandom_1bit(benchmark::State &state) { + FastRandomContext rng(true); + uint32_t x; + while (state.KeepRunning()) { + for (int i = 0; i < 1000000; i++) { + x += rng.randbool(); + } + } +} + BENCHMARK(RIPEMD160); BENCHMARK(SHA1); BENCHMARK(SHA256); BENCHMARK(SHA512); BENCHMARK(SHA256_32b); BENCHMARK(SipHash_32b); +BENCHMARK(FastRandom_32bit); +BENCHMARK(FastRandom_1bit); diff --git a/src/crypto/chacha20.cpp b/src/crypto/chacha20.cpp new file mode 100644 index 000000000..7c175c6e1 --- /dev/null +++ b/src/crypto/chacha20.cpp @@ -0,0 +1,190 @@ +// Copyright (c) 2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// Based on the public domain implementation 'merged' by D. J. Bernstein +// See https://cr.yp.to/chacha.html. + +#include "crypto/chacha20.h" +#include "crypto/common.h" + +#include + +constexpr static inline uint32_t rotl32(uint32_t v, int c) { + return (v << c) | (v >> (32 - c)); +} + +#define QUARTERROUND(a, b, c, d) \ + a += b; \ + d = rotl32(d ^ a, 16); \ + c += d; \ + b = rotl32(b ^ c, 12); \ + a += b; \ + d = rotl32(d ^ a, 8); \ + c += d; \ + b = rotl32(b ^ c, 7); + +static const uint8_t sigma[] = "expand 32-byte k"; +static const uint8_t tau[] = "expand 16-byte k"; + +void ChaCha20::SetKey(const uint8_t *k, size_t keylen) { + const uint8_t *constants; + + input[4] = ReadLE32(k + 0); + input[5] = ReadLE32(k + 4); + input[6] = ReadLE32(k + 8); + input[7] = ReadLE32(k + 12); + if (keylen == 32) { + // recommended + k += 16; + constants = sigma; + } else { + // keylen == 16 + constants = tau; + } + input[8] = ReadLE32(k + 0); + input[9] = ReadLE32(k + 4); + input[10] = ReadLE32(k + 8); + input[11] = ReadLE32(k + 12); + input[0] = ReadLE32(constants + 0); + input[1] = ReadLE32(constants + 4); + input[2] = ReadLE32(constants + 8); + input[3] = ReadLE32(constants + 12); + input[12] = 0; + input[13] = 0; + input[14] = 0; + input[15] = 0; +} + +ChaCha20::ChaCha20() { + memset(input, 0, sizeof(input)); +} + +ChaCha20::ChaCha20(const uint8_t *k, size_t keylen) { + SetKey(k, keylen); +} + +void ChaCha20::SetIV(uint64_t iv) { + input[14] = iv; + input[15] = iv >> 32; +} + +void ChaCha20::Seek(uint64_t pos) { + input[12] = pos; + input[13] = pos >> 32; +} + +void ChaCha20::Output(uint8_t *c, size_t bytes) { + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, + x15; + uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, + j15; + uint8_t *ctarget = nullptr; + uint8_t tmp[64]; + unsigned int i; + + if (!bytes) { + return; + } + + j0 = input[0]; + j1 = input[1]; + j2 = input[2]; + j3 = input[3]; + j4 = input[4]; + j5 = input[5]; + j6 = input[6]; + j7 = input[7]; + j8 = input[8]; + j9 = input[9]; + j10 = input[10]; + j11 = input[11]; + j12 = input[12]; + j13 = input[13]; + j14 = input[14]; + j15 = input[15]; + + for (;;) { + if (bytes < 64) { + ctarget = c; + c = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for (i = 20; i > 0; i -= 2) { + QUARTERROUND(x0, x4, x8, x12) + QUARTERROUND(x1, x5, x9, x13) + QUARTERROUND(x2, x6, x10, x14) + QUARTERROUND(x3, x7, x11, x15) + QUARTERROUND(x0, x5, x10, x15) + QUARTERROUND(x1, x6, x11, x12) + QUARTERROUND(x2, x7, x8, x13) + QUARTERROUND(x3, x4, x9, x14) + } + x0 += j0; + x1 += j1; + x2 += j2; + x3 += j3; + x4 += j4; + x5 += j5; + x6 += j6; + x7 += j7; + x8 += j8; + x9 += j9; + x10 += j10; + x11 += j11; + x12 += j12; + x13 += j13; + x14 += j14; + x15 += j15; + + ++j12; + if (!j12) { + ++j13; + } + + WriteLE32(c + 0, x0); + WriteLE32(c + 4, x1); + WriteLE32(c + 8, x2); + WriteLE32(c + 12, x3); + WriteLE32(c + 16, x4); + WriteLE32(c + 20, x5); + WriteLE32(c + 24, x6); + WriteLE32(c + 28, x7); + WriteLE32(c + 32, x8); + WriteLE32(c + 36, x9); + WriteLE32(c + 40, x10); + WriteLE32(c + 44, x11); + WriteLE32(c + 48, x12); + WriteLE32(c + 52, x13); + WriteLE32(c + 56, x14); + WriteLE32(c + 60, x15); + + if (bytes <= 64) { + if (bytes < 64) { + for (i = 0; i < bytes; ++i) { + ctarget[i] = c[i]; + } + } + input[12] = j12; + input[13] = j13; + return; + } + bytes -= 64; + c += 64; + } +} diff --git a/src/crypto/chacha20.h b/src/crypto/chacha20.h new file mode 100644 index 000000000..9c0f1102a --- /dev/null +++ b/src/crypto/chacha20.h @@ -0,0 +1,25 @@ +// Copyright (c) 2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CRYPTO_CHACHA20_H +#define BITCOIN_CRYPTO_CHACHA20_H + +#include +#include + +/** A PRNG class for ChaCha20. */ +class ChaCha20 { +private: + uint32_t input[16]; + +public: + ChaCha20(); + ChaCha20(const uint8_t *key, size_t keylen); + void SetKey(const uint8_t *key, size_t keylen); + void SetIV(uint64_t iv); + void Seek(uint64_t pos); + void Output(uint8_t *output, size_t bytes); +}; + +#endif // BITCOIN_CRYPTO_CHACHA20_H diff --git a/src/crypto/common.h b/src/crypto/common.h index 07d103836..db8e6857b 100644 --- a/src/crypto/common.h +++ b/src/crypto/common.h @@ -1,72 +1,95 @@ // Copyright (c) 2014 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_CRYPTO_COMMON_H #define BITCOIN_CRYPTO_COMMON_H #if defined(HAVE_CONFIG_H) #include "bitcoin-config.h" #endif #include #include #include "compat/endian.h" static inline uint16_t ReadLE16(const uint8_t *ptr) { uint16_t x; memcpy((char *)&x, ptr, 2); return le16toh(x); } static inline uint32_t ReadLE32(const uint8_t *ptr) { uint32_t x; memcpy((char *)&x, ptr, 4); return le32toh(x); } static inline uint64_t ReadLE64(const uint8_t *ptr) { uint64_t x; memcpy((char *)&x, ptr, 8); return le64toh(x); } static inline void WriteLE16(uint8_t *ptr, uint16_t x) { uint16_t v = htole16(x); memcpy(ptr, (char *)&v, 2); } static inline void WriteLE32(uint8_t *ptr, uint32_t x) { uint32_t v = htole32(x); memcpy(ptr, (char *)&v, 4); } static inline void WriteLE64(uint8_t *ptr, uint64_t x) { uint64_t v = htole64(x); memcpy(ptr, (char *)&v, 8); } static inline uint32_t ReadBE32(const uint8_t *ptr) { uint32_t x; memcpy((char *)&x, ptr, 4); return be32toh(x); } static inline uint64_t ReadBE64(const uint8_t *ptr) { uint64_t x; memcpy((char *)&x, ptr, 8); return be64toh(x); } static inline void WriteBE32(uint8_t *ptr, uint32_t x) { uint32_t v = htobe32(x); memcpy(ptr, (char *)&v, 4); } static inline void WriteBE64(uint8_t *ptr, uint64_t x) { uint64_t v = htobe64(x); memcpy(ptr, (char *)&v, 8); } +/** + * Return the smallest number n such that (x >> n) == 0 (or 64 if the highest + * bit in x is set. + */ +uint64_t static inline CountBits(uint64_t x) { +#ifdef HAVE_DECL___BUILTIN_CLZL + if (sizeof(unsigned long) >= sizeof(uint64_t)) { + return x ? 8 * sizeof(unsigned long) - __builtin_clzl(x) : 0; + } +#endif +#ifdef HAVE_DECL___BUILTIN_CLZLL + if (sizeof(unsigned long long) >= sizeof(uint64_t)) { + return x ? 8 * sizeof(unsigned long long) - __builtin_clzll(x) : 0; + } +#endif + int ret = 0; + while (x) { + x >>= 1; + ++ret; + } + return ret; +} + #endif // BITCOIN_CRYPTO_COMMON_H diff --git a/src/net.h b/src/net.h index 8dec8d69a..61db159b1 100644 --- a/src/net.h +++ b/src/net.h @@ -1,831 +1,831 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Copyright (c) 2017 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_NET_H #define BITCOIN_NET_H #include "addrdb.h" #include "addrman.h" #include "amount.h" #include "bloom.h" #include "chainparams.h" #include "compat.h" #include "hash.h" #include "limitedmap.h" #include "netaddress.h" #include "protocol.h" #include "random.h" #include "streams.h" #include "sync.h" #include "threadinterrupt.h" #include "uint256.h" #include #include #include #include #include #include #ifndef WIN32 #include #endif #include #include class CAddrMan; class Config; class CNode; class CScheduler; namespace boost { class thread_group; } // namespace boost /** Time between pings automatically sent out for latency probing and keepalive * (in seconds). */ static const int PING_INTERVAL = 2 * 60; /** Time after which to disconnect, after waiting for a ping response (or * inactivity). */ static const int TIMEOUT_INTERVAL = 20 * 60; /** Run the feeler connection loop once every 2 minutes or 120 seconds. **/ static const int FEELER_INTERVAL = 120; /** The maximum number of entries in an 'inv' protocol message */ static const unsigned int MAX_INV_SZ = 50000; /** The maximum number of new addresses to accumulate before announcing. */ static const unsigned int MAX_ADDR_TO_SEND = 1000; /** Maximum length of incoming protocol messages (no message over 4 MB is * currently acceptable). */ static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 32 * 1000 * 1000; /** Maximum length of strSubVer in `version` message */ static const unsigned int MAX_SUBVERSION_LENGTH = 256; /** Maximum number of automatic outgoing nodes */ static const int MAX_OUTBOUND_CONNECTIONS = 8; /** Maximum number of addnode outgoing nodes */ static const int MAX_ADDNODE_CONNECTIONS = 8; /** -listen default */ static const bool DEFAULT_LISTEN = true; /** -upnp default */ #ifdef USE_UPNP static const bool DEFAULT_UPNP = USE_UPNP; #else static const bool DEFAULT_UPNP = false; #endif /** The maximum number of entries in mapAskFor */ static const size_t MAPASKFOR_MAX_SZ = MAX_INV_SZ; /** The maximum number of entries in setAskFor (larger due to getdata latency)*/ static const size_t SETASKFOR_MAX_SZ = 2 * MAX_INV_SZ; /** The maximum number of peer connections to maintain. */ static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125; /** The default for -maxuploadtarget. 0 = Unlimited */ static const uint64_t DEFAULT_MAX_UPLOAD_TARGET = 0; /** The default timeframe for -maxuploadtarget. 1 day. */ static const uint64_t MAX_UPLOAD_TIMEFRAME = 60 * 60 * 24; /** Default for blocks only*/ static const bool DEFAULT_BLOCKSONLY = false; // Force DNS seed use ahead of UAHF fork, to ensure peers are found // as long as seeders are working. // TODO: Change this back to false after the forked network is stable. static const bool DEFAULT_FORCEDNSSEED = true; static const size_t DEFAULT_MAXRECEIVEBUFFER = 5 * 1000; static const size_t DEFAULT_MAXSENDBUFFER = 1 * 1000; static const ServiceFlags REQUIRED_SERVICES = ServiceFlags(NODE_NETWORK | NODE_BITCOIN_CASH); // Default 24-hour ban. // NOTE: When adjusting this, update rpcnet:setban's help ("24h") static const unsigned int DEFAULT_MISBEHAVING_BANTIME = 60 * 60 * 24; typedef int64_t NodeId; struct AddedNodeInfo { std::string strAddedNode; CService resolvedAddress; bool fConnected; bool fInbound; }; class CTransaction; class CNodeStats; class CClientUIInterface; struct CSerializedNetMsg { CSerializedNetMsg() = default; CSerializedNetMsg(CSerializedNetMsg &&) = default; CSerializedNetMsg &operator=(CSerializedNetMsg &&) = default; // No copying, only moves. CSerializedNetMsg(const CSerializedNetMsg &msg) = delete; CSerializedNetMsg &operator=(const CSerializedNetMsg &) = delete; std::vector data; std::string command; }; class CConnman { public: enum NumConnections { CONNECTIONS_NONE = 0, CONNECTIONS_IN = (1U << 0), CONNECTIONS_OUT = (1U << 1), CONNECTIONS_ALL = (CONNECTIONS_IN | CONNECTIONS_OUT), }; struct Options { ServiceFlags nLocalServices = NODE_NONE; ServiceFlags nRelevantServices = NODE_NONE; int nMaxConnections = 0; int nMaxOutbound = 0; int nMaxAddnode = 0; int nMaxFeeler = 0; int nBestHeight = 0; CClientUIInterface *uiInterface = nullptr; unsigned int nSendBufferMaxSize = 0; unsigned int nReceiveFloodSize = 0; uint64_t nMaxOutboundTimeframe = 0; uint64_t nMaxOutboundLimit = 0; }; CConnman(const Config &configIn, uint64_t seed0, uint64_t seed1); ~CConnman(); bool Start(CScheduler &scheduler, std::string &strNodeError, Options options); void Stop(); void Interrupt(); bool BindListenPort(const CService &bindAddr, std::string &strError, bool fWhitelisted = false); bool GetNetworkActive() const { return fNetworkActive; }; void SetNetworkActive(bool active); bool OpenNetworkConnection(const CAddress &addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, bool fOneShot = false, bool fFeeler = false, bool fAddnode = false); bool CheckIncomingNonce(uint64_t nonce); bool ForNode(NodeId id, std::function func); void PushMessage(CNode *pnode, CSerializedNetMsg &&msg); template void ForEachNode(Callable &&func) { LOCK(cs_vNodes); for (auto &&node : vNodes) { if (NodeFullyConnected(node)) func(node); } }; template void ForEachNode(Callable &&func) const { LOCK(cs_vNodes); for (auto &&node : vNodes) { if (NodeFullyConnected(node)) func(node); } }; template void ForEachNodeThen(Callable &&pre, CallableAfter &&post) { LOCK(cs_vNodes); for (auto &&node : vNodes) { if (NodeFullyConnected(node)) pre(node); } post(); }; template void ForEachNodeThen(Callable &&pre, CallableAfter &&post) const { LOCK(cs_vNodes); for (auto &&node : vNodes) { if (NodeFullyConnected(node)) pre(node); } post(); }; // Addrman functions size_t GetAddressCount() const; void SetServices(const CService &addr, ServiceFlags nServices); void MarkAddressGood(const CAddress &addr); void AddNewAddress(const CAddress &addr, const CAddress &addrFrom, int64_t nTimePenalty = 0); void AddNewAddresses(const std::vector &vAddr, const CAddress &addrFrom, int64_t nTimePenalty = 0); std::vector GetAddresses(); void AddressCurrentlyConnected(const CService &addr); // Denial-of-service detection/prevention. The idea is to detect peers that // are behaving badly and disconnect/ban them, but do it in a // one-coding-mistake-won't-shatter-the-entire-network way. // IMPORTANT: There should be nothing I can give a node that it will forward // on that will make that node's peers drop it. If there is, an attacker can // isolate a node and/or try to split the network. Dropping a node for // sending stuff that is invalid now but might be valid in a later version // is also dangerous, because it can cause a network split between nodes // running old code and nodes running new code. void Ban(const CNetAddr &netAddr, const BanReason &reason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); void Ban(const CSubNet &subNet, const BanReason &reason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); // Needed for unit testing. void ClearBanned(); bool IsBanned(CNetAddr ip); bool IsBanned(CSubNet subnet); bool Unban(const CNetAddr &ip); bool Unban(const CSubNet &ip); void GetBanned(banmap_t &banmap); void SetBanned(const banmap_t &banmap); void AddOneShot(const std::string &strDest); bool AddNode(const std::string &node); bool RemoveAddedNode(const std::string &node); std::vector GetAddedNodeInfo(); size_t GetNodeCount(NumConnections num); void GetNodeStats(std::vector &vstats); bool DisconnectNode(const std::string &node); bool DisconnectNode(NodeId id); unsigned int GetSendBufferSize() const; void AddWhitelistedRange(const CSubNet &subnet); ServiceFlags GetLocalServices() const; //! set the max outbound target in bytes. void SetMaxOutboundTarget(uint64_t limit); uint64_t GetMaxOutboundTarget(); //! set the timeframe for the max outbound target. void SetMaxOutboundTimeframe(uint64_t timeframe); uint64_t GetMaxOutboundTimeframe(); //! check if the outbound target is reached. // If param historicalBlockServingLimit is set true, the function will // response true if the limit for serving historical blocks has been // reached. bool OutboundTargetReached(bool historicalBlockServingLimit); //! response the bytes left in the current max outbound cycle // in case of no limit, it will always response 0 uint64_t GetOutboundTargetBytesLeft(); //! response the time in second left in the current max outbound cycle // in case of no limit, it will always response 0 uint64_t GetMaxOutboundTimeLeftInCycle(); uint64_t GetTotalBytesRecv(); uint64_t GetTotalBytesSent(); void SetBestHeight(int height); int GetBestHeight() const; /** Get a unique deterministic randomizer. */ CSipHasher GetDeterministicRandomizer(uint64_t id) const; unsigned int GetReceiveFloodSize() const; void WakeMessageHandler(); private: struct ListenSocket { SOCKET socket; bool whitelisted; ListenSocket(SOCKET socket_, bool whitelisted_) : socket(socket_), whitelisted(whitelisted_) {} }; void ThreadOpenAddedConnections(); void ProcessOneShot(); void ThreadOpenConnections(); void ThreadMessageHandler(); void AcceptConnection(const ListenSocket &hListenSocket); void ThreadSocketHandler(); void ThreadDNSAddressSeed(); uint64_t CalculateKeyedNetGroup(const CAddress &ad) const; CNode *FindNode(const CNetAddr &ip); CNode *FindNode(const CSubNet &subNet); CNode *FindNode(const std::string &addrName); CNode *FindNode(const CService &addr); bool AttemptToEvictConnection(); CNode *ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure); bool IsWhitelistedRange(const CNetAddr &addr); void DeleteNode(CNode *pnode); NodeId GetNewNodeId(); size_t SocketSendData(CNode *pnode) const; //! check is the banlist has unwritten changes bool BannedSetIsDirty(); //! set the "dirty" flag for the banlist void SetBannedSetDirty(bool dirty = true); //! clean unused entries (if bantime has expired) void SweepBanned(); void DumpAddresses(); void DumpData(); void DumpBanlist(); // Network stats void RecordBytesRecv(uint64_t bytes); void RecordBytesSent(uint64_t bytes); // Whether the node should be passed out in ForEach* callbacks static bool NodeFullyConnected(const CNode *pnode); const Config *config; // Network usage totals CCriticalSection cs_totalBytesRecv; CCriticalSection cs_totalBytesSent; uint64_t nTotalBytesRecv; uint64_t nTotalBytesSent; // outbound limit & stats uint64_t nMaxOutboundTotalBytesSentInCycle; uint64_t nMaxOutboundCycleStartTime; uint64_t nMaxOutboundLimit; uint64_t nMaxOutboundTimeframe; // Whitelisted ranges. Any node connecting from these is automatically // whitelisted (as well as those connecting to whitelisted binds). std::vector vWhitelistedRange; CCriticalSection cs_vWhitelistedRange; unsigned int nSendBufferMaxSize; unsigned int nReceiveFloodSize; std::vector vhListenSocket; std::atomic fNetworkActive; banmap_t setBanned; CCriticalSection cs_setBanned; bool setBannedIsDirty; bool fAddressesInitialized; CAddrMan addrman; std::deque vOneShots; CCriticalSection cs_vOneShots; std::vector vAddedNodes; CCriticalSection cs_vAddedNodes; std::vector vNodes; std::list vNodesDisconnected; mutable CCriticalSection cs_vNodes; std::atomic nLastNodeId; /** Services this instance offers */ ServiceFlags nLocalServices; /** Services this instance cares about */ ServiceFlags nRelevantServices; CSemaphore *semOutbound; CSemaphore *semAddnode; int nMaxConnections; int nMaxOutbound; int nMaxAddnode; int nMaxFeeler; std::atomic nBestHeight; CClientUIInterface *clientInterface; /** SipHasher seeds for deterministic randomness */ const uint64_t nSeed0, nSeed1; /** flag for waking the message processor. */ bool fMsgProcWake; std::condition_variable condMsgProc; std::mutex mutexMsgProc; std::atomic flagInterruptMsgProc; CThreadInterrupt interruptNet; std::thread threadDNSAddressSeed; std::thread threadSocketHandler; std::thread threadOpenAddedConnections; std::thread threadOpenConnections; std::thread threadMessageHandler; }; extern std::unique_ptr g_connman; void Discover(boost::thread_group &threadGroup); void MapPort(bool fUseUPnP); unsigned short GetListenPort(); bool BindListenPort(const CService &bindAddr, std::string &strError, bool fWhitelisted = false); struct CombinerAll { typedef bool result_type; template bool operator()(I first, I last) const { while (first != last) { if (!(*first)) { return false; } ++first; } return true; } }; // Signals for message handling struct CNodeSignals { boost::signals2::signal &), CombinerAll> ProcessMessages; boost::signals2::signal &), CombinerAll> SendMessages; boost::signals2::signal InitializeNode; boost::signals2::signal FinalizeNode; }; CNodeSignals &GetNodeSignals(); enum { // unknown LOCAL_NONE, // address a local interface listens on LOCAL_IF, // address explicit bound to LOCAL_BIND, // address reported by UPnP LOCAL_UPNP, // address explicitly specified (-externalip=) LOCAL_MANUAL, LOCAL_MAX }; bool IsPeerAddrLocalGood(CNode *pnode); void AdvertiseLocal(CNode *pnode); void SetLimited(enum Network net, bool fLimited = true); bool IsLimited(enum Network net); bool IsLimited(const CNetAddr &addr); bool AddLocal(const CService &addr, int nScore = LOCAL_NONE); bool AddLocal(const CNetAddr &addr, int nScore = LOCAL_NONE); bool RemoveLocal(const CService &addr); bool SeenLocal(const CService &addr); bool IsLocal(const CService &addr); bool GetLocal(CService &addr, const CNetAddr *paddrPeer = nullptr); bool IsReachable(enum Network net); bool IsReachable(const CNetAddr &addr); CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices); extern bool fDiscover; extern bool fListen; extern bool fRelayTxes; extern limitedmap mapAlreadyAskedFor; struct LocalServiceInfo { int nScore; int nPort; }; extern CCriticalSection cs_mapLocalHost; extern std::map mapLocalHost; // Command, total bytes typedef std::map mapMsgCmdSize; class CNodeStats { public: NodeId nodeid; ServiceFlags nServices; bool fRelayTxes; int64_t nLastSend; int64_t nLastRecv; int64_t nTimeConnected; int64_t nTimeOffset; std::string addrName; int nVersion; std::string cleanSubVer; bool fInbound; bool fAddnode; int nStartingHeight; uint64_t nSendBytes; mapMsgCmdSize mapSendBytesPerMsgCmd; uint64_t nRecvBytes; mapMsgCmdSize mapRecvBytesPerMsgCmd; bool fWhitelisted; double dPingTime; double dPingWait; double dMinPing; std::string addrLocal; CAddress addr; }; class CNetMessage { private: mutable CHash256 hasher; mutable uint256 data_hash; public: // Parsing header (false) or data (true) bool in_data; // Partially received header. CDataStream hdrbuf; // Complete header. CMessageHeader hdr; unsigned int nHdrPos; // Received message data. CDataStream vRecv; unsigned int nDataPos; // Time (in microseconds) of message receipt. int64_t nTime; CNetMessage(const CMessageHeader::MessageStartChars &pchMessageStartIn, int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), hdr(pchMessageStartIn), vRecv(nTypeIn, nVersionIn) { hdrbuf.resize(24); in_data = false; nHdrPos = 0; nDataPos = 0; nTime = 0; } bool complete() const { if (!in_data) { return false; } return (hdr.nMessageSize == nDataPos); } const uint256 &GetMessageHash() const; void SetVersion(int nVersionIn) { hdrbuf.SetVersion(nVersionIn); vRecv.SetVersion(nVersionIn); } int readHeader(const char *pch, unsigned int nBytes); int readData(const char *pch, unsigned int nBytes); }; /** Information about a peer */ class CNode { friend class CConnman; public: // socket std::atomic nServices; ServiceFlags nServicesExpected; SOCKET hSocket; // Total size of all vSendMsg entries. size_t nSendSize; // Offset inside the first vSendMsg already sent. size_t nSendOffset; uint64_t nSendBytes; std::deque> vSendMsg; CCriticalSection cs_vSend; CCriticalSection cs_hSocket; CCriticalSection cs_vRecv; CCriticalSection cs_vProcessMsg; std::list vProcessMsg; size_t nProcessQueueSize; CCriticalSection cs_sendProcessing; std::deque vRecvGetData; uint64_t nRecvBytes; std::atomic nRecvVersion; std::atomic nLastSend; std::atomic nLastRecv; const int64_t nTimeConnected; std::atomic nTimeOffset; const CAddress addr; std::atomic nVersion; // strSubVer is whatever byte array we read from the wire. However, this // field is intended to be printed out, displayed to humans in various forms // and so on. So we sanitize it and store the sanitized version in // cleanSubVer. The original should be used when dealing with the network or // wire types and the cleaned string used when displayed or logged. std::string strSubVer, cleanSubVer; // Used for both cleanSubVer and strSubVer. CCriticalSection cs_SubVer; // This peer can bypass DoS banning. bool fWhitelisted; // If true this node is being used as a short lived feeler. bool fFeeler; bool fOneShot; bool fAddnode; bool fClient; const bool fInbound; std::atomic_bool fSuccessfullyConnected; std::atomic_bool fDisconnect; // We use fRelayTxes for two purposes - // 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. // protected by cs_filter bool fRelayTxes; bool fSentAddr; CSemaphoreGrant grantOutbound; CCriticalSection cs_filter; CBloomFilter *pfilter; std::atomic nRefCount; const NodeId id; const uint64_t nKeyedNetGroup; std::atomic_bool fPauseRecv; std::atomic_bool fPauseSend; protected: mapMsgCmdSize mapSendBytesPerMsgCmd; mapMsgCmdSize mapRecvBytesPerMsgCmd; public: uint256 hashContinue; std::atomic nStartingHeight; // flood relay std::vector vAddrToSend; CRollingBloomFilter addrKnown; bool fGetAddr; std::set setKnown; int64_t nNextAddrSend; int64_t nNextLocalAddrSend; // Inventory based relay. CRollingBloomFilter filterInventoryKnown; // 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 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 vInventoryBlockToSend; CCriticalSection cs_inventory; std::set setAskFor; std::multimap mapAskFor; int64_t nNextInvSend; // Used for headers announcements - unfiltered blocks to relay. Also // protected by cs_inventory. std::vector vBlockHashesToAnnounce; // Used for BIP35 mempool sending, also protected by cs_inventory. bool fSendMempool; // Last time a "MEMPOOL" request was serviced. std::atomic timeLastMempoolReq; // Block and TXN accept times std::atomic nLastBlockTime; std::atomic nLastTXTime; // Ping time measurement: // The pong reply we're expecting, or 0 if no pong expected. std::atomic nPingNonceSent; // Time (in usec) the last ping was sent, or 0 if no ping was ever sent. std::atomic nPingUsecStart; // Last measured round-trip time. std::atomic nPingUsecTime; // Best measured round-trip time. std::atomic nMinPingUsecTime; // Whether a ping is requested. std::atomic fPingQueued; // Whether the node uses the bitcoin cash magic to communicate. std::atomic fUsesCashMagic; // Minimum fee rate with which to filter inv's to this node CAmount minFeeFilter; CCriticalSection cs_feeFilter; CAmount lastSentFeeFilter; int64_t nextSendTimeFeeFilter; CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const std::string &addrNameIn = "", bool fInboundIn = false); ~CNode(); private: CNode(const CNode &); void operator=(const CNode &); const uint64_t nLocalHostNonce; // Services offered to this peer const ServiceFlags nLocalServices; const int nMyStartingHeight; int nSendVersion; // Used only by SocketHandler thread. std::list vRecvMsg; mutable CCriticalSection cs_addrName; std::string addrName; CService addrLocal; mutable CCriticalSection cs_addrLocal; public: NodeId GetId() const { return id; } uint64_t GetLocalNonce() const { return nLocalHostNonce; } int GetMyStartingHeight() const { return nMyStartingHeight; } int GetRefCount() { assert(nRefCount >= 0); return nRefCount; } bool ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool &complete); void SetRecvVersion(int nVersionIn) { nRecvVersion = nVersionIn; } int GetRecvVersion() { return nRecvVersion; } void SetSendVersion(int nVersionIn); int GetSendVersion() const; const CMessageHeader::MessageStartChars & GetMagic(const CChainParams ¶ms) const { return fUsesCashMagic ? params.CashMessageStart() : params.MessageStart(); } CService GetAddrLocal() const; //! May not be called more than once void SetAddrLocal(const CService &addrLocalIn); CNode *AddRef() { nRefCount++; return this; } void Release() { nRefCount--; } void AddAddressKnown(const CAddress &_addr) { addrKnown.insert(_addr.GetKey()); } void PushAddress(const CAddress &_addr, FastRandomContext &insecure_rand) { // Known checking here is only to save space from duplicates. // SendMessages will filter it again for knowns that were added // after addresses were pushed. if (_addr.IsValid() && !addrKnown.contains(_addr.GetKey())) { if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) { - vAddrToSend[insecure_rand.rand32() % vAddrToSend.size()] = + vAddrToSend[insecure_rand.randrange(vAddrToSend.size())] = _addr; } else { vAddrToSend.push_back(_addr); } } } void AddInventoryKnown(const CInv &inv) { LOCK(cs_inventory); filterInventoryKnown.insert(inv.hash); } void PushInventory(const CInv &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); } } void PushBlockHash(const uint256 &hash) { LOCK(cs_inventory); vBlockHashesToAnnounce.push_back(hash); } void AskFor(const CInv &inv); void CloseSocketDisconnect(); void copyStats(CNodeStats &stats); ServiceFlags GetLocalServices() const { return nLocalServices; } std::string GetAddrName() const; //! Sets the addrName only if it was not previously set void MaybeSetAddrName(const std::string &addrNameIn); }; /** * Return a timestamp in the future (in microseconds) for exponentially * distributed events. */ int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds); std::string getSubVersionEB(uint64_t MaxBlockSize); std::string userAgent(const Config &config); #endif // BITCOIN_NET_H diff --git a/src/random.cpp b/src/random.cpp index 1f9b0f13f..1831f4547 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -1,290 +1,293 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "random.h" #include "crypto/sha512.h" #include "support/cleanse.h" #ifdef WIN32 #include "compat.h" // for Windows API #include #endif #include "util.h" // for LogPrint() #include "utilstrencodings.h" // for GetTime() #include #include #ifndef WIN32 #include #endif #ifdef HAVE_SYS_GETRANDOM #include #include #endif #ifdef HAVE_GETENTROPY #include #endif #ifdef HAVE_SYSCTL_ARND #include #endif #include #include static void RandFailure() { LogPrintf("Failed to read randomness, aborting\n"); abort(); } static inline int64_t GetPerformanceCounter() { int64_t nCounter = 0; #ifdef WIN32 QueryPerformanceCounter((LARGE_INTEGER *)&nCounter); #else timeval t; gettimeofday(&t, nullptr); nCounter = t.tv_sec * 1000000LL + t.tv_usec; #endif return nCounter; } void RandAddSeed() { // Seed with CPU performance counter int64_t nCounter = GetPerformanceCounter(); RAND_add(&nCounter, sizeof(nCounter), 1.5); memory_cleanse((void *)&nCounter, sizeof(nCounter)); } static void RandAddSeedPerfmon() { RandAddSeed(); #ifdef WIN32 // Don't need this on Linux, OpenSSL automatically uses /dev/urandom // Seed with the entire set of perfmon data // This can take up to 2 seconds, so only do it every 10 minutes static int64_t nLastPerfmon; if (GetTime() < nLastPerfmon + 10 * 60) return; nLastPerfmon = GetTime(); std::vector vData(250000, 0); long ret = 0; unsigned long nSize = 0; // Bail out at more than 10MB of performance data const size_t nMaxSize = 10000000; while (true) { nSize = vData.size(); ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", nullptr, nullptr, vData.data(), &nSize); if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize) { break; } // Grow size of buffer exponentially vData.resize(std::max((vData.size() * 3) / 2, nMaxSize)); } RegCloseKey(HKEY_PERFORMANCE_DATA); if (ret == ERROR_SUCCESS) { RAND_add(vData.data(), nSize, nSize / 100.0); memory_cleanse(vData.data(), nSize); LogPrint("rand", "%s: %lu bytes\n", __func__, nSize); } else { // Warn only once static bool warned = false; if (!warned) { LogPrintf("%s: Warning: RegQueryValueExA(HKEY_PERFORMANCE_DATA) " "failed with code %i\n", __func__, ret); warned = true; } } #endif } #ifndef WIN32 /** *Fallback: get 32 bytes of system entropy from /dev/urandom. The most *compatible way to get cryptographic randomness on UNIX-ish platforms. */ void GetDevURandom(unsigned char *ent32) { int f = open("/dev/urandom", O_RDONLY); if (f == -1) { RandFailure(); } int have = 0; do { ssize_t n = read(f, ent32 + have, NUM_OS_RANDOM_BYTES - have); if (n <= 0 || n + have > NUM_OS_RANDOM_BYTES) { RandFailure(); } have += n; } while (have < NUM_OS_RANDOM_BYTES); close(f); } #endif /** Get 32 bytes of system entropy. */ void GetOSRand(uint8_t *ent32) { #if defined(WIN32) HCRYPTPROV hProvider; int ret = CryptAcquireContextW(&hProvider, nullptr, nullptr, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); if (!ret) { RandFailure(); } ret = CryptGenRandom(hProvider, NUM_OS_RANDOM_BYTES, ent32); if (!ret) { RandFailure(); } CryptReleaseContext(hProvider, 0); #elif defined(HAVE_SYS_GETRANDOM) /** * Linux. From the getrandom(2) man page: * "If the urandom source has been initialized, reads of up to 256 bytes * will always return as many bytes as requested and will not be interrupted * by signals." */ int rv = syscall(SYS_getrandom, ent32, NUM_OS_RANDOM_BYTES, 0); if (rv != NUM_OS_RANDOM_BYTES) { if (rv < 0 && errno == ENOSYS) { /* Fallback for kernel <3.17: the return value will be -1 and errno * ENOSYS if the syscall is not available, in that case fall back * to /dev/urandom. */ GetDevURandom(ent32); } else { RandFailure(); } } #elif defined(HAVE_GETENTROPY) /* On OpenBSD this can return up to 256 bytes of entropy, will return an * error if more are requested. * The call cannot return less than the requested number of bytes. */ if (getentropy(ent32, NUM_OS_RANDOM_BYTES) != 0) { RandFailure(); } #elif defined(HAVE_SYSCTL_ARND) /* FreeBSD and similar. It is possible for the call to return less * bytes than requested, so need to read in a loop. */ static const int name[2] = {CTL_KERN, KERN_ARND}; int have = 0; do { size_t len = NUM_OS_RANDOM_BYTES - have; if (sysctl(name, ARRAYLEN(name), ent32 + have, &len, nullptr, 0) != 0) { RandFailure(); } have += len; } while (have < NUM_OS_RANDOM_BYTES); #else /* Fall back to /dev/urandom if there is no specific method implemented to * get system entropy for this OS. */ GetDevURandom(ent32); #endif } void GetRandBytes(uint8_t *buf, int num) { if (RAND_bytes(buf, num) != 1) { RandFailure(); } } void GetStrongRandBytes(uint8_t *out, int num) { assert(num <= 32); CSHA512 hasher; uint8_t buf[64]; // First source: OpenSSL's RNG RandAddSeedPerfmon(); GetRandBytes(buf, 32); hasher.Write(buf, 32); // Second source: OS RNG GetOSRand(buf); hasher.Write(buf, 32); // Produce output hasher.Finalize(buf); memcpy(out, buf, num); memory_cleanse(buf, 64); } uint64_t GetRand(uint64_t nMax) { if (nMax == 0) { return 0; } // The range of the random source must be a multiple of the modulus to give // every possible output value an equal possibility uint64_t nRange = (std::numeric_limits::max() / nMax) * nMax; uint64_t nRand = 0; do { GetRandBytes((uint8_t *)&nRand, sizeof(nRand)); } while (nRand >= nRange); return (nRand % nMax); } int GetRandInt(int nMax) { return GetRand(nMax); } uint256 GetRandHash() { uint256 hash; GetRandBytes((uint8_t *)&hash, sizeof(hash)); return hash; } -FastRandomContext::FastRandomContext(bool fDeterministic) { - // The seed values have some unlikely fixed points which we avoid. - if (fDeterministic) { - Rz = Rw = 11; - } else { - uint32_t tmp; - do { - GetRandBytes((uint8_t *)&tmp, 4); - } while (tmp == 0 || tmp == 0x9068ffffU); - Rz = tmp; - do { - GetRandBytes((uint8_t *)&tmp, 4); - } while (tmp == 0 || tmp == 0x464fffffU); - Rw = tmp; - } +void FastRandomContext::RandomSeed() { + uint256 seed = GetRandHash(); + rng.SetKey(seed.begin(), 32); + requires_seed = false; +} + +FastRandomContext::FastRandomContext(const uint256 &seed) + : requires_seed(false), bytebuf_size(0), bitbuf_size(0) { + rng.SetKey(seed.begin(), 32); } bool Random_SanityCheck() { /* This does not measure the quality of randomness, but it does test that * OSRandom() overwrites all 32 bytes of the output given a maximum number * of tries. */ static const ssize_t MAX_TRIES = 1024; uint8_t data[NUM_OS_RANDOM_BYTES]; /* Tracks which bytes have been overwritten at least once */ bool overwritten[NUM_OS_RANDOM_BYTES] = {}; int num_overwritten; int tries = 0; /* Loop until all bytes have been overwritten at least once, or max number * tries reached */ do { memset(data, 0, NUM_OS_RANDOM_BYTES); GetOSRand(data); for (int x = 0; x < NUM_OS_RANDOM_BYTES; ++x) { overwritten[x] |= (data[x] != 0); } num_overwritten = 0; for (int x = 0; x < NUM_OS_RANDOM_BYTES; ++x) { if (overwritten[x]) { num_overwritten += 1; } } tries += 1; } while (num_overwritten < NUM_OS_RANDOM_BYTES && tries < MAX_TRIES); /* If this failed, bailed out after too many tries */ return (num_overwritten == NUM_OS_RANDOM_BYTES); } + +FastRandomContext::FastRandomContext(bool fDeterministic) + : requires_seed(!fDeterministic), bytebuf_size(0), bitbuf_size(0) { + if (!fDeterministic) { + return; + } + uint256 seed; + rng.SetKey(seed.begin(), 32); +} diff --git a/src/random.h b/src/random.h index 58074b91b..cad408070 100644 --- a/src/random.h +++ b/src/random.h @@ -1,69 +1,135 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-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. #ifndef BITCOIN_RANDOM_H #define BITCOIN_RANDOM_H +#include "crypto/chacha20.h" +#include "crypto/common.h" #include "uint256.h" #include /* Seed OpenSSL PRNG with additional entropy data */ void RandAddSeed(); /** * Functions to gather random data via the OpenSSL PRNG */ void GetRandBytes(uint8_t *buf, int num); uint64_t GetRand(uint64_t nMax); int GetRandInt(int nMax); uint256 GetRandHash(); /** * Function to gather random data from multiple sources, failing whenever any of * those source fail to provide a result. */ void GetStrongRandBytes(uint8_t *buf, int num); /** * Fast randomness source. This is seeded once with secure random data, but is * completely deterministic and insecure after that. * This class is not thread-safe. */ class FastRandomContext { +private: + bool requires_seed; + ChaCha20 rng; + + uint8_t bytebuf[64]; + int bytebuf_size; + + uint64_t bitbuf; + int bitbuf_size; + + void RandomSeed(); + + void FillByteBuffer() { + if (requires_seed) { + RandomSeed(); + } + rng.Output(bytebuf, sizeof(bytebuf)); + bytebuf_size = sizeof(bytebuf); + } + + void FillBitBuffer() { + bitbuf = rand64(); + bitbuf_size = 64; + } + public: explicit FastRandomContext(bool fDeterministic = false); - uint32_t rand32() { - Rz = 36969 * (Rz & 65535) + (Rz >> 16); - Rw = 18000 * (Rw & 65535) + (Rw >> 16); - return (Rw << 16) + Rz; + /** Initialize with explicit seed (only for testing) */ + explicit FastRandomContext(const uint256 &seed); + + /** Generate a random 64-bit integer. */ + uint64_t rand64() { + if (bytebuf_size < 8) { + FillByteBuffer(); + } + uint64_t ret = ReadLE64(bytebuf + 64 - bytebuf_size); + bytebuf_size -= 8; + return ret; + } + + /** Generate a random (bits)-bit integer. */ + uint64_t randbits(int bits) { + if (bits == 0) { + return 0; + } else if (bits > 32) { + return rand64() >> (64 - bits); + } else { + if (bitbuf_size < bits) { + FillBitBuffer(); + } + uint64_t ret = bitbuf & (~uint64_t(0) >> (64 - bits)); + bitbuf >>= bits; + bitbuf_size -= bits; + return ret; + } } - uint32_t Rz; - uint32_t Rw; + /** Generate a random integer in the range [0..range). */ + uint64_t randrange(uint64_t range) { + --range; + int bits = CountBits(range); + while (true) { + uint64_t ret = randbits(bits); + if (ret <= range) { + return ret; + } + } + } + + /** Generate a random 32-bit integer. */ + uint32_t rand32() { return randbits(32); } + + /** Generate a random boolean. */ + bool randbool() { return randbits(1); } }; /** * Number of random bytes returned by GetOSRand. * When changing this constant make sure to change all call sites, and make sure * that the underlying OS APIs for all platforms support the number (many cap * out at 256 bytes). */ static const ssize_t NUM_OS_RANDOM_BYTES = 32; /** * Get 32 bytes of system entropy. Do not use this in application code: use * GetStrongRandBytes instead. */ void GetOSRand(unsigned char *ent32); /** * Check that OS randomness is available and returning the requested number of * bytes. */ bool Random_SanityCheck(); #endif // BITCOIN_RANDOM_H diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index 8360b1951..b58ac1fdf 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -1,515 +1,516 @@ // Copyright (c) 2012-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "addrman.h" #include "test/test_bitcoin.h" #include #include #include "hash.h" #include "netbase.h" #include "random.h" class CAddrManTest : public CAddrMan { uint64_t state; public: CAddrManTest() { state = 1; } //! Ensure that bucket placement is always the same for testing purposes. void MakeDeterministic() { nKey.SetNull(); insecure_rand = FastRandomContext(true); } int RandomInt(int nMax) { state = (CHashWriter(SER_GETHASH, 0) << state).GetHash().GetCheapHash(); return (unsigned int)(state % nMax); } CAddrInfo *Find(const CNetAddr &addr, int *pnId = nullptr) { return CAddrMan::Find(addr, pnId); } CAddrInfo *Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = nullptr) { return CAddrMan::Create(addr, addrSource, pnId); } void Delete(int nId) { CAddrMan::Delete(nId); } }; static CNetAddr ResolveIP(const char *ip) { CNetAddr addr; BOOST_CHECK_MESSAGE(LookupHost(ip, addr, false), strprintf("failed to resolve: %s", ip)); return addr; } static CNetAddr ResolveIP(std::string ip) { return ResolveIP(ip.c_str()); } static CService ResolveService(const char *ip, int port = 0) { CService serv; BOOST_CHECK_MESSAGE(Lookup(ip, serv, port, false), strprintf("failed to resolve: %s:%i", ip, port)); return serv; } static CService ResolveService(std::string ip, int port = 0) { return ResolveService(ip.c_str(), port); } BOOST_FIXTURE_TEST_SUITE(addrman_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(addrman_simple) { CAddrManTest addrman; // Set addrman addr placement to be deterministic. addrman.MakeDeterministic(); CNetAddr source = ResolveIP("252.2.2.2"); // Test 1: Does Addrman respond correctly when empty. BOOST_CHECK(addrman.size() == 0); CAddrInfo addr_null = addrman.Select(); BOOST_CHECK(addr_null.ToString() == "[::]:0"); // Test 2: Does Addrman::Add work as expected. CService addr1 = ResolveService("250.1.1.1", 8333); addrman.Add(CAddress(addr1, NODE_NONE), source); BOOST_CHECK(addrman.size() == 1); CAddrInfo addr_ret1 = addrman.Select(); BOOST_CHECK(addr_ret1.ToString() == "250.1.1.1:8333"); // Test 3: Does IP address deduplication work correctly. // Expected dup IP should not be added. CService addr1_dup = ResolveService("250.1.1.1", 8333); addrman.Add(CAddress(addr1_dup, NODE_NONE), source); BOOST_CHECK(addrman.size() == 1); // Test 5: New table has one addr and we add a diff addr we should // have two addrs. CService addr2 = ResolveService("250.1.1.2", 8333); addrman.Add(CAddress(addr2, NODE_NONE), source); BOOST_CHECK(addrman.size() == 2); // Test 6: AddrMan::Clear() should empty the new table. addrman.Clear(); BOOST_CHECK(addrman.size() == 0); CAddrInfo addr_null2 = addrman.Select(); BOOST_CHECK(addr_null2.ToString() == "[::]:0"); } BOOST_AUTO_TEST_CASE(addrman_ports) { CAddrManTest addrman; // Set addrman addr placement to be deterministic. addrman.MakeDeterministic(); CNetAddr source = ResolveIP("252.2.2.2"); BOOST_CHECK(addrman.size() == 0); // Test 7; Addr with same IP but diff port does not replace existing addr. CService addr1 = ResolveService("250.1.1.1", 8333); addrman.Add(CAddress(addr1, NODE_NONE), source); BOOST_CHECK(addrman.size() == 1); CService addr1_port = ResolveService("250.1.1.1", 8334); addrman.Add(CAddress(addr1_port, NODE_NONE), source); BOOST_CHECK(addrman.size() == 1); CAddrInfo addr_ret2 = addrman.Select(); BOOST_CHECK(addr_ret2.ToString() == "250.1.1.1:8333"); // Test 8: Add same IP but diff port to tried table, it doesn't get added. // Perhaps this is not ideal behavior but it is the current behavior. addrman.Good(CAddress(addr1_port, NODE_NONE)); BOOST_CHECK(addrman.size() == 1); bool newOnly = true; CAddrInfo addr_ret3 = addrman.Select(newOnly); BOOST_CHECK(addr_ret3.ToString() == "250.1.1.1:8333"); } BOOST_AUTO_TEST_CASE(addrman_select) { CAddrManTest addrman; // Set addrman addr placement to be deterministic. addrman.MakeDeterministic(); CNetAddr source = ResolveIP("252.2.2.2"); // Test 9: Select from new with 1 addr in new. CService addr1 = ResolveService("250.1.1.1", 8333); addrman.Add(CAddress(addr1, NODE_NONE), source); BOOST_CHECK(addrman.size() == 1); bool newOnly = true; CAddrInfo addr_ret1 = addrman.Select(newOnly); BOOST_CHECK(addr_ret1.ToString() == "250.1.1.1:8333"); // Test 10: move addr to tried, select from new expected nothing returned. addrman.Good(CAddress(addr1, NODE_NONE)); BOOST_CHECK(addrman.size() == 1); CAddrInfo addr_ret2 = addrman.Select(newOnly); BOOST_CHECK(addr_ret2.ToString() == "[::]:0"); CAddrInfo addr_ret3 = addrman.Select(); BOOST_CHECK(addr_ret3.ToString() == "250.1.1.1:8333"); BOOST_CHECK(addrman.size() == 1); // Add three addresses to new table. CService addr2 = ResolveService("250.3.1.1", 8333); CService addr3 = ResolveService("250.3.2.2", 9999); CService addr4 = ResolveService("250.3.3.3", 9999); addrman.Add(CAddress(addr2, NODE_NONE), ResolveService("250.3.1.1", 8333)); addrman.Add(CAddress(addr3, NODE_NONE), ResolveService("250.3.1.1", 8333)); addrman.Add(CAddress(addr4, NODE_NONE), ResolveService("250.4.1.1", 8333)); // Add three addresses to tried table. CService addr5 = ResolveService("250.4.4.4", 8333); CService addr6 = ResolveService("250.4.5.5", 7777); CService addr7 = ResolveService("250.4.6.6", 8333); addrman.Add(CAddress(addr5, NODE_NONE), ResolveService("250.3.1.1", 8333)); addrman.Good(CAddress(addr5, NODE_NONE)); addrman.Add(CAddress(addr6, NODE_NONE), ResolveService("250.3.1.1", 8333)); addrman.Good(CAddress(addr6, NODE_NONE)); addrman.Add(CAddress(addr7, NODE_NONE), ResolveService("250.1.1.3", 8333)); addrman.Good(CAddress(addr7, NODE_NONE)); // Test 11: 6 addrs + 1 addr from last test = 7. BOOST_CHECK(addrman.size() == 7); // Test 12: Select pulls from new and tried regardless of port number. - BOOST_CHECK(addrman.Select().ToString() == "250.4.6.6:8333"); - BOOST_CHECK(addrman.Select().ToString() == "250.3.2.2:9999"); - BOOST_CHECK(addrman.Select().ToString() == "250.3.3.3:9999"); - BOOST_CHECK(addrman.Select().ToString() == "250.4.4.4:8333"); + std::set ports; + for (int i = 0; i < 20; ++i) { + ports.insert(addrman.Select().GetPort()); + } + BOOST_CHECK_EQUAL(ports.size(), 3); } BOOST_AUTO_TEST_CASE(addrman_new_collisions) { CAddrManTest addrman; // Set addrman addr placement to be deterministic. addrman.MakeDeterministic(); CNetAddr source = ResolveIP("252.2.2.2"); BOOST_CHECK(addrman.size() == 0); for (unsigned int i = 1; i < 18; i++) { CService addr = ResolveService("250.1.1." + boost::to_string(i)); addrman.Add(CAddress(addr, NODE_NONE), source); // Test 13: No collision in new table yet. BOOST_CHECK(addrman.size() == i); } // Test 14: new table collision! CService addr1 = ResolveService("250.1.1.18"); addrman.Add(CAddress(addr1, NODE_NONE), source); BOOST_CHECK(addrman.size() == 17); CService addr2 = ResolveService("250.1.1.19"); addrman.Add(CAddress(addr2, NODE_NONE), source); BOOST_CHECK(addrman.size() == 18); } BOOST_AUTO_TEST_CASE(addrman_tried_collisions) { CAddrManTest addrman; // Set addrman addr placement to be deterministic. addrman.MakeDeterministic(); CNetAddr source = ResolveIP("252.2.2.2"); BOOST_CHECK(addrman.size() == 0); for (unsigned int i = 1; i < 80; i++) { CService addr = ResolveService("250.1.1." + boost::to_string(i)); addrman.Add(CAddress(addr, NODE_NONE), source); addrman.Good(CAddress(addr, NODE_NONE)); // Test 15: No collision in tried table yet. BOOST_CHECK_EQUAL(addrman.size(), i); } // Test 16: tried table collision! CService addr1 = ResolveService("250.1.1.80"); addrman.Add(CAddress(addr1, NODE_NONE), source); BOOST_CHECK(addrman.size() == 79); CService addr2 = ResolveService("250.1.1.81"); addrman.Add(CAddress(addr2, NODE_NONE), source); BOOST_CHECK(addrman.size() == 80); } BOOST_AUTO_TEST_CASE(addrman_find) { CAddrManTest addrman; // Set addrman addr placement to be deterministic. addrman.MakeDeterministic(); BOOST_CHECK(addrman.size() == 0); CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE); CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE); CAddress addr3 = CAddress(ResolveService("251.255.2.1", 8333), NODE_NONE); CNetAddr source1 = ResolveIP("250.1.2.1"); CNetAddr source2 = ResolveIP("250.1.2.2"); addrman.Add(addr1, source1); addrman.Add(addr2, source2); addrman.Add(addr3, source1); // Test 17: ensure Find returns an IP matching what we searched on. CAddrInfo *info1 = addrman.Find(addr1); BOOST_CHECK(info1); if (info1) BOOST_CHECK(info1->ToString() == "250.1.2.1:8333"); // Test 18; Find does not discriminate by port number. CAddrInfo *info2 = addrman.Find(addr2); BOOST_CHECK(info2); if (info2 && info1) BOOST_CHECK(info2->ToString() == info1->ToString()); // Test 19: Find returns another IP matching what we searched on. CAddrInfo *info3 = addrman.Find(addr3); BOOST_CHECK(info3); if (info3) BOOST_CHECK(info3->ToString() == "251.255.2.1:8333"); } BOOST_AUTO_TEST_CASE(addrman_create) { CAddrManTest addrman; // Set addrman addr placement to be deterministic. addrman.MakeDeterministic(); BOOST_CHECK(addrman.size() == 0); CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE); CNetAddr source1 = ResolveIP("250.1.2.1"); int nId; CAddrInfo *pinfo = addrman.Create(addr1, source1, &nId); // Test 20: The result should be the same as the input addr. BOOST_CHECK(pinfo->ToString() == "250.1.2.1:8333"); CAddrInfo *info2 = addrman.Find(addr1); BOOST_CHECK(info2->ToString() == "250.1.2.1:8333"); } BOOST_AUTO_TEST_CASE(addrman_delete) { CAddrManTest addrman; // Set addrman addr placement to be deterministic. addrman.MakeDeterministic(); BOOST_CHECK(addrman.size() == 0); CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE); CNetAddr source1 = ResolveIP("250.1.2.1"); int nId; addrman.Create(addr1, source1, &nId); // Test 21: Delete should actually delete the addr. BOOST_CHECK(addrman.size() == 1); addrman.Delete(nId); BOOST_CHECK(addrman.size() == 0); CAddrInfo *info2 = addrman.Find(addr1); BOOST_CHECK(info2 == nullptr); } BOOST_AUTO_TEST_CASE(addrman_getaddr) { CAddrManTest addrman; // Set addrman addr placement to be deterministic. addrman.MakeDeterministic(); // Test 22: Sanity check, GetAddr should never return anything if addrman // is empty. BOOST_CHECK(addrman.size() == 0); std::vector vAddr1 = addrman.GetAddr(); BOOST_CHECK(vAddr1.size() == 0); CAddress addr1 = CAddress(ResolveService("250.250.2.1", 8333), NODE_NONE); addr1.nTime = GetAdjustedTime(); // Set time so isTerrible = false CAddress addr2 = CAddress(ResolveService("250.251.2.2", 9999), NODE_NONE); addr2.nTime = GetAdjustedTime(); CAddress addr3 = CAddress(ResolveService("251.252.2.3", 8333), NODE_NONE); addr3.nTime = GetAdjustedTime(); CAddress addr4 = CAddress(ResolveService("252.253.3.4", 8333), NODE_NONE); addr4.nTime = GetAdjustedTime(); CAddress addr5 = CAddress(ResolveService("252.254.4.5", 8333), NODE_NONE); addr5.nTime = GetAdjustedTime(); CNetAddr source1 = ResolveIP("250.1.2.1"); CNetAddr source2 = ResolveIP("250.2.3.3"); // Test 23: Ensure GetAddr works with new addresses. addrman.Add(addr1, source1); addrman.Add(addr2, source2); addrman.Add(addr3, source1); addrman.Add(addr4, source2); addrman.Add(addr5, source1); // GetAddr returns 23% of addresses, 23% of 5 is 1 rounded down. BOOST_CHECK(addrman.GetAddr().size() == 1); // Test 24: Ensure GetAddr works with new and tried addresses. addrman.Good(CAddress(addr1, NODE_NONE)); addrman.Good(CAddress(addr2, NODE_NONE)); BOOST_CHECK(addrman.GetAddr().size() == 1); // Test 25: Ensure GetAddr still returns 23% when addrman has many addrs. for (unsigned int i = 1; i < (8 * 256); i++) { int octet1 = i % 256; int octet2 = (i / 256) % 256; int octet3 = (i / (256 * 2)) % 256; std::string strAddr = boost::to_string(octet1) + "." + boost::to_string(octet2) + "." + boost::to_string(octet3) + ".23"; CAddress addr = CAddress(ResolveService(strAddr), NODE_NONE); // Ensure that for all addrs in addrman, isTerrible == false. addr.nTime = GetAdjustedTime(); addrman.Add(addr, ResolveIP(strAddr)); if (i % 8 == 0) addrman.Good(addr); } std::vector vAddr = addrman.GetAddr(); size_t percent23 = (addrman.size() * 23) / 100; BOOST_CHECK(vAddr.size() == percent23); BOOST_CHECK(vAddr.size() == 461); // (Addrman.size() < number of addresses added) due to address collisons. BOOST_CHECK(addrman.size() == 2007); } BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket) { CAddrManTest addrman; // Set addrman addr placement to be deterministic. addrman.MakeDeterministic(); CAddress addr1 = CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE); CAddress addr2 = CAddress(ResolveService("250.1.1.1", 9999), NODE_NONE); CNetAddr source1 = ResolveIP("250.1.1.1"); CAddrInfo info1 = CAddrInfo(addr1, source1); uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash(); uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash(); BOOST_CHECK(info1.GetTriedBucket(nKey1) == 40); // Test 26: Make sure key actually randomizes bucket placement. A fail on // this test could be a security issue. BOOST_CHECK(info1.GetTriedBucket(nKey1) != info1.GetTriedBucket(nKey2)); // Test 27: Two addresses with same IP but different ports can map to // different buckets because they have different keys. CAddrInfo info2 = CAddrInfo(addr2, source1); BOOST_CHECK(info1.GetKey() != info2.GetKey()); BOOST_CHECK(info1.GetTriedBucket(nKey1) != info2.GetTriedBucket(nKey1)); std::set buckets; for (int i = 0; i < 255; i++) { CAddrInfo infoi = CAddrInfo(CAddress(ResolveService("250.1.1." + boost::to_string(i)), NODE_NONE), ResolveIP("250.1.1." + boost::to_string(i))); int bucket = infoi.GetTriedBucket(nKey1); buckets.insert(bucket); } // Test 28: IP addresses in the same group (\16 prefix for IPv4) should // never get more than 8 buckets BOOST_CHECK(buckets.size() == 8); buckets.clear(); for (int j = 0; j < 255; j++) { CAddrInfo infoj = CAddrInfo( CAddress(ResolveService("250." + boost::to_string(j) + ".1.1"), NODE_NONE), ResolveIP("250." + boost::to_string(j) + ".1.1")); int bucket = infoj.GetTriedBucket(nKey1); buckets.insert(bucket); } // Test 29: IP addresses in the different groups should map to more than // 8 buckets. BOOST_CHECK(buckets.size() == 160); } BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket) { CAddrManTest addrman; // Set addrman addr placement to be deterministic. addrman.MakeDeterministic(); CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE); CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE); CNetAddr source1 = ResolveIP("250.1.2.1"); CAddrInfo info1 = CAddrInfo(addr1, source1); uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash(); uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash(); BOOST_CHECK(info1.GetNewBucket(nKey1) == 786); // Test 30: Make sure key actually randomizes bucket placement. A fail on // this test could be a security issue. BOOST_CHECK(info1.GetNewBucket(nKey1) != info1.GetNewBucket(nKey2)); // Test 31: Ports should not effect bucket placement in the addr CAddrInfo info2 = CAddrInfo(addr2, source1); BOOST_CHECK(info1.GetKey() != info2.GetKey()); BOOST_CHECK(info1.GetNewBucket(nKey1) == info2.GetNewBucket(nKey1)); std::set buckets; for (int i = 0; i < 255; i++) { CAddrInfo infoi = CAddrInfo(CAddress(ResolveService("250.1.1." + boost::to_string(i)), NODE_NONE), ResolveIP("250.1.1." + boost::to_string(i))); int bucket = infoi.GetNewBucket(nKey1); buckets.insert(bucket); } // Test 32: IP addresses in the same group (\16 prefix for IPv4) should // always map to the same bucket. BOOST_CHECK(buckets.size() == 1); buckets.clear(); for (int j = 0; j < 4 * 255; j++) { CAddrInfo infoj = CAddrInfo( CAddress(ResolveService(boost::to_string(250 + (j / 255)) + "." + boost::to_string(j % 256) + ".1.1"), NODE_NONE), ResolveIP("251.4.1.1")); int bucket = infoj.GetNewBucket(nKey1); buckets.insert(bucket); } // Test 33: IP addresses in the same source groups should map to no more // than 64 buckets. BOOST_CHECK(buckets.size() <= 64); buckets.clear(); for (int p = 0; p < 255; p++) { CAddrInfo infoj = CAddrInfo(CAddress(ResolveService("250.1.1.1"), NODE_NONE), ResolveIP("250." + boost::to_string(p) + ".1.1")); int bucket = infoj.GetNewBucket(nKey1); buckets.insert(bucket); } // Test 34: IP addresses in the different source groups should map to more // than 64 buckets. BOOST_CHECK(buckets.size() > 64); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index fc0f49e14..3a05a906a 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -1,553 +1,631 @@ // Copyright (c) 2014-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "crypto/aes.h" +#include "crypto/chacha20.h" #include "crypto/hmac_sha256.h" #include "crypto/hmac_sha512.h" #include "crypto/ripemd160.h" #include "crypto/sha1.h" #include "crypto/sha256.h" #include "crypto/sha512.h" +#include "random.h" #include "test/test_bitcoin.h" #include "test/test_random.h" #include "utilstrencodings.h" #include #include #include #include BOOST_FIXTURE_TEST_SUITE(crypto_tests, BasicTestingSetup) template void TestVector(const Hasher &h, const In &in, const Out &out) { Out hash; BOOST_CHECK(out.size() == h.OUTPUT_SIZE); hash.resize(out.size()); { // Test that writing the whole input string at once works. Hasher(h).Write((uint8_t *)&in[0], in.size()).Finalize(&hash[0]); BOOST_CHECK(hash == out); } for (int i = 0; i < 32; i++) { // Test that writing the string broken up in random pieces works. Hasher hasher(h); size_t pos = 0; while (pos < in.size()) { size_t len = insecure_rand() % ((in.size() - pos + 1) / 2 + 1); hasher.Write((uint8_t *)&in[pos], len); pos += len; if (pos > 0 && pos + 2 * out.size() > in.size() && pos < in.size()) { // Test that writing the rest at once to a copy of a hasher // works. Hasher(hasher) .Write((uint8_t *)&in[pos], in.size() - pos) .Finalize(&hash[0]); BOOST_CHECK(hash == out); } } hasher.Finalize(&hash[0]); BOOST_CHECK(hash == out); } } void TestSHA1(const std::string &in, const std::string &hexout) { TestVector(CSHA1(), in, ParseHex(hexout)); } void TestSHA256(const std::string &in, const std::string &hexout) { TestVector(CSHA256(), in, ParseHex(hexout)); } void TestSHA512(const std::string &in, const std::string &hexout) { TestVector(CSHA512(), in, ParseHex(hexout)); } void TestRIPEMD160(const std::string &in, const std::string &hexout) { TestVector(CRIPEMD160(), in, ParseHex(hexout)); } void TestHMACSHA256(const std::string &hexkey, const std::string &hexin, const std::string &hexout) { std::vector key = ParseHex(hexkey); TestVector(CHMAC_SHA256(&key[0], key.size()), ParseHex(hexin), ParseHex(hexout)); } void TestHMACSHA512(const std::string &hexkey, const std::string &hexin, const std::string &hexout) { std::vector key = ParseHex(hexkey); TestVector(CHMAC_SHA512(&key[0], key.size()), ParseHex(hexin), ParseHex(hexout)); } void TestAES128(const std::string &hexkey, const std::string &hexin, const std::string &hexout) { std::vector key = ParseHex(hexkey); std::vector in = ParseHex(hexin); std::vector correctout = ParseHex(hexout); std::vector buf, buf2; assert(key.size() == 16); assert(in.size() == 16); assert(correctout.size() == 16); AES128Encrypt enc(&key[0]); buf.resize(correctout.size()); buf2.resize(correctout.size()); enc.Encrypt(&buf[0], &in[0]); BOOST_CHECK_EQUAL(HexStr(buf), HexStr(correctout)); AES128Decrypt dec(&key[0]); dec.Decrypt(&buf2[0], &buf[0]); BOOST_CHECK_EQUAL(HexStr(buf2), HexStr(in)); } void TestAES256(const std::string &hexkey, const std::string &hexin, const std::string &hexout) { std::vector key = ParseHex(hexkey); std::vector in = ParseHex(hexin); std::vector correctout = ParseHex(hexout); std::vector buf; assert(key.size() == 32); assert(in.size() == 16); assert(correctout.size() == 16); AES256Encrypt enc(&key[0]); buf.resize(correctout.size()); enc.Encrypt(&buf[0], &in[0]); BOOST_CHECK(buf == correctout); AES256Decrypt dec(&key[0]); dec.Decrypt(&buf[0], &buf[0]); BOOST_CHECK(buf == in); } void TestAES128CBC(const std::string &hexkey, const std::string &hexiv, bool pad, const std::string &hexin, const std::string &hexout) { std::vector key = ParseHex(hexkey); std::vector iv = ParseHex(hexiv); std::vector in = ParseHex(hexin); std::vector correctout = ParseHex(hexout); std::vector realout(in.size() + AES_BLOCKSIZE); // Encrypt the plaintext and verify that it equals the cipher AES128CBCEncrypt enc(&key[0], &iv[0], pad); int size = enc.Encrypt(&in[0], in.size(), &realout[0]); realout.resize(size); BOOST_CHECK(realout.size() == correctout.size()); BOOST_CHECK_MESSAGE(realout == correctout, HexStr(realout) + std::string(" != ") + hexout); // Decrypt the cipher and verify that it equals the plaintext std::vector decrypted(correctout.size()); AES128CBCDecrypt dec(&key[0], &iv[0], pad); size = dec.Decrypt(&correctout[0], correctout.size(), &decrypted[0]); decrypted.resize(size); BOOST_CHECK(decrypted.size() == in.size()); BOOST_CHECK_MESSAGE(decrypted == in, HexStr(decrypted) + std::string(" != ") + hexin); // Encrypt and re-decrypt substrings of the plaintext and verify that they // equal each-other for (std::vector::iterator i(in.begin()); i != in.end(); ++i) { std::vector sub(i, in.end()); std::vector subout(sub.size() + AES_BLOCKSIZE); int _size = enc.Encrypt(&sub[0], sub.size(), &subout[0]); if (_size != 0) { subout.resize(_size); std::vector subdecrypted(subout.size()); _size = dec.Decrypt(&subout[0], subout.size(), &subdecrypted[0]); subdecrypted.resize(_size); BOOST_CHECK(decrypted.size() == in.size()); BOOST_CHECK_MESSAGE(subdecrypted == sub, HexStr(subdecrypted) + std::string(" != ") + HexStr(sub)); } } } void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad, const std::string &hexin, const std::string &hexout) { std::vector key = ParseHex(hexkey); std::vector iv = ParseHex(hexiv); std::vector in = ParseHex(hexin); std::vector correctout = ParseHex(hexout); std::vector realout(in.size() + AES_BLOCKSIZE); // Encrypt the plaintext and verify that it equals the cipher AES256CBCEncrypt enc(&key[0], &iv[0], pad); int size = enc.Encrypt(&in[0], in.size(), &realout[0]); realout.resize(size); BOOST_CHECK(realout.size() == correctout.size()); BOOST_CHECK_MESSAGE(realout == correctout, HexStr(realout) + std::string(" != ") + hexout); // Decrypt the cipher and verify that it equals the plaintext std::vector decrypted(correctout.size()); AES256CBCDecrypt dec(&key[0], &iv[0], pad); size = dec.Decrypt(&correctout[0], correctout.size(), &decrypted[0]); decrypted.resize(size); BOOST_CHECK(decrypted.size() == in.size()); BOOST_CHECK_MESSAGE(decrypted == in, HexStr(decrypted) + std::string(" != ") + hexin); // Encrypt and re-decrypt substrings of the plaintext and verify that they // equal each-other for (std::vector::iterator i(in.begin()); i != in.end(); ++i) { std::vector sub(i, in.end()); std::vector subout(sub.size() + AES_BLOCKSIZE); int _size = enc.Encrypt(&sub[0], sub.size(), &subout[0]); if (_size != 0) { subout.resize(_size); std::vector subdecrypted(subout.size()); _size = dec.Decrypt(&subout[0], subout.size(), &subdecrypted[0]); subdecrypted.resize(_size); BOOST_CHECK(decrypted.size() == in.size()); BOOST_CHECK_MESSAGE(subdecrypted == sub, HexStr(subdecrypted) + std::string(" != ") + HexStr(sub)); } } } +void TestChaCha20(const std::string &hexkey, uint64_t nonce, uint64_t seek, + const std::string &hexout) { + std::vector key = ParseHex(hexkey); + ChaCha20 rng(key.data(), key.size()); + rng.SetIV(nonce); + rng.Seek(seek); + std::vector out = ParseHex(hexout); + std::vector outres; + outres.resize(out.size()); + rng.Output(outres.data(), outres.size()); + BOOST_CHECK(out == outres); +} + std::string LongTestString(void) { std::string ret; for (int i = 0; i < 200000; i++) { ret += uint8_t(i); ret += uint8_t(i >> 4); ret += uint8_t(i >> 8); ret += uint8_t(i >> 12); ret += uint8_t(i >> 16); } return ret; } const std::string test1 = LongTestString(); BOOST_AUTO_TEST_CASE(ripemd160_testvectors) { TestRIPEMD160("", "9c1185a5c5e9fc54612808977ee8f548b2258d31"); TestRIPEMD160("abc", "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"); TestRIPEMD160("message digest", "5d0689ef49d2fae572b881b123a85ffa21595f36"); TestRIPEMD160("secure hash algorithm", "20397528223b6a5f4cbc2808aba0464e645544f9"); TestRIPEMD160("RIPEMD160 is considered to be safe", "a7d78608c7af8a8e728778e81576870734122b66"); TestRIPEMD160("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "12a053384a9c0c88e405a06c27dcf49ada62eb2b"); TestRIPEMD160( "For this sample, this 63-byte string will be used as input data", "de90dbfee14b63fb5abf27c2ad4a82aaa5f27a11"); TestRIPEMD160( "This is exactly 64 bytes long, not counting the terminating byte", "eda31d51d3a623b81e19eb02e24ff65d27d67b37"); TestRIPEMD160(std::string(1000000, 'a'), "52783243c1697bdbe16d37f97f68f08325dc1528"); TestRIPEMD160(test1, "464243587bd146ea835cdf57bdae582f25ec45f1"); } BOOST_AUTO_TEST_CASE(sha1_testvectors) { TestSHA1("", "da39a3ee5e6b4b0d3255bfef95601890afd80709"); TestSHA1("abc", "a9993e364706816aba3e25717850c26c9cd0d89d"); TestSHA1("message digest", "c12252ceda8be8994d5fa0290a47231c1d16aae3"); TestSHA1("secure hash algorithm", "d4d6d2f0ebe317513bbd8d967d89bac5819c2f60"); TestSHA1("SHA1 is considered to be safe", "f2b6650569ad3a8720348dd6ea6c497dee3a842a"); TestSHA1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "84983e441c3bd26ebaae4aa1f95129e5e54670f1"); TestSHA1("For this sample, this 63-byte string will be used as input data", "4f0ea5cd0585a23d028abdc1a6684e5a8094dc49"); TestSHA1("This is exactly 64 bytes long, not counting the terminating byte", "fb679f23e7d1ce053313e66e127ab1b444397057"); TestSHA1(std::string(1000000, 'a'), "34aa973cd4c4daa4f61eeb2bdbad27316534016f"); TestSHA1(test1, "b7755760681cbfd971451668f32af5774f4656b5"); } BOOST_AUTO_TEST_CASE(sha256_testvectors) { TestSHA256( "", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); TestSHA256( "abc", "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); TestSHA256( "message digest", "f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650"); TestSHA256( "secure hash algorithm", "f30ceb2bb2829e79e4ca9753d35a8ecc00262d164cc077080295381cbd643f0d"); TestSHA256( "SHA256 is considered to be safe", "6819d915c73f4d1e77e4e1b52d1fa0f9cf9beaead3939f15874bd988e2a23630"); TestSHA256( "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"); TestSHA256( "For this sample, this 63-byte string will be used as input data", "f08a78cbbaee082b052ae0708f32fa1e50c5c421aa772ba5dbb406a2ea6be342"); TestSHA256( "This is exactly 64 bytes long, not counting the terminating byte", "ab64eff7e88e2e46165e29f2bce41826bd4c7b3552f6b382a9e7d3af47c245f8"); TestSHA256( "As Bitcoin relies on 80 byte header hashes, we want to have an " "example for that.", "7406e8de7d6e4fffc573daef05aefb8806e7790f55eab5576f31349743cca743"); TestSHA256( std::string(1000000, 'a'), "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"); TestSHA256( test1, "a316d55510b49662420f49d145d42fb83f31ef8dc016aa4e32df049991a91e26"); } BOOST_AUTO_TEST_CASE(sha512_testvectors) { TestSHA512( "", "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce" "47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"); TestSHA512( "abc", "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a" "2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"); TestSHA512( "message digest", "107dbf389d9e9f71a3a95f6c055b9251bc5268c2be16d6c13492ea45b0199f33" "09e16455ab1e96118e8a905d5597b72038ddb372a89826046de66687bb420e7c"); TestSHA512( "secure hash algorithm", "7746d91f3de30c68cec0dd693120a7e8b04d8073cb699bdce1a3f64127bca7a3" "d5db502e814bb63c063a7a5043b2df87c61133395f4ad1edca7fcf4b30c3236e"); TestSHA512( "SHA512 is considered to be safe", "099e6468d889e1c79092a89ae925a9499b5408e01b66cb5b0a3bd0dfa51a9964" "6b4a3901caab1318189f74cd8cf2e941829012f2449df52067d3dd5b978456c2"); TestSHA512( "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c335" "96fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445"); TestSHA512( "For this sample, this 63-byte string will be used as input data", "b3de4afbc516d2478fe9b518d063bda6c8dd65fc38402dd81d1eb7364e72fb6e" "6663cf6d2771c8f5a6da09601712fb3d2a36c6ffea3e28b0818b05b0a8660766"); TestSHA512( "This is exactly 64 bytes long, not counting the terminating byte", "70aefeaa0e7ac4f8fe17532d7185a289bee3b428d950c14fa8b713ca09814a38" "7d245870e007a80ad97c369d193e41701aa07f3221d15f0e65a1ff970cedf030"); TestSHA512( "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno" "ijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018" "501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909"); TestSHA512( std::string(1000000, 'a'), "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb" "de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"); TestSHA512( test1, "40cac46c147e6131c5193dd5f34e9d8bb4951395f27b08c558c65ff4ba2de594" "37de8c3ef5459d76a52cedc02dc499a3c9ed9dedbfb3281afd9653b8a112fafc"); } BOOST_AUTO_TEST_CASE(hmac_sha256_testvectors) { // test cases 1, 2, 3, 4, 6 and 7 of RFC 4231 TestHMACSHA256( "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", "4869205468657265", "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"); TestHMACSHA256( "4a656665", "7768617420646f2079612077616e7420666f72206e6f7468696e673f", "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"); TestHMACSHA256( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" "dddddddddddddddddddddddddddddddddddd", "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe"); TestHMACSHA256( "0102030405060708090a0b0c0d0e0f10111213141516171819", "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b"); TestHMACSHA256( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaa", "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a" "65204b6579202d2048617368204b6579204669727374", "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54"); TestHMACSHA256( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaa", "5468697320697320612074657374207573696e672061206c6172676572207468" "616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074" "68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565" "647320746f20626520686173686564206265666f7265206265696e6720757365" "642062792074686520484d414320616c676f726974686d2e", "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2"); } BOOST_AUTO_TEST_CASE(hmac_sha512_testvectors) { // test cases 1, 2, 3, 4, 6 and 7 of RFC 4231 TestHMACSHA512( "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", "4869205468657265", "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cde" "daa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854"); TestHMACSHA512( "4a656665", "7768617420646f2079612077616e7420666f72206e6f7468696e673f", "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea250554" "9758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737"); TestHMACSHA512( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" "dddddddddddddddddddddddddddddddddddd", "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39" "bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb"); TestHMACSHA512( "0102030405060708090a0b0c0d0e0f10111213141516171819", "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3db" "a91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd"); TestHMACSHA512( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaa", "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a" "65204b6579202d2048617368204b6579204669727374", "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f352" "6b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598"); TestHMACSHA512( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaa", "5468697320697320612074657374207573696e672061206c6172676572207468" "616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074" "68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565" "647320746f20626520686173686564206265666f7265206265696e6720757365" "642062792074686520484d414320616c676f726974686d2e", "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944" "b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58"); } BOOST_AUTO_TEST_CASE(aes_testvectors) { // AES test vectors from FIPS 197. TestAES128("000102030405060708090a0b0c0d0e0f", "00112233445566778899aabbccddeeff", "69c4e0d86a7b0430d8cdb78070b4c55a"); TestAES256( "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "00112233445566778899aabbccddeeff", "8ea2b7ca516745bfeafc49904b496089"); // AES-ECB test vectors from NIST sp800-38a. TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "6bc1bee22e409f96e93d7e117393172a", "3ad77bb40d7a3660a89ecaf32466ef97"); TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "ae2d8a571e03ac9c9eb76fac45af8e51", "f5d3d58503b9699de785895a96fdbaaf"); TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "30c81c46a35ce411e5fbc1191a0a52ef", "43b1cd7f598ece23881b00e3ed030688"); TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "f69f2445df4f9b17ad2b417be66c3710", "7b0c785e27e8ad3f8223207104725dd4"); TestAES256( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "6bc1bee22e409f96e93d7e117393172a", "f3eed1bdb5d2a03c064b5a7e3db181f8"); TestAES256( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "ae2d8a571e03ac9c9eb76fac45af8e51", "591ccb10d410ed26dc5ba74a31362870"); TestAES256( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "30c81c46a35ce411e5fbc1191a0a52ef", "b6ed21b99ca6f4f9f153e7b1beafed1d"); TestAES256( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "f69f2445df4f9b17ad2b417be66c3710", "23304b7a39f9f3ff067d8d8f9e24ecc7"); } BOOST_AUTO_TEST_CASE(aes_cbc_testvectors) { // NIST AES CBC 128-bit encryption test-vectors TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "000102030405060708090A0B0C0D0E0F", false, "6bc1bee22e409f96e93d7e117393172a", "7649abac8119b246cee98e9b12e9197d"); TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "7649ABAC8119B246CEE98E9B12E9197D", false, "ae2d8a571e03ac9c9eb76fac45af8e51", "5086cb9b507219ee95db113a917678b2"); TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "5086cb9b507219ee95db113a917678b2", false, "30c81c46a35ce411e5fbc1191a0a52ef", "73bed6b8e3c1743b7116e69e22229516"); TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "73bed6b8e3c1743b7116e69e22229516", false, "f69f2445df4f9b17ad2b417be66c3710", "3ff1caa1681fac09120eca307586e1a7"); // The same vectors with padding enabled TestAES128CBC( "2b7e151628aed2a6abf7158809cf4f3c", "000102030405060708090A0B0C0D0E0F", true, "6bc1bee22e409f96e93d7e117393172a", "7649abac8119b246cee98e9b12e9197d8964e0b149c10b7b682e6e39aaeb731c"); TestAES128CBC( "2b7e151628aed2a6abf7158809cf4f3c", "7649ABAC8119B246CEE98E9B12E9197D", true, "ae2d8a571e03ac9c9eb76fac45af8e51", "5086cb9b507219ee95db113a917678b255e21d7100b988ffec32feeafaf23538"); TestAES128CBC( "2b7e151628aed2a6abf7158809cf4f3c", "5086cb9b507219ee95db113a917678b2", true, "30c81c46a35ce411e5fbc1191a0a52ef", "73bed6b8e3c1743b7116e69e22229516f6eccda327bf8e5ec43718b0039adceb"); TestAES128CBC( "2b7e151628aed2a6abf7158809cf4f3c", "73bed6b8e3c1743b7116e69e22229516", true, "f69f2445df4f9b17ad2b417be66c3710", "3ff1caa1681fac09120eca307586e1a78cb82807230e1321d3fae00d18cc2012"); // NIST AES CBC 256-bit encryption test-vectors TestAES256CBC( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "000102030405060708090A0B0C0D0E0F", false, "6bc1bee22e409f96e93d7e117393172a", "f58c4c04d6e5f1ba779eabfb5f7bfbd6"); TestAES256CBC( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "F58C4C04D6E5F1BA779EABFB5F7BFBD6", false, "ae2d8a571e03ac9c9eb76fac45af8e51", "9cfc4e967edb808d679f777bc6702c7d"); TestAES256CBC( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "9CFC4E967EDB808D679F777BC6702C7D", false, "30c81c46a35ce411e5fbc1191a0a52ef", "39f23369a9d9bacfa530e26304231461"); TestAES256CBC( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "39F23369A9D9BACFA530E26304231461", false, "f69f2445df4f9b17ad2b417be66c3710", "b2eb05e2c39be9fcda6c19078c6a9d1b"); // The same vectors with padding enabled TestAES256CBC( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "000102030405060708090A0B0C0D0E0F", true, "6bc1bee22e409f96e93d7e117393172a", "f58c4c04d6e5f1ba779eabfb5f7bfbd6485a5c81519cf378fa36d42b8547edc0"); TestAES256CBC( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "F58C4C04D6E5F1BA779EABFB5F7BFBD6", true, "ae2d8a571e03ac9c9eb76fac45af8e51", "9cfc4e967edb808d679f777bc6702c7d3a3aa5e0213db1a9901f9036cf5102d2"); TestAES256CBC( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "9CFC4E967EDB808D679F777BC6702C7D", true, "30c81c46a35ce411e5fbc1191a0a52ef", "39f23369a9d9bacfa530e263042314612f8da707643c90a6f732b3de1d3f5cee"); TestAES256CBC( "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "39F23369A9D9BACFA530E26304231461", true, "f69f2445df4f9b17ad2b417be66c3710", "b2eb05e2c39be9fcda6c19078c6a9d1b3f461796d6b0d6b2e0c2a72b4d80e644"); } +BOOST_AUTO_TEST_CASE(chacha20_testvector) { + // Test vector from RFC 7539 + TestChaCha20( + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + 0x4a000000UL, 1, "224f51f3401bd9e12fde276fb8631ded8c131f823d2c06e27e4fc" + "aec9ef3cf788a3b0aa372600a92b57974cded2b9334794cba40c6" + "3e34cdea212c4cf07d41b769a6749f3f630f4122cafe28ec4dc47" + "e26d4346d70b98c73f3e9c53ac40c5945398b6eda1a832c89c167" + "eacd901d7e2bf363"); + + // Test vectors from + // https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7 + TestChaCha20( + "0000000000000000000000000000000000000000000000000000000000000000", 0, + 0, "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da4" + "1597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"); + TestChaCha20( + "0000000000000000000000000000000000000000000000000000000000000001", 0, + 0, "4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41bbe" + "2a0b6ea7566d2a5d1e7e20d42af2c53d792b1c43fea817e9ad275ae546963"); + TestChaCha20( + "0000000000000000000000000000000000000000000000000000000000000000", + 0x0100000000000000ULL, 0, "de9cba7bf3d69ef5e786dc63973f653a0b49e015adbf" + "f7134fcb7df137821031e85a050278a7084527214f73" + "efc7fa5b5277062eb7a0433e445f41e3"); + TestChaCha20( + "0000000000000000000000000000000000000000000000000000000000000000", 1, + 0, "ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111" + "e4caf237ee53ca8ad6426194a88545ddc497a0b466e7d6bbdb0041b2f586b"); + TestChaCha20( + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + 0x0706050403020100ULL, 0, + "f798a189f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a454" + "7b733b46413042c9440049176905d3be59ea1c53f15916155c2be8241a38008b9a26bc" + "35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc118be563e" + "b9b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a4750" + "32b63fc385245fe054e3dd5a97a5f576fe064025d3ce042c566ab2c507b138db853e3d" + "6959660996546cc9c4a6eafdc777c040d70eaf46f76dad3979e5c5360c3317166a1c89" + "4c94a371876a94df7628fe4eaaf2ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d" + "38407a6deb3ab78fab78c9"); +} + +BOOST_AUTO_TEST_CASE(countbits_tests) { + FastRandomContext ctx; + for (int i = 0; i <= 64; ++i) { + if (i == 0) { + // Check handling of zero. + BOOST_CHECK_EQUAL(CountBits(0), 0); + } else if (i < 10) { + for (uint64_t j = 1 << (i - 1); (j >> i) == 0; ++j) { + // Exhaustively test up to 10 bits + BOOST_CHECK_EQUAL(CountBits(j), i); + } + } else { + for (int k = 0; k < 1000; k++) { + // Randomly test 1000 samples of each length above 10 bits. + uint64_t j = uint64_t(1) << (i - 1) | ctx.randbits(i - 1); + BOOST_CHECK_EQUAL(CountBits(j), i); + } + } + } +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp index 888b6b3e3..acfb7839e 100644 --- a/src/test/prevector_tests.cpp +++ b/src/test/prevector_tests.cpp @@ -1,268 +1,268 @@ // Copyright (c) 2015-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "prevector.h" #include #include "serialize.h" #include "streams.h" #include "test/test_bitcoin.h" #include "test/test_random.h" #include #include BOOST_FIXTURE_TEST_SUITE(prevector_tests, TestingSetup) template class prevector_tester { typedef std::vector realtype; realtype real_vector; realtype real_vector_alt; typedef prevector pretype; pretype pre_vector; pretype pre_vector_alt; typedef typename pretype::size_type Size; bool passed = true; FastRandomContext rand_cache; + uint256 rand_seed; template void local_check_equal(A a, B b) { local_check(a == b); } void local_check(bool b) { passed &= b; } void test() { const pretype &const_pre_vector = pre_vector; local_check_equal(real_vector.size(), pre_vector.size()); local_check_equal(real_vector.empty(), pre_vector.empty()); for (Size s = 0; s < real_vector.size(); s++) { local_check(real_vector[s] == pre_vector[s]); local_check(&(pre_vector[s]) == &(pre_vector.begin()[s])); local_check(&(pre_vector[s]) == &*(pre_vector.begin() + s)); local_check(&(pre_vector[s]) == &*((pre_vector.end() + s) - real_vector.size())); } // local_check(realtype(pre_vector) == real_vector); local_check(pretype(real_vector.begin(), real_vector.end()) == pre_vector); local_check(pretype(pre_vector.begin(), pre_vector.end()) == pre_vector); size_t pos = 0; for (const T &v : pre_vector) { local_check(v == real_vector[pos++]); } // FIXME: For some reason, the prevector iterrator doesn't conform to // what boost::adaptors::reverse expect. for (const T &v : boost::adaptors::reverse(pre_vector)) { local_check(v == real_vector[--pos]); } for (const T &v : const_pre_vector) { local_check(v == real_vector[pos++]); } for (const T &v : boost::adaptors::reverse(const_pre_vector)) { local_check(v == real_vector[--pos]); } CDataStream ss1(SER_DISK, 0); CDataStream ss2(SER_DISK, 0); ss1 << real_vector; ss2 << pre_vector; local_check_equal(ss1.size(), ss2.size()); for (Size s = 0; s < ss1.size(); s++) { local_check_equal(ss1[s], ss2[s]); } } public: void resize(Size s) { real_vector.resize(s); local_check_equal(real_vector.size(), s); pre_vector.resize(s); local_check_equal(pre_vector.size(), s); test(); } void reserve(Size s) { real_vector.reserve(s); local_check(real_vector.capacity() >= s); pre_vector.reserve(s); local_check(pre_vector.capacity() >= s); test(); } void insert(Size position, const T &value) { real_vector.insert(real_vector.begin() + position, value); pre_vector.insert(pre_vector.begin() + position, value); test(); } void insert(Size position, Size count, const T &value) { real_vector.insert(real_vector.begin() + position, count, value); pre_vector.insert(pre_vector.begin() + position, count, value); test(); } template void insert_range(Size position, I first, I last) { real_vector.insert(real_vector.begin() + position, first, last); pre_vector.insert(pre_vector.begin() + position, first, last); test(); } void erase(Size position) { real_vector.erase(real_vector.begin() + position); pre_vector.erase(pre_vector.begin() + position); test(); } void erase(Size first, Size last) { real_vector.erase(real_vector.begin() + first, real_vector.begin() + last); pre_vector.erase(pre_vector.begin() + first, pre_vector.begin() + last); test(); } void update(Size pos, const T &value) { real_vector[pos] = value; pre_vector[pos] = value; test(); } void push_back(const T &value) { real_vector.push_back(value); pre_vector.push_back(value); test(); } void pop_back() { real_vector.pop_back(); pre_vector.pop_back(); test(); } void clear() { real_vector.clear(); pre_vector.clear(); } void assign(Size n, const T &value) { real_vector.assign(n, value); pre_vector.assign(n, value); } Size size() { return real_vector.size(); } Size capacity() { return pre_vector.capacity(); } void shrink_to_fit() { pre_vector.shrink_to_fit(); test(); } void swap() { real_vector.swap(real_vector_alt); pre_vector.swap(pre_vector_alt); test(); } void move() { real_vector = std::move(real_vector_alt); real_vector_alt.clear(); pre_vector = std::move(pre_vector_alt); pre_vector_alt.clear(); } void copy() { real_vector = real_vector_alt; pre_vector = pre_vector_alt; } ~prevector_tester() { - BOOST_CHECK_MESSAGE(passed, - "insecure_rand_Rz: " << rand_cache.Rz - << ", insecure_rand_Rw: " - << rand_cache.Rw); + BOOST_CHECK_MESSAGE(passed, "insecure_rand: " + rand_seed.ToString()); } + prevector_tester() { seed_insecure_rand(); + rand_seed = insecure_rand_seed; rand_cache = insecure_rand_ctx; } }; BOOST_AUTO_TEST_CASE(PrevectorTestInt) { for (int j = 0; j < 64; j++) { prevector_tester<8, int> test; for (int i = 0; i < 2048; i++) { int r = insecure_rand(); if ((r % 4) == 0) { test.insert(insecure_rand() % (test.size() + 1), insecure_rand()); } if (test.size() > 0 && ((r >> 2) % 4) == 1) { test.erase(insecure_rand() % test.size()); } if (((r >> 4) % 8) == 2) { int new_size = std::max( 0, std::min(30, test.size() + (insecure_rand() % 5) - 2)); test.resize(new_size); } if (((r >> 7) % 8) == 3) { test.insert(insecure_rand() % (test.size() + 1), 1 + (insecure_rand() % 2), insecure_rand()); } if (((r >> 10) % 8) == 4) { int del = std::min(test.size(), 1 + (insecure_rand() % 2)); int beg = insecure_rand() % (test.size() + 1 - del); test.erase(beg, beg + del); } if (((r >> 13) % 16) == 5) { test.push_back(insecure_rand()); } if (test.size() > 0 && ((r >> 17) % 16) == 6) { test.pop_back(); } if (((r >> 21) % 32) == 7) { int values[4]; int num = 1 + (insecure_rand() % 4); for (int k = 0; k < num; k++) { values[k] = insecure_rand(); } test.insert_range(insecure_rand() % (test.size() + 1), values, values + num); } if (((r >> 26) % 32) == 8) { int del = std::min(test.size(), 1 + (insecure_rand() % 4)); int beg = insecure_rand() % (test.size() + 1 - del); test.erase(beg, beg + del); } r = insecure_rand(); if (r % 32 == 9) { test.reserve(insecure_rand() % 32); } if ((r >> 5) % 64 == 10) { test.shrink_to_fit(); } if (test.size() > 0) { test.update(insecure_rand() % test.size(), insecure_rand()); } if (((r >> 11) % 1024) == 11) { test.clear(); } if (((r >> 21) % 512) == 12) { test.assign(insecure_rand() % 32, insecure_rand()); } if (((r >> 15) % 8) == 3) { test.swap(); } if (((r >> 15) % 16) == 8) { test.copy(); } if (((r >> 15) % 32) == 18) { test.move(); } } } } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp index 73f8cb6c0..bb4618f00 100644 --- a/src/test/random_tests.cpp +++ b/src/test/random_tests.cpp @@ -1,17 +1,51 @@ // Copyright (c) 2017 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "random.h" #include "test/test_bitcoin.h" #include BOOST_FIXTURE_TEST_SUITE(random_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(osrandom_tests) { BOOST_CHECK(Random_SanityCheck()); } +BOOST_AUTO_TEST_CASE(fastrandom_tests) { + // Check that deterministic FastRandomContexts are deterministic + FastRandomContext ctx1(true); + FastRandomContext ctx2(true); + + BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32()); + BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32()); + BOOST_CHECK_EQUAL(ctx1.rand64(), ctx2.rand64()); + BOOST_CHECK_EQUAL(ctx1.randbits(3), ctx2.randbits(3)); + BOOST_CHECK_EQUAL(ctx1.randbits(7), ctx2.randbits(7)); + BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32()); + BOOST_CHECK_EQUAL(ctx1.randbits(3), ctx2.randbits(3)); + + // Check that a nondeterministic ones are not + FastRandomContext ctx3; + FastRandomContext ctx4; + // extremely unlikely to be equal + BOOST_CHECK(ctx3.rand64() != ctx4.rand64()); +} + +BOOST_AUTO_TEST_CASE(fastrandom_randbits) { + FastRandomContext ctx1; + FastRandomContext ctx2; + for (int bits = 0; bits < 63; ++bits) { + for (int j = 0; j < 1000; ++j) { + uint64_t rangebits = ctx1.randbits(bits); + BOOST_CHECK_EQUAL(rangebits >> bits, 0); + uint64_t range = uint64_t(1) << bits | rangebits; + uint64_t rand = ctx2.randrange(range); + BOOST_CHECK(rand < range); + } + } +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 0dd4ce223..52ec47cb4 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -1,183 +1,184 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #define BOOST_TEST_MODULE Bitcoin Test Suite #include "test_bitcoin.h" #include "chainparams.h" #include "config.h" #include "consensus/consensus.h" #include "consensus/validation.h" #include "key.h" #include "miner.h" #include "net_processing.h" #include "pubkey.h" #include "random.h" #include "rpc/register.h" #include "rpc/server.h" #include "script/sigcache.h" #include "txdb.h" #include "txmempool.h" #include "ui_interface.h" #include "validation.h" #include "test/testutil.h" #include #include #include #include std::unique_ptr g_connman; -FastRandomContext insecure_rand_ctx(true); +uint256 insecure_rand_seed = GetRandHash(); +FastRandomContext insecure_rand_ctx(insecure_rand_seed); extern bool fPrintToConsole; extern void noui_connect(); BasicTestingSetup::BasicTestingSetup(const std::string &chainName) { ECC_Start(); SetupEnvironment(); SetupNetworking(); InitSignatureCache(); // Don't want to write to debug.log file. fPrintToDebugLog = false; fCheckBlockIndex = true; SelectParams(chainName); noui_connect(); // Set config parameters to default. GlobalConfig config; config.SetMaxBlockSize(DEFAULT_MAX_BLOCK_SIZE); } BasicTestingSetup::~BasicTestingSetup() { ECC_Stop(); g_connman.reset(); } TestingSetup::TestingSetup(const std::string &chainName) : BasicTestingSetup(chainName) { // Ideally we'd move all the RPC tests to the functional testing framework // instead of unit tests, but for now we need these here. const Config &config = GetConfig(); RegisterAllRPCCommands(tableRPC); ClearDatadirCache(); pathTemp = GetTempPath() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(100000))); boost::filesystem::create_directories(pathTemp); ForceSetArg("-datadir", pathTemp.string()); mempool.setSanityCheck(1.0); pblocktree = new CBlockTreeDB(1 << 20, true); pcoinsdbview = new CCoinsViewDB(1 << 23, true); pcoinsTip = new CCoinsViewCache(pcoinsdbview); InitBlockIndex(config); { CValidationState state; bool ok = ActivateBestChain(config, state); BOOST_CHECK(ok); } nScriptCheckThreads = 3; for (int i = 0; i < nScriptCheckThreads - 1; i++) { threadGroup.create_thread(&ThreadScriptCheck); } // Deterministic randomness for tests. g_connman = std::unique_ptr(new CConnman(config, 0x1337, 0x1337)); connman = g_connman.get(); RegisterNodeSignals(GetNodeSignals()); } TestingSetup::~TestingSetup() { UnregisterNodeSignals(GetNodeSignals()); threadGroup.interrupt_all(); threadGroup.join_all(); UnloadBlockIndex(); delete pcoinsTip; delete pcoinsdbview; delete pblocktree; boost::filesystem::remove_all(pathTemp); } TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST) { // Generate a 100-block chain: coinbaseKey.MakeNewKey(true); CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; for (int i = 0; i < COINBASE_MATURITY; i++) { std::vector noTxns; CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey); coinbaseTxns.push_back(*b.vtx[0]); } } // // Create a new block with just given transactions, coinbase paying to // scriptPubKey, and try to add it to the current chain. // CBlock TestChain100Setup::CreateAndProcessBlock( const std::vector &txns, const CScript &scriptPubKey) { const CChainParams &chainparams = Params(); const Config &config = GetConfig(); std::unique_ptr pblocktemplate = BlockAssembler(config, chainparams).CreateNewBlock(scriptPubKey); CBlock &block = pblocktemplate->block; // Replace mempool-selected txns with just coinbase plus passed-in txns: block.vtx.resize(1); for (const CMutableTransaction &tx : txns) { block.vtx.push_back(MakeTransactionRef(tx)); } // IncrementExtraNonce creates a valid coinbase and merkleRoot unsigned int extraNonce = 0; IncrementExtraNonce(config, &block, chainActive.Tip(), extraNonce); while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) { ++block.nNonce; } std::shared_ptr shared_pblock = std::make_shared(block); ProcessNewBlock(GetConfig(), shared_pblock, true, nullptr); CBlock result = block; return result; } TestChain100Setup::~TestChain100Setup() {} CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx, CTxMemPool *pool) { CTransaction txn(tx); return FromTx(txn, pool); } CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransaction &txn, CTxMemPool *pool) { // Hack to assume either it's completely dependent on other mempool txs or // not at all. CAmount inChainValue = pool && pool->HasNoInputsOf(txn) ? txn.GetValueOut() : 0; return CTxMemPoolEntry(MakeTransactionRef(txn), nFee, nTime, dPriority, nHeight, inChainValue, spendsCoinbase, sigOpCost, lp); } void Shutdown(void *parg) { exit(0); } void StartShutdown() { exit(0); } bool ShutdownRequested() { return false; } diff --git a/src/test/test_random.h b/src/test/test_random.h index 9532221d9..551b5d4bb 100644 --- a/src/test/test_random.h +++ b/src/test/test_random.h @@ -1,21 +1,27 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-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. #ifndef BITCOIN_TEST_RANDOM_H #define BITCOIN_TEST_RANDOM_H #include "random.h" +extern uint256 insecure_rand_seed; extern FastRandomContext insecure_rand_ctx; static inline void seed_insecure_rand(bool fDeterministic = false) { - insecure_rand_ctx = FastRandomContext(fDeterministic); + if (fDeterministic) { + insecure_rand_seed = uint256(); + } else { + insecure_rand_seed = GetRandHash(); + } + insecure_rand_ctx = FastRandomContext(insecure_rand_seed); } static inline uint32_t insecure_rand(void) { return insecure_rand_ctx.rand32(); } #endif diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index ceef78e9b..9b5187870 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1,4483 +1,4483 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "wallet/wallet.h" #include "base58.h" #include "chain.h" #include "checkpoints.h" #include "config.h" #include "consensus/consensus.h" #include "consensus/validation.h" #include "key.h" #include "keystore.h" #include "net.h" #include "policy/policy.h" #include "primitives/block.h" #include "primitives/transaction.h" #include "script/script.h" #include "script/sign.h" #include "timedata.h" #include "txmempool.h" #include "ui_interface.h" #include "util.h" #include "utilmoneystr.h" #include "validation.h" #include "wallet/coincontrol.h" #include "wallet/finaltx.h" #include #include #include #include CWallet *pwalletMain = nullptr; /** Transaction fee set by the user */ CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE); unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET; bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE; bool fSendFreeTransactions = DEFAULT_SEND_FREE_TRANSACTIONS; const char *DEFAULT_WALLET_DAT = "wallet.dat"; const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; /** * Fees smaller than this (in satoshi) are considered zero fee (for transaction * creation) * Override with -mintxfee */ CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE); /** * If fee estimation does not have enough data to provide estimates, use this * fee instead. Has no effect if not using fee estimation. * Override with -fallbackfee */ CFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE); const uint256 CMerkleTx::ABANDON_HASH(uint256S( "0000000000000000000000000000000000000000000000000000000000000001")); /** @defgroup mapWallet * * @{ */ struct CompareValueOnly { bool operator()( const std::pair> &t1, const std::pair> &t2) const { return t1.first < t2.first; } }; std::string COutput::ToString() const { return strprintf("COutput(%s, %d, %d) [%s]", tx->GetId().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue)); } const CWalletTx *CWallet::GetWalletTx(const uint256 &hash) const { LOCK(cs_wallet); std::map::const_iterator it = mapWallet.find(hash); if (it == mapWallet.end()) { return nullptr; } return &(it->second); } CPubKey CWallet::GenerateNewKey() { // mapKeyMetadata AssertLockHeld(cs_wallet); // default to compressed public keys if we want 0.6.0 wallets bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); CKey secret; // Create new metadata int64_t nCreationTime = GetTime(); CKeyMetadata metadata(nCreationTime); // use HD key derivation if HD was enabled during wallet creation if (IsHDEnabled()) { DeriveNewChildKey(metadata, secret); } else { secret.MakeNewKey(fCompressed); } // Compressed public keys were introduced in version 0.6.0 if (fCompressed) { SetMinVersion(FEATURE_COMPRPUBKEY); } CPubKey pubkey = secret.GetPubKey(); assert(secret.VerifyPubKey(pubkey)); mapKeyMetadata[pubkey.GetID()] = metadata; UpdateTimeFirstKey(nCreationTime); if (!AddKeyPubKey(secret, pubkey)) { throw std::runtime_error(std::string(__func__) + ": AddKey failed"); } return pubkey; } void CWallet::DeriveNewChildKey(CKeyMetadata &metadata, CKey &secret) { // for now we use a fixed keypath scheme of m/0'/0'/k // master key seed (256bit) CKey key; // hd master key CExtKey masterKey; // key at m/0' CExtKey accountKey; // key at m/0'/0' CExtKey externalChainChildKey; // key at m/0'/0'/' CExtKey childKey; // try to get the master key if (!GetKey(hdChain.masterKeyID, key)) { throw std::runtime_error(std::string(__func__) + ": Master key not found"); } masterKey.SetMaster(key.begin(), key.size()); // derive m/0' // use hardened derivation (child keys >= 0x80000000 are hardened after // bip32) masterKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT); // derive m/0'/0' accountKey.Derive(externalChainChildKey, BIP32_HARDENED_KEY_LIMIT); // derive child key at next index, skip keys already known to the wallet do { // always derive hardened keys // childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened // child-index-range // example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649 externalChainChildKey.Derive(childKey, hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT); metadata.hdKeypath = "m/0'/0'/" + std::to_string(hdChain.nExternalChainCounter) + "'"; metadata.hdMasterKeyID = hdChain.masterKeyID; // increment childkey index hdChain.nExternalChainCounter++; } while (HaveKey(childKey.key.GetPubKey().GetID())); secret = childKey.key; // update the chain model in the database if (!CWalletDB(strWalletFile).WriteHDChain(hdChain)) { throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed"); } } bool CWallet::AddKeyPubKey(const CKey &secret, const CPubKey &pubkey) { // mapKeyMetadata AssertLockHeld(cs_wallet); if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) { return false; } // Check if we need to remove from watch-only. CScript script; script = GetScriptForDestination(pubkey.GetID()); if (HaveWatchOnly(script)) { RemoveWatchOnly(script); } script = GetScriptForRawPubKey(pubkey); if (HaveWatchOnly(script)) { RemoveWatchOnly(script); } if (!fFileBacked) { return true; } if (IsCrypted()) { return true; } return CWalletDB(strWalletFile) .WriteKey(pubkey, secret.GetPrivKey(), mapKeyMetadata[pubkey.GetID()]); } bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret) { if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) { return false; } if (!fFileBacked) { return true; } LOCK(cs_wallet); if (pwalletdbEncryption) { return pwalletdbEncryption->WriteCryptedKey( vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); } return CWalletDB(strWalletFile) .WriteCryptedKey(vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); } bool CWallet::LoadKeyMetadata(const CTxDestination &keyID, const CKeyMetadata &meta) { // mapKeyMetadata AssertLockHeld(cs_wallet); UpdateTimeFirstKey(meta.nCreateTime); mapKeyMetadata[keyID] = meta; return true; } bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret) { return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); } void CWallet::UpdateTimeFirstKey(int64_t nCreateTime) { AssertLockHeld(cs_wallet); if (nCreateTime <= 1) { // Cannot determine birthday information, so set the wallet birthday to // the beginning of time. nTimeFirstKey = 1; } else if (!nTimeFirstKey || nCreateTime < nTimeFirstKey) { nTimeFirstKey = nCreateTime; } } bool CWallet::AddCScript(const CScript &redeemScript) { if (!CCryptoKeyStore::AddCScript(redeemScript)) { return false; } if (!fFileBacked) { return true; } return CWalletDB(strWalletFile) .WriteCScript(Hash160(redeemScript), redeemScript); } bool CWallet::LoadCScript(const CScript &redeemScript) { /** * A sanity check was added in pull #3843 to avoid adding redeemScripts that * never can be redeemed. However, old wallets may still contain these. Do * not add them to the wallet and warn. */ if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) { std::string strAddr = CBitcoinAddress(CScriptID(redeemScript)).ToString(); LogPrintf("%s: Warning: This wallet contains a redeemScript of size %i " "which exceeds maximum size %i thus can never be redeemed. " "Do not use address %s.\n", __func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr); return true; } return CCryptoKeyStore::AddCScript(redeemScript); } bool CWallet::AddWatchOnly(const CScript &dest) { if (!CCryptoKeyStore::AddWatchOnly(dest)) { return false; } const CKeyMetadata &meta = mapKeyMetadata[CScriptID(dest)]; UpdateTimeFirstKey(meta.nCreateTime); NotifyWatchonlyChanged(true); if (!fFileBacked) { return true; } return CWalletDB(strWalletFile).WriteWatchOnly(dest, meta); } bool CWallet::AddWatchOnly(const CScript &dest, int64_t nCreateTime) { mapKeyMetadata[CScriptID(dest)].nCreateTime = nCreateTime; return AddWatchOnly(dest); } bool CWallet::RemoveWatchOnly(const CScript &dest) { AssertLockHeld(cs_wallet); if (!CCryptoKeyStore::RemoveWatchOnly(dest)) { return false; } if (!HaveWatchOnly()) { NotifyWatchonlyChanged(false); } if (fFileBacked && !CWalletDB(strWalletFile).EraseWatchOnly(dest)) { return false; } return true; } bool CWallet::LoadWatchOnly(const CScript &dest) { return CCryptoKeyStore::AddWatchOnly(dest); } bool CWallet::Unlock(const SecureString &strWalletPassphrase) { CCrypter crypter; CKeyingMaterial vMasterKey; LOCK(cs_wallet); for (const MasterKeyMap::value_type &pMasterKey : mapMasterKeys) { if (!crypter.SetKeyFromPassphrase( strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) { return false; } if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) { // try another master key continue; } if (CCryptoKeyStore::Unlock(vMasterKey)) { return true; } } return false; } bool CWallet::ChangeWalletPassphrase( const SecureString &strOldWalletPassphrase, const SecureString &strNewWalletPassphrase) { bool fWasLocked = IsLocked(); LOCK(cs_wallet); Lock(); CCrypter crypter; CKeyingMaterial vMasterKey; for (MasterKeyMap::value_type &pMasterKey : mapMasterKeys) { if (!crypter.SetKeyFromPassphrase( strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) { return false; } if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) { return false; } if (CCryptoKeyStore::Unlock(vMasterKey)) { int64_t nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime))); nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * 100 / double(GetTimeMillis() - nStartTime)) / 2; if (pMasterKey.second.nDeriveIterations < 25000) { pMasterKey.second.nDeriveIterations = 25000; } LogPrintf( "Wallet passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations); if (!crypter.SetKeyFromPassphrase( strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) { return false; } if (!crypter.Encrypt(vMasterKey, pMasterKey.second.vchCryptedKey)) { return false; } CWalletDB(strWalletFile) .WriteMasterKey(pMasterKey.first, pMasterKey.second); if (fWasLocked) { Lock(); } return true; } } return false; } void CWallet::SetBestChain(const CBlockLocator &loc) { CWalletDB walletdb(strWalletFile); walletdb.WriteBestBlock(loc); } bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB *pwalletdbIn, bool fExplicit) { // nWalletVersion LOCK(cs_wallet); if (nWalletVersion >= nVersion) { return true; } // When doing an explicit upgrade, if we pass the max version permitted, // upgrade all the way. if (fExplicit && nVersion > nWalletMaxVersion) { nVersion = FEATURE_LATEST; } nWalletVersion = nVersion; if (nVersion > nWalletMaxVersion) { nWalletMaxVersion = nVersion; } if (fFileBacked) { CWalletDB *pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile); if (nWalletVersion > 40000) { pwalletdb->WriteMinVersion(nWalletVersion); } if (!pwalletdbIn) { delete pwalletdb; } } return true; } bool CWallet::SetMaxVersion(int nVersion) { // nWalletVersion, nWalletMaxVersion LOCK(cs_wallet); // Cannot downgrade below current version if (nWalletVersion > nVersion) { return false; } nWalletMaxVersion = nVersion; return true; } std::set CWallet::GetConflicts(const uint256 &txid) const { std::set result; AssertLockHeld(cs_wallet); std::map::const_iterator it = mapWallet.find(txid); if (it == mapWallet.end()) { return result; } const CWalletTx &wtx = it->second; std::pair range; for (const CTxIn &txin : wtx.tx->vin) { if (mapTxSpends.count(txin.prevout) <= 1) { // No conflict if zero or one spends. continue; } range = mapTxSpends.equal_range(txin.prevout); for (TxSpends::const_iterator _it = range.first; _it != range.second; ++_it) { result.insert(_it->second); } } return result; } bool CWallet::HasWalletSpend(const uint256 &txid) const { AssertLockHeld(cs_wallet); auto iter = mapTxSpends.lower_bound(COutPoint(txid, 0)); return (iter != mapTxSpends.end() && iter->first.hash == txid); } void CWallet::Flush(bool shutdown) { bitdb.Flush(shutdown); } bool CWallet::Verify() { if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { return true; } LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0)); std::string walletFile = GetArg("-wallet", DEFAULT_WALLET_DAT); LogPrintf("Using wallet %s\n", walletFile); uiInterface.InitMessage(_("Verifying wallet...")); // Wallet file must be a plain filename without a directory. if (walletFile != boost::filesystem::basename(walletFile) + boost::filesystem::extension(walletFile)) { return InitError( strprintf(_("Wallet %s resides outside data directory %s"), walletFile, GetDataDir().string())); } if (!bitdb.Open(GetDataDir())) { // Try moving the database env out of the way. boost::filesystem::path pathDatabase = GetDataDir() / "database"; boost::filesystem::path pathDatabaseBak = GetDataDir() / strprintf("database.%d.bak", GetTime()); try { boost::filesystem::rename(pathDatabase, pathDatabaseBak); LogPrintf("Moved old %s to %s. Retrying.\n", pathDatabase.string(), pathDatabaseBak.string()); } catch (const boost::filesystem::filesystem_error &) { // Failure is ok (well, not really, but it's not worse than what we // started with) } // try again if (!bitdb.Open(GetDataDir())) { // If it still fails, it probably means we can't even create the // database env. return InitError(strprintf( _("Error initializing wallet database environment %s!"), GetDataDir())); } } if (GetBoolArg("-salvagewallet", false)) { // Recover readable keypairs: if (!CWalletDB::Recover(bitdb, walletFile, true)) return false; } if (boost::filesystem::exists(GetDataDir() / walletFile)) { CDBEnv::VerifyResult r = bitdb.Verify(walletFile, CWalletDB::Recover); if (r == CDBEnv::RECOVER_OK) { InitWarning(strprintf( _("Warning: Wallet file corrupt, data salvaged!" " Original %s saved as %s in %s; if" " your balance or transactions are incorrect you should" " restore from a backup."), walletFile, "wallet.{timestamp}.bak", GetDataDir())); } if (r == CDBEnv::RECOVER_FAIL) { return InitError( strprintf(_("%s corrupt, salvage failed"), walletFile)); } } return true; } void CWallet::SyncMetaData( std::pair range) { // We want all the wallet transactions in range to have the same metadata as // the oldest (smallest nOrderPos). // So: find smallest nOrderPos: int nMinOrderPos = std::numeric_limits::max(); const CWalletTx *copyFrom = nullptr; for (TxSpends::iterator it = range.first; it != range.second; ++it) { const uint256 &hash = it->second; int n = mapWallet[hash].nOrderPos; if (n < nMinOrderPos) { nMinOrderPos = n; copyFrom = &mapWallet[hash]; } } // Now copy data from copyFrom to rest: for (TxSpends::iterator it = range.first; it != range.second; ++it) { const uint256 &hash = it->second; CWalletTx *copyTo = &mapWallet[hash]; if (copyFrom == copyTo) { continue; } if (!copyFrom->IsEquivalentTo(*copyTo)) { continue; } copyTo->mapValue = copyFrom->mapValue; copyTo->vOrderForm = copyFrom->vOrderForm; // fTimeReceivedIsTxTime not copied on purpose nTimeReceived not copied // on purpose. copyTo->nTimeSmart = copyFrom->nTimeSmart; copyTo->fFromMe = copyFrom->fFromMe; copyTo->strFromAccount = copyFrom->strFromAccount; // nOrderPos not copied on purpose cached members not copied on purpose. } } /** * Outpoint is spent if any non-conflicted transaction, spends it: */ bool CWallet::IsSpent(const uint256 &hash, unsigned int n) const { const COutPoint outpoint(hash, n); std::pair range; range = mapTxSpends.equal_range(outpoint); for (TxSpends::const_iterator it = range.first; it != range.second; ++it) { const uint256 &wtxid = it->second; std::map::const_iterator mit = mapWallet.find(wtxid); if (mit != mapWallet.end()) { int depth = mit->second.GetDepthInMainChain(); if (depth > 0 || (depth == 0 && !mit->second.isAbandoned())) { // Spent return true; } } } return false; } void CWallet::AddToSpends(const COutPoint &outpoint, const uint256 &wtxid) { mapTxSpends.insert(std::make_pair(outpoint, wtxid)); std::pair range; range = mapTxSpends.equal_range(outpoint); SyncMetaData(range); } void CWallet::AddToSpends(const uint256 &wtxid) { assert(mapWallet.count(wtxid)); CWalletTx &thisTx = mapWallet[wtxid]; // Coinbases don't spend anything! if (thisTx.IsCoinBase()) { return; } for (const CTxIn &txin : thisTx.tx->vin) { AddToSpends(txin.prevout, wtxid); } } bool CWallet::EncryptWallet(const SecureString &strWalletPassphrase) { if (IsCrypted()) { return false; } CKeyingMaterial vMasterKey; vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE); GetStrongRandBytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE); CMasterKey kMasterKey; kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE); GetStrongRandBytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE); CCrypter crypter; int64_t nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod); kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime)); nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod); kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; if (kMasterKey.nDeriveIterations < 25000) { kMasterKey.nDeriveIterations = 25000; } LogPrintf("Encrypting Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations); if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod)) { return false; } if (!crypter.Encrypt(vMasterKey, kMasterKey.vchCryptedKey)) { return false; } { LOCK(cs_wallet); mapMasterKeys[++nMasterKeyMaxID] = kMasterKey; if (fFileBacked) { assert(!pwalletdbEncryption); pwalletdbEncryption = new CWalletDB(strWalletFile); if (!pwalletdbEncryption->TxnBegin()) { delete pwalletdbEncryption; pwalletdbEncryption = nullptr; return false; } pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey); } if (!EncryptKeys(vMasterKey)) { if (fFileBacked) { pwalletdbEncryption->TxnAbort(); delete pwalletdbEncryption; } // We now probably have half of our keys encrypted in memory, and // half not... die and let the user reload the unencrypted wallet. assert(false); } // Encryption was introduced in version 0.4.0 SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true); if (fFileBacked) { if (!pwalletdbEncryption->TxnCommit()) { delete pwalletdbEncryption; // We now have keys encrypted in memory, but not on disk... die // to avoid confusion and let the user reload the unencrypted // wallet. assert(false); } delete pwalletdbEncryption; pwalletdbEncryption = nullptr; } Lock(); Unlock(strWalletPassphrase); // If we are using HD, replace the HD master key (seed) with a new one. if (IsHDEnabled()) { CKey key; CPubKey masterPubKey = GenerateNewHDMasterKey(); if (!SetHDMasterKey(masterPubKey)) { return false; } } NewKeyPool(); Lock(); // Need to completely rewrite the wallet file; if we don't, bdb might // keep bits of the unencrypted private key in slack space in the // database file. CDB::Rewrite(strWalletFile); } NotifyStatusChanged(this); return true; } DBErrors CWallet::ReorderTransactions() { LOCK(cs_wallet); CWalletDB walletdb(strWalletFile); // Old wallets didn't have any defined order for transactions. Probably a // bad idea to change the output of this. // First: get all CWalletTx and CAccountingEntry into a sorted-by-time // multimap. typedef std::pair TxPair; typedef std::multimap TxItems; TxItems txByTime; for (std::map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { CWalletTx *wtx = &((*it).second); txByTime.insert( std::make_pair(wtx->nTimeReceived, TxPair(wtx, nullptr))); } std::list acentries; walletdb.ListAccountCreditDebit("", acentries); for (CAccountingEntry &entry : acentries) { txByTime.insert(std::make_pair(entry.nTime, TxPair(nullptr, &entry))); } nOrderPosNext = 0; std::vector nOrderPosOffsets; for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it) { CWalletTx *const pwtx = (*it).second.first; CAccountingEntry *const pacentry = (*it).second.second; int64_t &nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos; if (nOrderPos == -1) { nOrderPos = nOrderPosNext++; nOrderPosOffsets.push_back(nOrderPos); if (pwtx) { if (!walletdb.WriteTx(*pwtx)) { return DB_LOAD_FAIL; } } else if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) { return DB_LOAD_FAIL; } } else { int64_t nOrderPosOff = 0; for (const int64_t &nOffsetStart : nOrderPosOffsets) { if (nOrderPos >= nOffsetStart) { ++nOrderPosOff; } } nOrderPos += nOrderPosOff; nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1); if (!nOrderPosOff) { continue; } // Since we're changing the order, write it back. if (pwtx) { if (!walletdb.WriteTx(*pwtx)) { return DB_LOAD_FAIL; } } else if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) { return DB_LOAD_FAIL; } } } walletdb.WriteOrderPosNext(nOrderPosNext); return DB_LOAD_OK; } int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb) { // nOrderPosNext AssertLockHeld(cs_wallet); int64_t nRet = nOrderPosNext++; if (pwalletdb) { pwalletdb->WriteOrderPosNext(nOrderPosNext); } else { CWalletDB(strWalletFile).WriteOrderPosNext(nOrderPosNext); } return nRet; } bool CWallet::AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment) { CWalletDB walletdb(strWalletFile); if (!walletdb.TxnBegin()) { return false; } int64_t nNow = GetAdjustedTime(); // Debit CAccountingEntry debit; debit.nOrderPos = IncOrderPosNext(&walletdb); debit.strAccount = strFrom; debit.nCreditDebit = -nAmount; debit.nTime = nNow; debit.strOtherAccount = strTo; debit.strComment = strComment; AddAccountingEntry(debit, &walletdb); // Credit CAccountingEntry credit; credit.nOrderPos = IncOrderPosNext(&walletdb); credit.strAccount = strTo; credit.nCreditDebit = nAmount; credit.nTime = nNow; credit.strOtherAccount = strFrom; credit.strComment = strComment; AddAccountingEntry(credit, &walletdb); if (!walletdb.TxnCommit()) { return false; } return true; } bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bForceNew) { CWalletDB walletdb(strWalletFile); CAccount account; walletdb.ReadAccount(strAccount, account); if (!bForceNew) { if (!account.vchPubKey.IsValid()) { bForceNew = true; } else { // Check if the current key has been used. CScript scriptPubKey = GetScriptForDestination(account.vchPubKey.GetID()); for (std::map::iterator it = mapWallet.begin(); it != mapWallet.end() && account.vchPubKey.IsValid(); ++it) { for (const CTxOut &txout : (*it).second.tx->vout) { if (txout.scriptPubKey == scriptPubKey) { bForceNew = true; break; } } } } } // Generate a new key if (bForceNew) { if (!GetKeyFromPool(account.vchPubKey)) { return false; } SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive"); walletdb.WriteAccount(strAccount, account); } pubKey = account.vchPubKey; return true; } void CWallet::MarkDirty() { LOCK(cs_wallet); for (std::pair &item : mapWallet) { item.second.MarkDirty(); } } bool CWallet::MarkReplaced(const uint256 &originalHash, const uint256 &newHash) { LOCK(cs_wallet); auto mi = mapWallet.find(originalHash); // There is a bug if MarkReplaced is not called on an existing wallet // transaction. assert(mi != mapWallet.end()); CWalletTx &wtx = (*mi).second; // Ensure for now that we're not overwriting data. assert(wtx.mapValue.count("replaced_by_txid") == 0); wtx.mapValue["replaced_by_txid"] = newHash.ToString(); CWalletDB walletdb(strWalletFile, "r+"); bool success = true; if (!walletdb.WriteTx(wtx)) { LogPrintf("%s: Updating walletdb tx %s failed", __func__, wtx.GetId().ToString()); success = false; } NotifyTransactionChanged(this, originalHash, CT_UPDATED); return success; } bool CWallet::AddToWallet(const CWalletTx &wtxIn, bool fFlushOnClose) { LOCK(cs_wallet); CWalletDB walletdb(strWalletFile, "r+", fFlushOnClose); uint256 hash = wtxIn.GetId(); // Inserts only if not already there, returns tx inserted or tx found. std::pair::iterator, bool> ret = mapWallet.insert(std::make_pair(hash, wtxIn)); CWalletTx &wtx = (*ret.first).second; wtx.BindWallet(this); bool fInsertedNew = ret.second; if (fInsertedNew) { wtx.nTimeReceived = GetAdjustedTime(); wtx.nOrderPos = IncOrderPosNext(&walletdb); wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr))); wtx.nTimeSmart = wtx.nTimeReceived; if (!wtxIn.hashUnset()) { if (mapBlockIndex.count(wtxIn.hashBlock)) { int64_t latestNow = wtx.nTimeReceived; int64_t latestEntry = 0; { // Tolerate times up to the last timestamp in the wallet not // more than 5 minutes into the future. int64_t latestTolerated = latestNow + 300; const TxItems &txOrdered = wtxOrdered; for (TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) { CWalletTx *const pwtx = (*it).second.first; if (pwtx == &wtx) { continue; } CAccountingEntry *const pacentry = (*it).second.second; int64_t nSmartTime; if (pwtx) { nSmartTime = pwtx->nTimeSmart; if (!nSmartTime) { nSmartTime = pwtx->nTimeReceived; } } else { nSmartTime = pacentry->nTime; } if (nSmartTime <= latestTolerated) { latestEntry = nSmartTime; if (nSmartTime > latestNow) { latestNow = nSmartTime; } break; } } } int64_t blocktime = mapBlockIndex[wtxIn.hashBlock]->GetBlockTime(); wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow)); } else { LogPrintf("AddToWallet(): found %s in block %s not in index\n", wtxIn.GetId().ToString(), wtxIn.hashBlock.ToString()); } } AddToSpends(hash); } bool fUpdated = false; if (!fInsertedNew) { // Merge if (!wtxIn.hashUnset() && wtxIn.hashBlock != wtx.hashBlock) { wtx.hashBlock = wtxIn.hashBlock; fUpdated = true; } // If no longer abandoned, update if (wtxIn.hashBlock.IsNull() && wtx.isAbandoned()) { wtx.hashBlock = wtxIn.hashBlock; fUpdated = true; } if (wtxIn.nIndex != -1 && (wtxIn.nIndex != wtx.nIndex)) { wtx.nIndex = wtxIn.nIndex; fUpdated = true; } if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) { wtx.fFromMe = wtxIn.fFromMe; fUpdated = true; } } //// debug print LogPrintf("AddToWallet %s %s%s\n", wtxIn.GetId().ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); // Write to disk if ((fInsertedNew || fUpdated) && !walletdb.WriteTx(wtx)) { return false; } // Break debit/credit balance caches: wtx.MarkDirty(); // Notify UI of new or updated transaction. NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); // Notify an external script when a wallet transaction comes in or is // updated. std::string strCmd = GetArg("-walletnotify", ""); if (!strCmd.empty()) { boost::replace_all(strCmd, "%s", wtxIn.GetId().GetHex()); // Thread runs free. boost::thread t(runCommand, strCmd); } return true; } bool CWallet::LoadToWallet(const CWalletTx &wtxIn) { uint256 txid = wtxIn.GetId(); mapWallet[txid] = wtxIn; CWalletTx &wtx = mapWallet[txid]; wtx.BindWallet(this); wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr))); AddToSpends(txid); for (const CTxIn &txin : wtx.tx->vin) { if (mapWallet.count(txin.prevout.hash)) { CWalletTx &prevtx = mapWallet[txin.prevout.hash]; if (prevtx.nIndex == -1 && !prevtx.hashUnset()) { MarkConflicted(prevtx.hashBlock, wtx.GetId()); } } } return true; } /** * Add a transaction to the wallet, or update it. pIndex and posInBlock should * be set when the transaction was known to be included in a block. When * posInBlock = SYNC_TRANSACTION_NOT_IN_BLOCK (-1), then wallet state is not * updated in AddToWallet, but notifications happen and cached balances are * marked dirty. If fUpdate is true, existing transactions will be updated. * * TODO: One exception to this is that the abandoned state is cleared under the * assumption that any further notification of a transaction that was considered * abandoned is an indication that it is not safe to be considered abandoned. * Abandoned state should probably be more carefuly tracked via different * posInBlock signals or by checking mempool presence when necessary. */ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction &tx, const CBlockIndex *pIndex, int posInBlock, bool fUpdate) { AssertLockHeld(cs_wallet); if (posInBlock != -1) { for (const CTxIn &txin : tx.vin) { std::pair range = mapTxSpends.equal_range(txin.prevout); while (range.first != range.second) { if (range.first->second != tx.GetId()) { LogPrintf("Transaction %s (in block %s) conflicts with " "wallet transaction %s (both spend %s:%i)\n", tx.GetId().ToString(), pIndex->GetBlockHash().ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n); MarkConflicted(pIndex->GetBlockHash(), range.first->second); } range.first++; } } } bool fExisted = mapWallet.count(tx.GetId()) != 0; if (fExisted && !fUpdate) { return false; } if (fExisted || IsMine(tx) || IsFromMe(tx)) { CWalletTx wtx(this, MakeTransactionRef(tx)); // Get merkle branch if transaction was found in a block. if (posInBlock != -1) { wtx.SetMerkleBranch(pIndex, posInBlock); } return AddToWallet(wtx, false); } return false; } bool CWallet::AbandonTransaction(const uint256 &hashTx) { LOCK2(cs_main, cs_wallet); CWalletDB walletdb(strWalletFile, "r+"); std::set todo; std::set done; // Can't mark abandoned if confirmed or in mempool. assert(mapWallet.count(hashTx)); CWalletTx &origtx = mapWallet[hashTx]; if (origtx.GetDepthInMainChain() > 0 || origtx.InMempool()) { return false; } todo.insert(hashTx); while (!todo.empty()) { uint256 now = *todo.begin(); todo.erase(now); done.insert(now); assert(mapWallet.count(now)); CWalletTx &wtx = mapWallet[now]; int currentconfirm = wtx.GetDepthInMainChain(); // If the orig tx was not in block, none of its spends can be. assert(currentconfirm <= 0); // If (currentconfirm < 0) {Tx and spends are already conflicted, no // need to abandon} if (currentconfirm == 0 && !wtx.isAbandoned()) { // If the orig tx was not in block/mempool, none of its spends can // be in mempool. assert(!wtx.InMempool()); wtx.nIndex = -1; wtx.setAbandoned(); wtx.MarkDirty(); walletdb.WriteTx(wtx); NotifyTransactionChanged(this, wtx.GetId(), CT_UPDATED); // Iterate over all its outputs, and mark transactions in the wallet // that spend them abandoned too. TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(hashTx, 0)); while (iter != mapTxSpends.end() && iter->first.hash == now) { if (!done.count(iter->second)) { todo.insert(iter->second); } iter++; } // If a transaction changes 'conflicted' state, that changes the // balance available of the outputs it spends. So force those to be // recomputed. for (const CTxIn &txin : wtx.tx->vin) { if (mapWallet.count(txin.prevout.hash)) mapWallet[txin.prevout.hash].MarkDirty(); } } } return true; } void CWallet::MarkConflicted(const uint256 &hashBlock, const uint256 &hashTx) { LOCK2(cs_main, cs_wallet); int conflictconfirms = 0; if (mapBlockIndex.count(hashBlock)) { CBlockIndex *pindex = mapBlockIndex[hashBlock]; if (chainActive.Contains(pindex)) { conflictconfirms = -(chainActive.Height() - pindex->nHeight + 1); } } // If number of conflict confirms cannot be determined, this means that the // block is still unknown or not yet part of the main chain, for example // when loading the wallet during a reindex. Do nothing in that case. if (conflictconfirms >= 0) { return; } // Do not flush the wallet here for performance reasons CWalletDB walletdb(strWalletFile, "r+", false); std::set todo; std::set done; todo.insert(hashTx); while (!todo.empty()) { uint256 now = *todo.begin(); todo.erase(now); done.insert(now); assert(mapWallet.count(now)); CWalletTx &wtx = mapWallet[now]; int currentconfirm = wtx.GetDepthInMainChain(); if (conflictconfirms < currentconfirm) { // Block is 'more conflicted' than current confirm; update. // Mark transaction as conflicted with this block. wtx.nIndex = -1; wtx.hashBlock = hashBlock; wtx.MarkDirty(); walletdb.WriteTx(wtx); // Iterate over all its outputs, and mark transactions in the wallet // that spend them conflicted too. TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(now, 0)); while (iter != mapTxSpends.end() && iter->first.hash == now) { if (!done.count(iter->second)) { todo.insert(iter->second); } iter++; } // If a transaction changes 'conflicted' state, that changes the // balance available of the outputs it spends. So force those to be // recomputed. for (const CTxIn &txin : wtx.tx->vin) { if (mapWallet.count(txin.prevout.hash)) { mapWallet[txin.prevout.hash].MarkDirty(); } } } } } void CWallet::SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock) { LOCK2(cs_main, cs_wallet); if (!AddToWalletIfInvolvingMe(tx, pindex, posInBlock, true)) { // Not one of ours return; } // If a transaction changes 'conflicted' state, that changes the balance // available of the outputs it spends. So force those to be recomputed, // also: for (const CTxIn &txin : tx.vin) { if (mapWallet.count(txin.prevout.hash)) mapWallet[txin.prevout.hash].MarkDirty(); } } isminetype CWallet::IsMine(const CTxIn &txin) const { LOCK(cs_wallet); std::map::const_iterator mi = mapWallet.find(txin.prevout.hash); if (mi != mapWallet.end()) { const CWalletTx &prev = (*mi).second; if (txin.prevout.n < prev.tx->vout.size()) { return IsMine(prev.tx->vout[txin.prevout.n]); } } return ISMINE_NO; } // Note that this function doesn't distinguish between a 0-valued input, and a // not-"is mine" (according to the filter) input. CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter &filter) const { LOCK(cs_wallet); std::map::const_iterator mi = mapWallet.find(txin.prevout.hash); if (mi != mapWallet.end()) { const CWalletTx &prev = (*mi).second; if (txin.prevout.n < prev.tx->vout.size()) { if (IsMine(prev.tx->vout[txin.prevout.n]) & filter) { return prev.tx->vout[txin.prevout.n].nValue; } } } return 0; } isminetype CWallet::IsMine(const CTxOut &txout) const { return ::IsMine(*this, txout.scriptPubKey); } CAmount CWallet::GetCredit(const CTxOut &txout, const isminefilter &filter) const { if (!MoneyRange(txout.nValue)) { throw std::runtime_error(std::string(__func__) + ": value out of range"); } return (IsMine(txout) & filter) ? txout.nValue : 0; } bool CWallet::IsChange(const CTxOut &txout) const { // TODO: fix handling of 'change' outputs. The assumption is that any // payment to a script that is ours, but is not in the address book is // change. That assumption is likely to break when we implement // multisignature wallets that return change back into a // multi-signature-protected address; a better way of identifying which // outputs are 'the send' and which are 'the change' will need to be // implemented (maybe extend CWalletTx to remember which output, if any, was // change). if (::IsMine(*this, txout.scriptPubKey)) { CTxDestination address; if (!ExtractDestination(txout.scriptPubKey, address)) { return true; } LOCK(cs_wallet); if (!mapAddressBook.count(address)) { return true; } } return false; } CAmount CWallet::GetChange(const CTxOut &txout) const { if (!MoneyRange(txout.nValue)) { throw std::runtime_error(std::string(__func__) + ": value out of range"); } return (IsChange(txout) ? txout.nValue : 0); } bool CWallet::IsMine(const CTransaction &tx) const { for (const CTxOut &txout : tx.vout) { if (IsMine(txout)) { return true; } } return false; } bool CWallet::IsFromMe(const CTransaction &tx) const { return GetDebit(tx, ISMINE_ALL) > 0; } CAmount CWallet::GetDebit(const CTransaction &tx, const isminefilter &filter) const { CAmount nDebit = 0; for (const CTxIn &txin : tx.vin) { nDebit += GetDebit(txin, filter); if (!MoneyRange(nDebit)) { throw std::runtime_error(std::string(__func__) + ": value out of range"); } } return nDebit; } bool CWallet::IsAllFromMe(const CTransaction &tx, const isminefilter &filter) const { LOCK(cs_wallet); for (const CTxIn &txin : tx.vin) { auto mi = mapWallet.find(txin.prevout.hash); if (mi == mapWallet.end()) { // Any unknown inputs can't be from us. return false; } const CWalletTx &prev = (*mi).second; if (txin.prevout.n >= prev.tx->vout.size()) { // Invalid input! return false; } if (!(IsMine(prev.tx->vout[txin.prevout.n]) & filter)) { return false; } } return true; } CAmount CWallet::GetCredit(const CTransaction &tx, const isminefilter &filter) const { CAmount nCredit = 0; for (const CTxOut &txout : tx.vout) { nCredit += GetCredit(txout, filter); if (!MoneyRange(nCredit)) { throw std::runtime_error(std::string(__func__) + ": value out of range"); } } return nCredit; } CAmount CWallet::GetChange(const CTransaction &tx) const { CAmount nChange = 0; for (const CTxOut &txout : tx.vout) { nChange += GetChange(txout); if (!MoneyRange(nChange)) { throw std::runtime_error(std::string(__func__) + ": value out of range"); } } return nChange; } CPubKey CWallet::GenerateNewHDMasterKey() { CKey key; key.MakeNewKey(true); int64_t nCreationTime = GetTime(); CKeyMetadata metadata(nCreationTime); // Calculate the pubkey. CPubKey pubkey = key.GetPubKey(); assert(key.VerifyPubKey(pubkey)); // Set the hd keypath to "m" -> Master, refers the masterkeyid to itself. metadata.hdKeypath = "m"; metadata.hdMasterKeyID = pubkey.GetID(); LOCK(cs_wallet); // mem store the metadata mapKeyMetadata[pubkey.GetID()] = metadata; // Write the key&metadata to the database. if (!AddKeyPubKey(key, pubkey)) { throw std::runtime_error(std::string(__func__) + ": AddKeyPubKey failed"); } return pubkey; } bool CWallet::SetHDMasterKey(const CPubKey &pubkey) { LOCK(cs_wallet); // Ensure this wallet.dat can only be opened by clients supporting HD. SetMinVersion(FEATURE_HD); // Store the keyid (hash160) together with the child index counter in the // database as a hdchain object. CHDChain newHdChain; newHdChain.masterKeyID = pubkey.GetID(); SetHDChain(newHdChain, false); return true; } bool CWallet::SetHDChain(const CHDChain &chain, bool memonly) { LOCK(cs_wallet); if (!memonly && !CWalletDB(strWalletFile).WriteHDChain(chain)) { throw std::runtime_error(std::string(__func__) + ": writing chain failed"); } hdChain = chain; return true; } bool CWallet::IsHDEnabled() { return !hdChain.masterKeyID.IsNull(); } int64_t CWalletTx::GetTxTime() const { int64_t n = nTimeSmart; return n ? n : nTimeReceived; } int CWalletTx::GetRequestCount() const { LOCK(pwallet->cs_wallet); // Returns -1 if it wasn't being tracked. int nRequests = -1; if (IsCoinBase()) { // Generated block. if (!hashUnset()) { std::map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); if (mi != pwallet->mapRequestCount.end()) { nRequests = (*mi).second; } } } else { // Did anyone request this transaction? std::map::const_iterator mi = pwallet->mapRequestCount.find(GetId()); if (mi != pwallet->mapRequestCount.end()) { nRequests = (*mi).second; // How about the block it's in? if (nRequests == 0 && !hashUnset()) { std::map::const_iterator _mi = pwallet->mapRequestCount.find(hashBlock); if (_mi != pwallet->mapRequestCount.end()) { nRequests = (*_mi).second; } else { // If it's in someone else's block it must have got out. nRequests = 1; } } } } return nRequests; } void CWalletTx::GetAmounts(std::list &listReceived, std::list &listSent, CAmount &nFee, std::string &strSentAccount, const isminefilter &filter) const { nFee = 0; listReceived.clear(); listSent.clear(); strSentAccount = strFromAccount; // Compute fee: CAmount nDebit = GetDebit(filter); // debit>0 means we signed/sent this transaction. if (nDebit > 0) { CAmount nValueOut = tx->GetValueOut(); nFee = nDebit - nValueOut; } // Sent/received. for (unsigned int i = 0; i < tx->vout.size(); ++i) { const CTxOut &txout = tx->vout[i]; isminetype fIsMine = pwallet->IsMine(txout); // Only need to handle txouts if AT LEAST one of these is true: // 1) they debit from us (sent) // 2) the output is to us (received) if (nDebit > 0) { // Don't report 'change' txouts if (pwallet->IsChange(txout)) { continue; } } else if (!(fIsMine & filter)) { continue; } // In either case, we need to get the destination address. CTxDestination address; if (!ExtractDestination(txout.scriptPubKey, address) && !txout.scriptPubKey.IsUnspendable()) { LogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, " "txid %s\n", this->GetId().ToString()); address = CNoDestination(); } COutputEntry output = {address, txout.nValue, (int)i}; // If we are debited by the transaction, add the output as a "sent" // entry. if (nDebit > 0) { listSent.push_back(output); } // If we are receiving the output, add it as a "received" entry. if (fIsMine & filter) { listReceived.push_back(output); } } } void CWalletTx::GetAccountAmounts(const std::string &strAccount, CAmount &nReceived, CAmount &nSent, CAmount &nFee, const isminefilter &filter) const { nReceived = nSent = nFee = 0; CAmount allFee; std::string strSentAccount; std::list listReceived; std::list listSent; GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); if (strAccount == strSentAccount) { for (const COutputEntry &s : listSent) { nSent += s.amount; } nFee = allFee; } LOCK(pwallet->cs_wallet); for (const COutputEntry &r : listReceived) { if (pwallet->mapAddressBook.count(r.destination)) { std::map::const_iterator mi = pwallet->mapAddressBook.find(r.destination); if (mi != pwallet->mapAddressBook.end() && (*mi).second.name == strAccount) { nReceived += r.amount; } } else if (strAccount.empty()) { nReceived += r.amount; } } } /** * Scan the block chain (starting in pindexStart) for transactions from or to * us. If fUpdate is true, found transactions that already exist in the wallet * will be updated. * * Returns pointer to the first block in the last contiguous range that was * successfully scanned. */ CBlockIndex *CWallet::ScanForWalletTransactions(CBlockIndex *pindexStart, bool fUpdate) { LOCK2(cs_main, cs_wallet); CBlockIndex *ret = nullptr; int64_t nNow = GetTime(); const CChainParams &chainParams = Params(); CBlockIndex *pindex = pindexStart; // No need to read and scan block, if block was created before our wallet // birthday (as adjusted for block time variability) while (pindex && nTimeFirstKey && (pindex->GetBlockTime() < (nTimeFirstKey - 7200))) { pindex = chainActive.Next(pindex); } // Show rescan progress in GUI as dialog or on splashscreen, if -rescan on // startup. ShowProgress(_("Rescanning..."), 0); double dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex); double dProgressTip = GuessVerificationProgress(chainParams.TxData(), chainActive.Tip()); while (pindex) { if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0) { ShowProgress( _("Rescanning..."), std::max(1, std::min(99, (int)((GuessVerificationProgress( chainParams.TxData(), pindex) - dProgressStart) / (dProgressTip - dProgressStart) * 100)))); } CBlock block; if (ReadBlockFromDisk(block, pindex, Params().GetConsensus())) { for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) { AddToWalletIfInvolvingMe(*block.vtx[posInBlock], pindex, posInBlock, fUpdate); } if (!ret) { ret = pindex; } } else { ret = nullptr; } pindex = chainActive.Next(pindex); if (GetTime() >= nNow + 60) { nNow = GetTime(); LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex)); } } // Hide progress dialog in GUI. ShowProgress(_("Rescanning..."), 100); return ret; } void CWallet::ReacceptWalletTransactions() { // If transactions aren't being broadcasted, don't let them into local // mempool either. if (!fBroadcastTransactions) { return; } LOCK2(cs_main, cs_wallet); std::map mapSorted; // Sort pending wallet transactions based on their initial wallet insertion // order. for (std::pair &item : mapWallet) { const uint256 &wtxid = item.first; CWalletTx &wtx = item.second; assert(wtx.GetId() == wtxid); int nDepth = wtx.GetDepthInMainChain(); if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.isAbandoned())) { mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx)); } } // Try to add wallet transactions to memory pool. for (std::pair &item : mapSorted) { CWalletTx &wtx = *(item.second); LOCK(mempool.cs); CValidationState state; wtx.AcceptToMemoryPool(maxTxFee, state); } } bool CWalletTx::RelayWalletTransaction(CConnman *connman) { assert(pwallet->GetBroadcastTransactions()); if (IsCoinBase() || isAbandoned() || GetDepthInMainChain() != 0) { return false; } CValidationState state; // GetDepthInMainChain already catches known conflicts. if (InMempool() || AcceptToMemoryPool(maxTxFee, state)) { LogPrintf("Relaying wtx %s\n", GetId().ToString()); if (connman) { CInv inv(MSG_TX, GetId()); connman->ForEachNode( [&inv](CNode *pnode) { pnode->PushInventory(inv); }); return true; } } return false; } std::set CWalletTx::GetConflicts() const { std::set result; if (pwallet != nullptr) { uint256 myHash = GetId(); result = pwallet->GetConflicts(myHash); result.erase(myHash); } return result; } CAmount CWalletTx::GetDebit(const isminefilter &filter) const { if (tx->vin.empty()) return 0; CAmount debit = 0; if (filter & ISMINE_SPENDABLE) { if (fDebitCached) { debit += nDebitCached; } else { nDebitCached = pwallet->GetDebit(*this, ISMINE_SPENDABLE); fDebitCached = true; debit += nDebitCached; } } if (filter & ISMINE_WATCH_ONLY) { if (fWatchDebitCached) { debit += nWatchDebitCached; } else { nWatchDebitCached = pwallet->GetDebit(*this, ISMINE_WATCH_ONLY); fWatchDebitCached = true; debit += nWatchDebitCached; } } return debit; } CAmount CWalletTx::GetCredit(const isminefilter &filter) const { // Must wait until coinbase is safely deep enough in the chain before // valuing it. if (IsCoinBase() && GetBlocksToMaturity() > 0) { return 0; } CAmount credit = 0; if (filter & ISMINE_SPENDABLE) { // GetBalance can assume transactions in mapWallet won't change. if (fCreditCached) { credit += nCreditCached; } else { nCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE); fCreditCached = true; credit += nCreditCached; } } if (filter & ISMINE_WATCH_ONLY) { if (fWatchCreditCached) { credit += nWatchCreditCached; } else { nWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY); fWatchCreditCached = true; credit += nWatchCreditCached; } } return credit; } CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const { if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) { if (fUseCache && fImmatureCreditCached) return nImmatureCreditCached; nImmatureCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE); fImmatureCreditCached = true; return nImmatureCreditCached; } return 0; } CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const { if (pwallet == 0) { return 0; } // Must wait until coinbase is safely deep enough in the chain before // valuing it. if (IsCoinBase() && GetBlocksToMaturity() > 0) { return 0; } if (fUseCache && fAvailableCreditCached) { return nAvailableCreditCached; } CAmount nCredit = 0; uint256 hashTx = GetId(); for (unsigned int i = 0; i < tx->vout.size(); i++) { if (!pwallet->IsSpent(hashTx, i)) { const CTxOut &txout = tx->vout[i]; nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); if (!MoneyRange(nCredit)) { throw std::runtime_error( "CWalletTx::GetAvailableCredit() : value out of range"); } } } nAvailableCreditCached = nCredit; fAvailableCreditCached = true; return nCredit; } CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool &fUseCache) const { if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) { if (fUseCache && fImmatureWatchCreditCached) { return nImmatureWatchCreditCached; } nImmatureWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY); fImmatureWatchCreditCached = true; return nImmatureWatchCreditCached; } return 0; } CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool &fUseCache) const { if (pwallet == 0) { return 0; } // Must wait until coinbase is safely deep enough in the chain before // valuing it. if (IsCoinBase() && GetBlocksToMaturity() > 0) { return 0; } if (fUseCache && fAvailableWatchCreditCached) { return nAvailableWatchCreditCached; } CAmount nCredit = 0; for (unsigned int i = 0; i < tx->vout.size(); i++) { if (!pwallet->IsSpent(GetId(), i)) { const CTxOut &txout = tx->vout[i]; nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY); if (!MoneyRange(nCredit)) { throw std::runtime_error( "CWalletTx::GetAvailableCredit() : value out of range"); } } } nAvailableWatchCreditCached = nCredit; fAvailableWatchCreditCached = true; return nCredit; } CAmount CWalletTx::GetChange() const { if (fChangeCached) { return nChangeCached; } nChangeCached = pwallet->GetChange(*this); fChangeCached = true; return nChangeCached; } bool CWalletTx::InMempool() const { LOCK(mempool.cs); if (mempool.exists(GetId())) { return true; } return false; } bool CWalletTx::IsTrusted() const { // Quick answer in most cases if (!CheckFinalTx(*this)) { return false; } int nDepth = GetDepthInMainChain(); if (nDepth >= 1) { return true; } if (nDepth < 0) { return false; } // using wtx's cached debit if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) { return false; } // Don't trust unconfirmed transactions from us unless they are in the // mempool. if (!InMempool()) { return false; } // Trusted if all inputs are from us and are in the mempool: for (const CTxIn &txin : tx->vin) { // Transactions not sent by us: not trusted const CWalletTx *parent = pwallet->GetWalletTx(txin.prevout.hash); if (parent == nullptr) { return false; } const CTxOut &parentOut = parent->tx->vout[txin.prevout.n]; if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) { return false; } } return true; } bool CWalletTx::IsEquivalentTo(const CWalletTx &_tx) const { CMutableTransaction tx1 = *this->tx; CMutableTransaction tx2 = *_tx.tx; for (unsigned int i = 0; i < tx1.vin.size(); i++) { tx1.vin[i].scriptSig = CScript(); } for (unsigned int i = 0; i < tx2.vin.size(); i++) { tx2.vin[i].scriptSig = CScript(); } return CTransaction(tx1) == CTransaction(tx2); } std::vector CWallet::ResendWalletTransactionsBefore(int64_t nTime, CConnman *connman) { std::vector result; LOCK(cs_wallet); // Sort them in chronological order std::multimap mapSorted; for (std::pair &item : mapWallet) { CWalletTx &wtx = item.second; // Don't rebroadcast if newer than nTime: if (wtx.nTimeReceived > nTime) { continue; } mapSorted.insert(std::make_pair(wtx.nTimeReceived, &wtx)); } for (std::pair &item : mapSorted) { CWalletTx &wtx = *item.second; if (wtx.RelayWalletTransaction(connman)) { result.push_back(wtx.GetId()); } } return result; } void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, CConnman *connman) { // Do this infrequently and randomly to avoid giving away that these are our // transactions. if (GetTime() < nNextResend || !fBroadcastTransactions) { return; } bool fFirst = (nNextResend == 0); nNextResend = GetTime() + GetRand(30 * 60); if (fFirst) { return; } // Only do it if there's been a new block since last time if (nBestBlockTime < nLastResend) { return; } nLastResend = GetTime(); // Rebroadcast unconfirmed txes older than 5 minutes before the last block // was found: std::vector relayed = ResendWalletTransactionsBefore(nBestBlockTime - 5 * 60, connman); if (!relayed.empty()) { LogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, relayed.size()); } } /** @} */ // end of mapWallet /** * @defgroup Actions * * @{ */ CAmount CWallet::GetBalance() const { LOCK2(cs_main, cs_wallet); CAmount nTotal = 0; for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx *pcoin = &(*it).second; if (pcoin->IsTrusted()) { nTotal += pcoin->GetAvailableCredit(); } } return nTotal; } CAmount CWallet::GetUnconfirmedBalance() const { LOCK2(cs_main, cs_wallet); CAmount nTotal = 0; for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx *pcoin = &(*it).second; if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool()) { nTotal += pcoin->GetAvailableCredit(); } } return nTotal; } CAmount CWallet::GetImmatureBalance() const { LOCK2(cs_main, cs_wallet); CAmount nTotal = 0; for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx *pcoin = &(*it).second; nTotal += pcoin->GetImmatureCredit(); } return nTotal; } CAmount CWallet::GetWatchOnlyBalance() const { LOCK2(cs_main, cs_wallet); CAmount nTotal = 0; for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx *pcoin = &(*it).second; if (pcoin->IsTrusted()) { nTotal += pcoin->GetAvailableWatchOnlyCredit(); } } return nTotal; } CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const { LOCK2(cs_main, cs_wallet); CAmount nTotal = 0; for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx *pcoin = &(*it).second; if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool()) { nTotal += pcoin->GetAvailableWatchOnlyCredit(); } } return nTotal; } CAmount CWallet::GetImmatureWatchOnlyBalance() const { LOCK2(cs_main, cs_wallet); CAmount nTotal = 0; for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx *pcoin = &(*it).second; nTotal += pcoin->GetImmatureWatchOnlyCredit(); } return nTotal; } void CWallet::AvailableCoins(std::vector &vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue) const { vCoins.clear(); LOCK2(cs_main, cs_wallet); for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const uint256 &wtxid = it->first; const CWalletTx *pcoin = &(*it).second; if (!CheckFinalTx(*pcoin)) { continue; } if (fOnlyConfirmed && !pcoin->IsTrusted()) { continue; } if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) { continue; } int nDepth = pcoin->GetDepthInMainChain(); if (nDepth < 0) { continue; } // We should not consider coins which aren't at least in our mempool. // It's possible for these to be conflicted via ancestors which we may // never be able to detect. if (nDepth == 0 && !pcoin->InMempool()) { continue; } // Bitcoin-ABC: Removed check that prevents consideration of coins from // transactions that are replacing other transactions. This check based // on pcoin->mapValue.count("replaces_txid") which was not being set // anywhere. // Similarly, we should not consider coins from transactions that have // been replaced. In the example above, we would want to prevent // creation of a transaction A' spending an output of A, because if // transaction B were initially confirmed, conflicting with A and A', we // wouldn't want to the user to create a transaction D intending to // replace A', but potentially resulting in a scenario where A, A', and // D could all be accepted (instead of just B and D, or just A and A' // like the user would want). // Bitcoin-ABC: retained this check as 'replaced_by_txid' is still set // in the wallet code. if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaced_by_txid")) { continue; } for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { isminetype mine = IsMine(pcoin->tx->vout[i]); if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && !IsLockedCoin((*it).first, i) && (pcoin->tx->vout[i].nValue > 0 || fIncludeZeroValue) && (!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), (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO)); } } } } static void ApproximateBestSubset( std::vector>> vValue, const CAmount &nTotalLower, const CAmount &nTargetValue, std::vector &vfBest, CAmount &nBest, int iterations = 1000) { std::vector vfIncluded; vfBest.assign(vValue.size(), true); nBest = nTotalLower; FastRandomContext insecure_rand; for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++) { vfIncluded.assign(vValue.size(), false); CAmount nTotal = 0; bool fReachedTarget = false; for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) { for (size_t i = 0; i < vValue.size(); i++) { // The solver here uses a randomized algorithm, the randomness // serves no real security purpose but is just needed to prevent // degenerate behavior and it is important that the rng is fast. // We do not use a constant random sequence, because there may // be some privacy improvement by making the selection random. - if (nPass == 0 ? insecure_rand.rand32() & 1 : !vfIncluded[i]) { + if (nPass == 0 ? insecure_rand.randbool() : !vfIncluded[i]) { nTotal += vValue[i].first; vfIncluded[i] = true; if (nTotal >= nTargetValue) { fReachedTarget = true; if (nTotal < nBest) { nBest = nTotal; vfBest = vfIncluded; } nTotal -= vValue[i].first; vfIncluded[i] = false; } } } } } } bool CWallet::SelectCoinsMinConf( const CAmount &nTargetValue, const int nConfMine, const int nConfTheirs, const uint64_t nMaxAncestors, std::vector vCoins, std::set> &setCoinsRet, CAmount &nValueRet) const { setCoinsRet.clear(); nValueRet = 0; // List of values less than target std::pair> coinLowestLarger; coinLowestLarger.first = std::numeric_limits::max(); coinLowestLarger.second.first = nullptr; std::vector>> vValue; CAmount nTotalLower = 0; random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); for (const COutput &output : vCoins) { if (!output.fSpendable) { continue; } const CWalletTx *pcoin = output.tx; if (output.nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs)) { continue; } if (!mempool.TransactionWithinChainLimit(pcoin->GetId(), nMaxAncestors)) { continue; } int i = output.i; CAmount n = pcoin->tx->vout[i].nValue; std::pair> coin = std::make_pair(n, std::make_pair(pcoin, i)); if (n == nTargetValue) { setCoinsRet.insert(coin.second); nValueRet += coin.first; return true; } else if (n < nTargetValue + MIN_CHANGE) { vValue.push_back(coin); nTotalLower += n; } else if (n < coinLowestLarger.first) { coinLowestLarger = coin; } } if (nTotalLower == nTargetValue) { for (unsigned int i = 0; i < vValue.size(); ++i) { setCoinsRet.insert(vValue[i].second); nValueRet += vValue[i].first; } return true; } if (nTotalLower < nTargetValue) { if (coinLowestLarger.second.first == nullptr) { return false; } setCoinsRet.insert(coinLowestLarger.second); nValueRet += coinLowestLarger.first; return true; } // Solve subset sum by stochastic approximation std::sort(vValue.begin(), vValue.end(), CompareValueOnly()); std::reverse(vValue.begin(), vValue.end()); std::vector vfBest; CAmount nBest; ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest); if (nBest != nTargetValue && nTotalLower >= nTargetValue + MIN_CHANGE) { ApproximateBestSubset(vValue, nTotalLower, nTargetValue + MIN_CHANGE, vfBest, nBest); } // If we have a bigger coin and (either the stochastic approximation didn't // find a good solution, or the next bigger coin is closer), return the // bigger coin. if (coinLowestLarger.second.first && ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || coinLowestLarger.first <= nBest)) { setCoinsRet.insert(coinLowestLarger.second); nValueRet += coinLowestLarger.first; } else { for (unsigned int i = 0; i < vValue.size(); i++) { if (vfBest[i]) { setCoinsRet.insert(vValue[i].second); nValueRet += vValue[i].first; } } LogPrint("selectcoins", "SelectCoins() best subset: "); for (unsigned int i = 0; i < vValue.size(); i++) { if (vfBest[i]) { LogPrint("selectcoins", "%s ", FormatMoney(vValue[i].first)); } } LogPrint("selectcoins", "total %s\n", FormatMoney(nBest)); } return true; } bool CWallet::SelectCoins( const std::vector &vAvailableCoins, const CAmount &nTargetValue, std::set> &setCoinsRet, CAmount &nValueRet, const CCoinControl *coinControl) const { std::vector vCoins(vAvailableCoins); // coin control -> return all selected outputs (we want all selected to go // into the transaction for sure). if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs) { for (const COutput &out : vCoins) { if (!out.fSpendable) { continue; } nValueRet += out.tx->tx->vout[out.i].nValue; setCoinsRet.insert(std::make_pair(out.tx, out.i)); } return (nValueRet >= nTargetValue); } // Calculate value from preset inputs and store them. std::set> setPresetCoins; CAmount nValueFromPresetInputs = 0; std::vector vPresetInputs; if (coinControl) { coinControl->ListSelected(vPresetInputs); } for (const COutPoint &outpoint : vPresetInputs) { std::map::const_iterator it = mapWallet.find(outpoint.hash); if (it == mapWallet.end()) { // TODO: Allow non-wallet inputs return false; } const CWalletTx *pcoin = &it->second; // Clearly invalid input, fail. if (pcoin->tx->vout.size() <= outpoint.n) { return false; } nValueFromPresetInputs += pcoin->tx->vout[outpoint.n].nValue; setPresetCoins.insert(std::make_pair(pcoin, outpoint.n)); } // Remove preset inputs from vCoins. for (std::vector::iterator it = vCoins.begin(); it != vCoins.end() && coinControl && coinControl->HasSelected();) { if (setPresetCoins.count(std::make_pair(it->tx, it->i))) { it = vCoins.erase(it); } else { ++it; } } size_t nMaxChainLength = std::min(GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT), GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)); bool fRejectLongChains = GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS); bool res = nTargetValue <= nValueFromPresetInputs || SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 6, 0, vCoins, setCoinsRet, nValueRet) || SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 1, 0, vCoins, setCoinsRet, nValueRet) || (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, 2, vCoins, setCoinsRet, nValueRet)) || (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::min((size_t)4, nMaxChainLength / 3), vCoins, setCoinsRet, nValueRet)) || (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength / 2, vCoins, setCoinsRet, nValueRet)) || (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength, vCoins, setCoinsRet, nValueRet)) || (bSpendZeroConfChange && !fRejectLongChains && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::numeric_limits::max(), vCoins, setCoinsRet, nValueRet)); // Because SelectCoinsMinConf clears the setCoinsRet, we now add the // possible inputs to the coinset. setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end()); // Add preset inputs to the total value selected. nValueRet += nValueFromPresetInputs; return res; } bool CWallet::FundTransaction(CMutableTransaction &tx, CAmount &nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate &specificFeeRate, int &nChangePosInOut, std::string &strFailReason, bool includeWatching, bool lockUnspents, const std::set &setSubtractFeeFromOutputs, bool keepReserveKey, const CTxDestination &destChange) { std::vector vecSend; // Turn the txout set into a CRecipient vector. for (size_t idx = 0; idx < tx.vout.size(); idx++) { const CTxOut &txOut = tx.vout[idx]; CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, setSubtractFeeFromOutputs.count(idx) == 1}; vecSend.push_back(recipient); } CCoinControl coinControl; coinControl.destChange = destChange; coinControl.fAllowOtherInputs = true; coinControl.fAllowWatchOnly = includeWatching; coinControl.fOverrideFeeRate = overrideEstimatedFeeRate; coinControl.nFeeRate = specificFeeRate; for (const CTxIn &txin : tx.vin) { coinControl.Select(txin.prevout); } CReserveKey reservekey(this); CWalletTx wtx; if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosInOut, strFailReason, &coinControl, false)) { return false; } if (nChangePosInOut != -1) { tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.tx->vout[nChangePosInOut]); } // Copy output sizes from new transaction; they may have had the fee // subtracted from them. for (size_t idx = 0; idx < tx.vout.size(); idx++) { tx.vout[idx].nValue = wtx.tx->vout[idx].nValue; } // Add new txins (keeping original txin scriptSig/order) for (const CTxIn &txin : wtx.tx->vin) { if (!coinControl.IsSelected(txin.prevout)) { tx.vin.push_back(txin); if (lockUnspents) { LOCK2(cs_main, cs_wallet); LockCoin(txin.prevout); } } } // Optionally keep the change output key. if (keepReserveKey) { reservekey.KeepKey(); } return true; } bool CWallet::CreateTransaction(const std::vector &vecSend, CWalletTx &wtxNew, CReserveKey &reservekey, CAmount &nFeeRet, int &nChangePosInOut, std::string &strFailReason, const CCoinControl *coinControl, bool sign) { CAmount nValue = 0; int nChangePosRequest = nChangePosInOut; unsigned int nSubtractFeeFromAmount = 0; for (const auto &recipient : vecSend) { if (nValue < 0 || recipient.nAmount < 0) { strFailReason = _("Transaction amounts must not be negative"); return false; } nValue += recipient.nAmount; if (recipient.fSubtractFeeFromAmount) { nSubtractFeeFromAmount++; } } if (vecSend.empty()) { strFailReason = _("Transaction must have at least one recipient"); return false; } wtxNew.fTimeReceivedIsTxTime = true; wtxNew.BindWallet(this); CMutableTransaction txNew; // Discourage fee sniping. // // For a large miner the value of the transactions in the best block and the // mempool can exceed the cost of deliberately attempting to mine two blocks // to orphan the current best block. By setting nLockTime such that only the // next block can include the transaction, we discourage this practice as // the height restricted and limited blocksize gives miners considering fee // sniping fewer options for pulling off this attack. // // A simple way to think about this is from the wallet's point of view we // always want the blockchain to move forward. By setting nLockTime this way // we're basically making the statement that we only want this transaction // to appear in the next block; we don't want to potentially encourage // reorgs by allowing transactions to appear at lower heights than the next // block in forks of the best chain. // // Of course, the subsidy is high enough, and transaction volume low enough, // that fee sniping isn't a problem yet, but by implementing a fix now we // ensure code won't be written that makes assumptions about nLockTime that // preclude a fix later. txNew.nLockTime = chainActive.Height(); // Secondly occasionally randomly pick a nLockTime even further back, so // that transactions that are delayed after signing for whatever reason, // e.g. high-latency mix networks and some CoinJoin implementations, have // better privacy. if (GetRandInt(10) == 0) { txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); } assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); assert(txNew.nLockTime < LOCKTIME_THRESHOLD); { std::set> setCoins; LOCK2(cs_main, cs_wallet); std::vector vAvailableCoins; AvailableCoins(vAvailableCoins, true, coinControl); nFeeRet = 0; // Start with no fee and loop until there is enough fee. while (true) { nChangePosInOut = nChangePosRequest; txNew.vin.clear(); txNew.vout.clear(); wtxNew.fFromMe = true; bool fFirst = true; CAmount nValueToSelect = nValue; if (nSubtractFeeFromAmount == 0) { nValueToSelect += nFeeRet; } double dPriority = 0; // vouts to the payees for (const auto &recipient : vecSend) { CTxOut txout(recipient.nAmount, recipient.scriptPubKey); if (recipient.fSubtractFeeFromAmount) { // Subtract fee equally from each selected recipient. txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // First receiver pays the remainder not divisible by output // count. if (fFirst) { fFirst = false; txout.nValue -= nFeeRet % nSubtractFeeFromAmount; } } if (txout.IsDust(dustRelayFee)) { if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) { if (txout.nValue < 0) { strFailReason = _("The transaction amount is " "too small to pay the fee"); } else { strFailReason = _("The transaction amount is too small to " "send after the fee has been deducted"); } } else { strFailReason = _("Transaction amount too small"); } return false; } txNew.vout.push_back(txout); } // Choose coins to use. CAmount nValueIn = 0; setCoins.clear(); if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) { strFailReason = _("Insufficient funds"); return false; } for (const auto &pcoin : setCoins) { CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; // The coin age after the next block (depth+1) is used instead // of the current, reflecting an assumption the user would // accept a bit more delay for a chance at a free transaction. // But mempool inputs might still be in the mempool, so their // age stays 0. int age = pcoin.first->GetDepthInMainChain(); assert(age >= 0); if (age != 0) age += 1; dPriority += (double)nCredit * age; } const CAmount nChange = nValueIn - nValueToSelect; if (nChange > 0) { // Fill a vout to ourself. // TODO: pass in scriptChange instead of reservekey so change // transaction isn't always pay-to-bitcoin-address. CScript scriptChange; // Coin control: send change to custom address. if (coinControl && !boost::get(&coinControl->destChange)) { scriptChange = GetScriptForDestination(coinControl->destChange); // No coin control: send change to newly generated address. } else { // Note: We use a new key here to keep it from being obvious // which side is the change. The drawback is that by not // reusing a previous key, the change may be lost if a // backup is restored, if the backup doesn't have the new // private key for the change. If we reused the old key, it // would be possible to add code to look for and rediscover // unknown transactions that were written with keys of ours // to recover post-backup change. // Reserve a new key pair from key pool. CPubKey vchPubKey; bool ret; ret = reservekey.GetReservedKey(vchPubKey); if (!ret) { strFailReason = _("Keypool ran out, please call " "keypoolrefill first"); return false; } scriptChange = GetScriptForDestination(vchPubKey.GetID()); } CTxOut newTxOut(nChange, scriptChange); // We do not move dust-change to fees, because the sender would // end up paying more than requested. This would be against the // purpose of the all-inclusive feature. So instead we raise the // change and deduct from the recipient. if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) { CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; // Raise change until no more dust. newTxOut.nValue += nDust; // Subtract from first recipient. for (unsigned int i = 0; i < vecSend.size(); i++) { if (vecSend[i].fSubtractFeeFromAmount) { txNew.vout[i].nValue -= nDust; if (txNew.vout[i].IsDust(dustRelayFee)) { strFailReason = _("The transaction amount is too small " "to send after the fee has been " "deducted"); return false; } break; } } } // Never create dust outputs; if we would, just add the dust to // the fee. if (newTxOut.IsDust(dustRelayFee)) { nChangePosInOut = -1; nFeeRet += nChange; reservekey.ReturnKey(); } else { if (nChangePosInOut == -1) { // Insert change txn at random position: nChangePosInOut = GetRandInt(txNew.vout.size() + 1); } else if ((unsigned int)nChangePosInOut > txNew.vout.size()) { strFailReason = _("Change index out of range"); return false; } std::vector::iterator position = txNew.vout.begin() + nChangePosInOut; txNew.vout.insert(position, newTxOut); } } else { reservekey.ReturnKey(); } // Fill vin // // Note how the sequence number is set to non-maxint so that the // nLockTime set above actually works. for (const auto &coin : setCoins) { txNew.vin.push_back( CTxIn(coin.first->GetId(), coin.second, CScript(), std::numeric_limits::max() - 1)); } // Fill in dummy signatures for fee calculation. if (!DummySignTx(txNew, setCoins)) { strFailReason = _("Signing transaction failed"); return false; } unsigned int nBytes = GetTransactionSize(txNew); CTransaction txNewConst(txNew); dPriority = txNewConst.ComputePriority(dPriority, nBytes); // Remove scriptSigs to eliminate the fee calculation dummy // signatures. for (auto &vin : txNew.vin) { vin.scriptSig = CScript(); } // Allow to override the default confirmation target over the // CoinControl instance. int currentConfirmationTarget = nTxConfirmTarget; if (coinControl && coinControl->nConfirmTarget > 0) { currentConfirmationTarget = coinControl->nConfirmTarget; } // Can we complete this as a free transaction? if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) { // Not enough fee: enough priority? double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget); // Require at least hard-coded AllowFree. if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) { break; } } CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool); if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { nFeeNeeded = coinControl->nMinimumTotalFee; } if (coinControl && coinControl->fOverrideFeeRate) { nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); } // If we made it here and we aren't even able to meet the relay fee // on the next pass, give up because we must be at the maximum // allowed fee. if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) { strFailReason = _("Transaction too large for fee policy"); return false; } if (nFeeRet >= nFeeNeeded) { // Reduce fee to only the needed amount if we have change output // to increase. This prevents potential overpayment in fees if // the coins selected to meet nFeeNeeded result in a transaction // that requires less fee than the prior iteration. // TODO: The case where nSubtractFeeFromAmount > 0 remains to be // addressed because it requires returning the fee to the payees // and not the change output. // TODO: The case where there is no change output remains to be // addressed so we avoid creating too small an output. if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { CAmount extraFeePaid = nFeeRet - nFeeNeeded; std::vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; change_position->nValue += extraFeePaid; nFeeRet -= extraFeePaid; } // Done, enough fee included. break; } // Try to reduce change to include necessary fee. if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; std::vector::iterator change_position = txNew.vout.begin() + nChangePosInOut; // Only reduce change if remaining amount is still a large // enough output. if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { change_position->nValue -= additionalFeeNeeded; nFeeRet += additionalFeeNeeded; // Done, able to increase fee from change. break; } } // Include more fee and try again. nFeeRet = nFeeNeeded; continue; } if (sign) { uint32_t nHashType = SIGHASH_ALL | SIGHASH_FORKID; CTransaction txNewConst(txNew); int nIn = 0; for (const auto &coin : setCoins) { const CScript &scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; SignatureData sigdata; if (!ProduceSignature( TransactionSignatureCreator( this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, nHashType), scriptPubKey, sigdata)) { strFailReason = _("Signing transaction failed"); return false; } else { UpdateTransaction(txNew, nIn, sigdata); } nIn++; } } // Embed the constructed transaction data in wtxNew. wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); // Limit size. if (GetTransactionSize(wtxNew) >= MAX_STANDARD_TX_SIZE) { strFailReason = _("Transaction too large"); return false; } } if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { // Lastly, ensure this tx will pass the mempool's chain limits. LockPoints lp; CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp); CTxMemPool::setEntries setAncestors; size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; std::string errString; if (!mempool.CalculateMemPoolAncestors( entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { strFailReason = _("Transaction has too long of a mempool chain"); return false; } } return true; } /** * Call after CreateTransaction unless you want to abort */ bool CWallet::CommitTransaction(CWalletTx &wtxNew, CReserveKey &reservekey, CConnman *connman, CValidationState &state) { LOCK2(cs_main, cs_wallet); LogPrintf("CommitTransaction:\n%s", wtxNew.tx->ToString()); // Take key pair from key pool so it won't be used again. reservekey.KeepKey(); // Add tx to wallet, because if it has change it's also ours, otherwise just // for transaction history. AddToWallet(wtxNew); // Notify that old coins are spent. for (const CTxIn &txin : wtxNew.tx->vin) { CWalletTx &coin = mapWallet[txin.prevout.hash]; coin.BindWallet(this); NotifyTransactionChanged(this, coin.GetId(), CT_UPDATED); } // Track how many getdata requests our transaction gets. mapRequestCount[wtxNew.GetId()] = 0; if (fBroadcastTransactions) { // Broadcast if (!wtxNew.AcceptToMemoryPool(maxTxFee, state)) { LogPrintf("CommitTransaction(): Transaction cannot be " "broadcast immediately, %s\n", state.GetRejectReason()); // TODO: if we expect the failure to be long term or permanent, // instead delete wtx from the wallet and return failure. } else { wtxNew.RelayWalletTransaction(connman); } } return true; } void CWallet::ListAccountCreditDebit(const std::string &strAccount, std::list &entries) { CWalletDB walletdb(strWalletFile); return walletdb.ListAccountCreditDebit(strAccount, entries); } bool CWallet::AddAccountingEntry(const CAccountingEntry &acentry) { CWalletDB walletdb(strWalletFile); return AddAccountingEntry(acentry, &walletdb); } bool CWallet::AddAccountingEntry(const CAccountingEntry &acentry, CWalletDB *pwalletdb) { if (!pwalletdb->WriteAccountingEntry_Backend(acentry)) { return false; } laccentries.push_back(acentry); CAccountingEntry &entry = laccentries.back(); wtxOrdered.insert(std::make_pair(entry.nOrderPos, TxPair(nullptr, &entry))); return true; } CAmount CWallet::GetRequiredFee(unsigned int nTxBytes) { return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes)); } CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool &pool) { // payTxFee is the user-set global for desired feerate. return GetMinimumFee(nTxBytes, nConfirmTarget, pool, payTxFee.GetFee(nTxBytes)); } CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool &pool, CAmount targetFee) { CAmount nFeeNeeded = targetFee; // User didn't set: use -txconfirmtarget to estimate... if (nFeeNeeded == 0) { int estimateFoundTarget = nConfirmTarget; nFeeNeeded = pool.estimateSmartFee(nConfirmTarget, &estimateFoundTarget) .GetFee(nTxBytes); // ... unless we don't have enough mempool data for estimatefee, then // use fallbackFee. if (nFeeNeeded == 0) { nFeeNeeded = fallbackFee.GetFee(nTxBytes); } } // Prevent user from paying a fee below minRelayTxFee or minTxFee. nFeeNeeded = std::max(nFeeNeeded, GetRequiredFee(nTxBytes)); // But always obey the maximum. if (nFeeNeeded > maxTxFee) { nFeeNeeded = maxTxFee; } return nFeeNeeded; } DBErrors CWallet::LoadWallet(bool &fFirstRunRet) { if (!fFileBacked) { return DB_LOAD_OK; } fFirstRunRet = false; DBErrors nLoadWalletRet = CWalletDB(strWalletFile, "cr+").LoadWallet(this); if (nLoadWalletRet == DB_NEED_REWRITE) { if (CDB::Rewrite(strWalletFile, "\x04pool")) { LOCK(cs_wallet); setKeyPool.clear(); // Note: can't top-up keypool here, because wallet is locked. User // will be prompted to unlock wallet the next operation that // requires a new key. } } if (nLoadWalletRet != DB_LOAD_OK) { return nLoadWalletRet; } fFirstRunRet = !vchDefaultKey.IsValid(); uiInterface.LoadWallet(this); return DB_LOAD_OK; } DBErrors CWallet::ZapSelectTx(std::vector &vHashIn, std::vector &vHashOut) { if (!fFileBacked) { return DB_LOAD_OK; } DBErrors nZapSelectTxRet = CWalletDB(strWalletFile, "cr+").ZapSelectTx(this, vHashIn, vHashOut); if (nZapSelectTxRet == DB_NEED_REWRITE) { if (CDB::Rewrite(strWalletFile, "\x04pool")) { LOCK(cs_wallet); setKeyPool.clear(); // Note: can't top-up keypool here, because wallet is locked. User // will be prompted to unlock wallet the next operation that // requires a new key. } } if (nZapSelectTxRet != DB_LOAD_OK) { return nZapSelectTxRet; } MarkDirty(); return DB_LOAD_OK; } DBErrors CWallet::ZapWalletTx(std::vector &vWtx) { if (!fFileBacked) { return DB_LOAD_OK; } DBErrors nZapWalletTxRet = CWalletDB(strWalletFile, "cr+").ZapWalletTx(this, vWtx); if (nZapWalletTxRet == DB_NEED_REWRITE) { if (CDB::Rewrite(strWalletFile, "\x04pool")) { LOCK(cs_wallet); setKeyPool.clear(); // Note: can't top-up keypool here, because wallet is locked. User // will be prompted to unlock wallet the next operation that // requires a new key. } } if (nZapWalletTxRet != DB_LOAD_OK) { return nZapWalletTxRet; } return DB_LOAD_OK; } bool CWallet::SetAddressBook(const CTxDestination &address, const std::string &strName, const std::string &strPurpose) { bool fUpdated = false; { // mapAddressBook LOCK(cs_wallet); std::map::iterator mi = mapAddressBook.find(address); fUpdated = mi != mapAddressBook.end(); mapAddressBook[address].name = strName; // Update purpose only if requested. if (!strPurpose.empty()) { mapAddressBook[address].purpose = strPurpose; } } NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != ISMINE_NO, strPurpose, (fUpdated ? CT_UPDATED : CT_NEW)); if (!fFileBacked) { return false; } if (!strPurpose.empty() && !CWalletDB(strWalletFile) .WritePurpose(CBitcoinAddress(address).ToString(), strPurpose)) { return false; } return CWalletDB(strWalletFile) .WriteName(CBitcoinAddress(address).ToString(), strName); } bool CWallet::DelAddressBook(const CTxDestination &address) { { // mapAddressBook LOCK(cs_wallet); if (fFileBacked) { // Delete destdata tuples associated with address. std::string strAddress = CBitcoinAddress(address).ToString(); for (const std::pair &item : mapAddressBook[address].destdata) { CWalletDB(strWalletFile).EraseDestData(strAddress, item.first); } } mapAddressBook.erase(address); } NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address) != ISMINE_NO, "", CT_DELETED); if (!fFileBacked) { return false; } CWalletDB(strWalletFile).ErasePurpose(CBitcoinAddress(address).ToString()); return CWalletDB(strWalletFile) .EraseName(CBitcoinAddress(address).ToString()); } bool CWallet::SetDefaultKey(const CPubKey &vchPubKey) { if (fFileBacked && !CWalletDB(strWalletFile).WriteDefaultKey(vchPubKey)) { return false; } vchDefaultKey = vchPubKey; return true; } /** * Mark old keypool keys as used, and generate all new keys. */ bool CWallet::NewKeyPool() { LOCK(cs_wallet); CWalletDB walletdb(strWalletFile); for (int64_t nIndex : setKeyPool) { walletdb.ErasePool(nIndex); } setKeyPool.clear(); if (IsLocked()) { return false; } int64_t nKeys = std::max(GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), int64_t(0)); for (int i = 0; i < nKeys; i++) { int64_t nIndex = i + 1; walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey())); setKeyPool.insert(nIndex); } LogPrintf("CWallet::NewKeyPool wrote %d new keys\n", nKeys); return true; } bool CWallet::TopUpKeyPool(unsigned int kpSize) { LOCK(cs_wallet); if (IsLocked()) { return false; } CWalletDB walletdb(strWalletFile); // Top up key pool. unsigned int nTargetSize; if (kpSize > 0) { nTargetSize = kpSize; } else { nTargetSize = std::max(GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), int64_t(0)); } while (setKeyPool.size() < (nTargetSize + 1)) { int64_t nEnd = 1; if (!setKeyPool.empty()) { nEnd = *(--setKeyPool.end()) + 1; } if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey()))) { throw std::runtime_error(std::string(__func__) + ": writing generated key failed"); } setKeyPool.insert(nEnd); LogPrintf("keypool added key %d, size=%u\n", nEnd, setKeyPool.size()); } return true; } void CWallet::ReserveKeyFromKeyPool(int64_t &nIndex, CKeyPool &keypool) { nIndex = -1; keypool.vchPubKey = CPubKey(); LOCK(cs_wallet); if (!IsLocked()) { TopUpKeyPool(); } // Get the oldest key. if (setKeyPool.empty()) { return; } CWalletDB walletdb(strWalletFile); nIndex = *(setKeyPool.begin()); setKeyPool.erase(setKeyPool.begin()); if (!walletdb.ReadPool(nIndex, keypool)) { throw std::runtime_error(std::string(__func__) + ": read failed"); } if (!HaveKey(keypool.vchPubKey.GetID())) { throw std::runtime_error(std::string(__func__) + ": unknown key in key pool"); } assert(keypool.vchPubKey.IsValid()); LogPrintf("keypool reserve %d\n", nIndex); } void CWallet::KeepKey(int64_t nIndex) { // Remove from key pool. if (fFileBacked) { CWalletDB walletdb(strWalletFile); walletdb.ErasePool(nIndex); } LogPrintf("keypool keep %d\n", nIndex); } void CWallet::ReturnKey(int64_t nIndex) { // Return to key pool. { LOCK(cs_wallet); setKeyPool.insert(nIndex); } LogPrintf("keypool return %d\n", nIndex); } bool CWallet::GetKeyFromPool(CPubKey &result) { LOCK(cs_wallet); int64_t nIndex = 0; CKeyPool keypool; ReserveKeyFromKeyPool(nIndex, keypool); if (nIndex == -1) { if (IsLocked()) { return false; } result = GenerateNewKey(); return true; } KeepKey(nIndex); result = keypool.vchPubKey; return true; } int64_t CWallet::GetOldestKeyPoolTime() { LOCK(cs_wallet); // If the keypool is empty, return if (setKeyPool.empty()) { return GetTime(); } // 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 std::runtime_error(std::string(__func__) + ": read oldest key in keypool failed"); } assert(keypool.vchPubKey.IsValid()); return keypool.nTime; } std::map CWallet::GetAddressBalances() { std::map balances; LOCK(cs_wallet); for (std::pair walletEntry : mapWallet) { CWalletTx *pcoin = &walletEntry.second; if (!pcoin->IsTrusted()) { continue; } if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) { continue; } int nDepth = pcoin->GetDepthInMainChain(); if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1)) { continue; } for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { CTxDestination addr; if (!IsMine(pcoin->tx->vout[i])) { continue; } if (!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr)) { continue; } CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->tx->vout[i].nValue; if (!balances.count(addr)) balances[addr] = 0; balances[addr] += n; } } return balances; } std::set> CWallet::GetAddressGroupings() { // mapWallet AssertLockHeld(cs_wallet); std::set> groupings; std::set grouping; for (std::pair walletEntry : mapWallet) { CWalletTx *pcoin = &walletEntry.second; if (pcoin->tx->vin.size() > 0) { bool any_mine = false; // Group all input addresses with each other. for (CTxIn txin : pcoin->tx->vin) { CTxDestination address; // If this input isn't mine, ignore it. if (!IsMine(txin)) { continue; } if (!ExtractDestination(mapWallet[txin.prevout.hash] .tx->vout[txin.prevout.n] .scriptPubKey, address)) { continue; } grouping.insert(address); any_mine = true; } // Group change with input addresses. if (any_mine) { for (CTxOut txout : pcoin->tx->vout) { if (IsChange(txout)) { CTxDestination txoutAddr; if (!ExtractDestination(txout.scriptPubKey, txoutAddr)) { continue; } grouping.insert(txoutAddr); } } } if (grouping.size() > 0) { groupings.insert(grouping); grouping.clear(); } } // Group lone addrs by themselves. for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) if (IsMine(pcoin->tx->vout[i])) { CTxDestination address; if (!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, address)) { continue; } grouping.insert(address); groupings.insert(grouping); grouping.clear(); } } // A set of pointers to groups of addresses. std::set *> uniqueGroupings; // Map addresses to the unique group containing it. std::map *> setmap; for (std::set _grouping : groupings) { // Make a set of all the groups hit by this new group. std::set *> hits; std::map *>::iterator it; for (CTxDestination address : _grouping) { if ((it = setmap.find(address)) != setmap.end()) hits.insert((*it).second); } // Merge all hit groups into a new single group and delete old groups. std::set *merged = new std::set(_grouping); for (std::set *hit : hits) { merged->insert(hit->begin(), hit->end()); uniqueGroupings.erase(hit); delete hit; } uniqueGroupings.insert(merged); // Update setmap. for (CTxDestination element : *merged) { setmap[element] = merged; } } std::set> ret; for (std::set *uniqueGrouping : uniqueGroupings) { ret.insert(*uniqueGrouping); delete uniqueGrouping; } return ret; } CAmount CWallet::GetAccountBalance(const std::string &strAccount, int nMinDepth, const isminefilter &filter) { CWalletDB walletdb(strWalletFile); return GetAccountBalance(walletdb, strAccount, nMinDepth, filter); } CAmount CWallet::GetAccountBalance(CWalletDB &walletdb, const std::string &strAccount, int nMinDepth, const isminefilter &filter) { CAmount nBalance = 0; // Tally wallet transactions. for (std::map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx &wtx = (*it).second; if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) { continue; } CAmount nReceived, nSent, nFee; wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee, filter); if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) { nBalance += nReceived; } nBalance -= nSent + nFee; } // Tally internal accounting entries. nBalance += walletdb.GetAccountCreditDebit(strAccount); return nBalance; } std::set CWallet::GetAccountAddresses(const std::string &strAccount) const { LOCK(cs_wallet); std::set result; for (const std::pair &item : mapAddressBook) { const CTxDestination &address = item.first; const std::string &strName = item.second.name; if (strName == strAccount) { result.insert(address); } } return result; } bool CReserveKey::GetReservedKey(CPubKey &pubkey) { if (nIndex == -1) { CKeyPool keypool; pwallet->ReserveKeyFromKeyPool(nIndex, keypool); if (nIndex != -1) { vchPubKey = keypool.vchPubKey; } else { return false; } } assert(vchPubKey.IsValid()); pubkey = vchPubKey; return true; } void CReserveKey::KeepKey() { if (nIndex != -1) { pwallet->KeepKey(nIndex); } nIndex = -1; vchPubKey = CPubKey(); } void CReserveKey::ReturnKey() { if (nIndex != -1) { pwallet->ReturnKey(nIndex); } nIndex = -1; vchPubKey = CPubKey(); } void CWallet::GetAllReserveKeys(std::set &setAddress) const { setAddress.clear(); CWalletDB walletdb(strWalletFile); LOCK2(cs_main, cs_wallet); for (const int64_t &id : setKeyPool) { CKeyPool keypool; if (!walletdb.ReadPool(id, keypool)) { throw std::runtime_error(std::string(__func__) + ": read failed"); } assert(keypool.vchPubKey.IsValid()); CKeyID keyID = keypool.vchPubKey.GetID(); if (!HaveKey(keyID)) { throw std::runtime_error(std::string(__func__) + ": unknown key in key pool"); } setAddress.insert(keyID); } } void CWallet::UpdatedTransaction(const uint256 &hashTx) { LOCK(cs_wallet); // Only notify UI if this transaction is in this wallet. std::map::const_iterator mi = mapWallet.find(hashTx); if (mi != mapWallet.end()) { NotifyTransactionChanged(this, hashTx, CT_UPDATED); } } void CWallet::GetScriptForMining(boost::shared_ptr &script) { boost::shared_ptr rKey(new CReserveKey(this)); CPubKey pubkey; if (!rKey->GetReservedKey(pubkey)) { return; } script = rKey; script->reserveScript = CScript() << ToByteVector(pubkey) << OP_CHECKSIG; } void CWallet::LockCoin(const COutPoint &output) { // setLockedCoins AssertLockHeld(cs_wallet); setLockedCoins.insert(output); } void CWallet::UnlockCoin(const COutPoint &output) { // setLockedCoins AssertLockHeld(cs_wallet); setLockedCoins.erase(output); } void CWallet::UnlockAllCoins() { // setLockedCoins AssertLockHeld(cs_wallet); setLockedCoins.clear(); } bool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const { // setLockedCoins AssertLockHeld(cs_wallet); COutPoint outpt(hash, n); return setLockedCoins.count(outpt) > 0; } void CWallet::ListLockedCoins(std::vector &vOutpts) { // setLockedCoins AssertLockHeld(cs_wallet); for (std::set::iterator it = setLockedCoins.begin(); it != setLockedCoins.end(); it++) { COutPoint outpt = (*it); vOutpts.push_back(outpt); } } /** @} */ // end of Actions class CAffectedKeysVisitor : public boost::static_visitor { private: const CKeyStore &keystore; std::vector &vKeys; public: CAffectedKeysVisitor(const CKeyStore &keystoreIn, std::vector &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {} void Process(const CScript &script) { txnouttype type; std::vector vDest; int nRequired; if (ExtractDestinations(script, type, vDest, nRequired)) { for (const CTxDestination &dest : vDest) { boost::apply_visitor(*this, dest); } } } void operator()(const CKeyID &keyId) { if (keystore.HaveKey(keyId)) { vKeys.push_back(keyId); } } void operator()(const CScriptID &scriptId) { CScript script; if (keystore.GetCScript(scriptId, script)) { Process(script); } } void operator()(const CNoDestination &none) {} }; void CWallet::GetKeyBirthTimes( std::map &mapKeyBirth) const { // mapKeyMetadata AssertLockHeld(cs_wallet); mapKeyBirth.clear(); // Get birth times for keys with metadata. for (const auto &entry : mapKeyMetadata) { if (entry.second.nCreateTime) { mapKeyBirth[entry.first] = entry.second.nCreateTime; } } // Map in which we'll infer heights of other keys the tip can be // reorganized; use a 144-block safety margin. CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; std::map mapKeyFirstBlock; std::set setKeys; GetKeys(setKeys); for (const CKeyID &keyid : setKeys) { if (mapKeyBirth.count(keyid) == 0) { mapKeyFirstBlock[keyid] = pindexMax; } } setKeys.clear(); // If there are no such keys, we're done. if (mapKeyFirstBlock.empty()) { return; } // Find first block that affects those keys, if there are any left. std::vector vAffected; for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); it++) { // Iterate over all wallet transactions... const CWalletTx &wtx = (*it).second; BlockMap::const_iterator blit = mapBlockIndex.find(wtx.hashBlock); if (blit != mapBlockIndex.end() && chainActive.Contains(blit->second)) { // ... which are already in a block. int nHeight = blit->second->nHeight; for (const CTxOut &txout : wtx.tx->vout) { // Iterate over all their outputs... CAffectedKeysVisitor(*this, vAffected) .Process(txout.scriptPubKey); for (const CKeyID &keyid : vAffected) { // ... and all their affected keys. std::map::iterator rit = mapKeyFirstBlock.find(keyid); if (rit != mapKeyFirstBlock.end() && nHeight < rit->second->nHeight) { rit->second = blit->second; } } vAffected.clear(); } } } // Extract block timestamps for those keys. for (std::map::const_iterator it = mapKeyFirstBlock.begin(); it != mapKeyFirstBlock.end(); it++) { // Block times can be 2h off. mapKeyBirth[it->first] = it->second->GetBlockTime() - 7200; } } bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, const std::string &value) { if (boost::get(&dest)) { return false; } mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); if (!fFileBacked) { return true; } return CWalletDB(strWalletFile) .WriteDestData(CBitcoinAddress(dest).ToString(), key, value); } bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key) { if (!mapAddressBook[dest].destdata.erase(key)) { return false; } if (!fFileBacked) { return true; } return CWalletDB(strWalletFile) .EraseDestData(CBitcoinAddress(dest).ToString(), key); } bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value) { mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); return true; } bool CWallet::GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const { std::map::const_iterator i = mapAddressBook.find(dest); if (i != mapAddressBook.end()) { CAddressBookData::StringMap::const_iterator j = i->second.destdata.find(key); if (j != i->second.destdata.end()) { if (value) { *value = j->second; } return true; } } return false; } std::string CWallet::GetWalletHelpString(bool showDebug) { std::string strUsage = HelpMessageGroup(_("Wallet options:")); strUsage += HelpMessageOpt( "-disablewallet", _("Do not load the wallet and disable wallet RPC calls")); strUsage += HelpMessageOpt( "-keypool=", strprintf(_("Set key pool size to (default: %u)"), DEFAULT_KEYPOOL_SIZE)); strUsage += HelpMessageOpt( "-fallbackfee=", strprintf(_("A fee rate (in %s/kB) that will be used when fee " "estimation has insufficient data (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE))); strUsage += HelpMessageOpt( "-mintxfee=", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee " "for transaction creation (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE))); strUsage += HelpMessageOpt( "-paytxfee=", strprintf( _("Fee (in %s/kB) to add to transactions you send (default: %s)"), CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK()))); strUsage += HelpMessageOpt( "-rescan", _("Rescan the block chain for missing wallet transactions on startup")); strUsage += HelpMessageOpt( "-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup")); if (showDebug) { strUsage += HelpMessageOpt( "-sendfreetransactions", strprintf(_("Send transactions as zero-fee transactions if " "possible (default: %u)"), DEFAULT_SEND_FREE_TRANSACTIONS)); } strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending " "transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); strUsage += HelpMessageOpt("-txconfirmtarget=", strprintf(_("If paytxfee is not set, include enough fee " "so transactions begin confirmation on " "average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET)); strUsage += HelpMessageOpt( "-usehd", _("Use hierarchical deterministic key generation (HD) after BIP32. " "Only has effect during wallet creation/first start") + " " + strprintf(_("(default: %u)"), DEFAULT_USE_HD_WALLET)); strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup")); strUsage += HelpMessageOpt("-wallet=", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); strUsage += HelpMessageOpt( "-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST)); strUsage += HelpMessageOpt("-walletnotify=", _("Execute command when a wallet transaction " "changes (%s in cmd is replaced by TxID)")); strUsage += HelpMessageOpt( "-zapwallettxes=", _("Delete all wallet transactions and only recover those parts of the " "blockchain through -rescan on startup") + " " + _("(1 = keep tx meta data e.g. account owner and payment " "request information, 2 = drop tx meta data)")); if (showDebug) { strUsage += HelpMessageGroup(_("Wallet debugging/testing options:")); strUsage += HelpMessageOpt( "-dblogsize=", strprintf("Flush wallet database activity from memory to disk log " "every megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE)); strUsage += HelpMessageOpt( "-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET)); strUsage += HelpMessageOpt( "-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db " "environment (default: %u)", DEFAULT_WALLET_PRIVDB)); strUsage += HelpMessageOpt( "-walletrejectlongchains", strprintf(_("Wallet will not create transactions that violate " "mempool chain limits (default: %u)"), DEFAULT_WALLET_REJECT_LONG_CHAINS)); } return strUsage; } CWallet *CWallet::CreateWalletFromFile(const std::string walletFile) { // Needed to restore wallet transaction meta data after -zapwallettxes std::vector vWtx; if (GetBoolArg("-zapwallettxes", false)) { uiInterface.InitMessage(_("Zapping all transactions from wallet...")); CWallet *tempWallet = new CWallet(walletFile); DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx); if (nZapWalletRet != DB_LOAD_OK) { InitError( strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); return nullptr; } delete tempWallet; tempWallet = nullptr; } uiInterface.InitMessage(_("Loading wallet...")); int64_t nStart = GetTimeMillis(); bool fFirstRun = true; CWallet *walletInstance = new CWallet(walletFile); DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun); if (nLoadWalletRet != DB_LOAD_OK) { if (nLoadWalletRet == DB_CORRUPT) { InitError( strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); return nullptr; } if (nLoadWalletRet == DB_NONCRITICAL_ERROR) { InitWarning(strprintf( _("Error reading %s! All keys read correctly, but transaction " "data" " or address book entries might be missing or incorrect."), walletFile)); } else if (nLoadWalletRet == DB_TOO_NEW) { InitError(strprintf( _("Error loading %s: Wallet requires newer version of %s"), walletFile, _(PACKAGE_NAME))); return nullptr; } else if (nLoadWalletRet == DB_NEED_REWRITE) { InitError(strprintf( _("Wallet needed to be rewritten: restart %s to complete"), _(PACKAGE_NAME))); return nullptr; } else { InitError(strprintf(_("Error loading %s"), walletFile)); return nullptr; } } if (GetBoolArg("-upgradewallet", fFirstRun)) { int nMaxVersion = GetArg("-upgradewallet", 0); // The -upgradewallet without argument case if (nMaxVersion == 0) { LogPrintf("Performing wallet upgrade to %i\n", FEATURE_LATEST); nMaxVersion = CLIENT_VERSION; // permanently upgrade the wallet immediately walletInstance->SetMinVersion(FEATURE_LATEST); } else { LogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion); } if (nMaxVersion < walletInstance->GetVersion()) { InitError(_("Cannot downgrade wallet")); return nullptr; } walletInstance->SetMaxVersion(nMaxVersion); } if (fFirstRun) { // Create new keyUser and set as default key. if (GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) && !walletInstance->IsHDEnabled()) { // Generate a new master key. CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey(); if (!walletInstance->SetHDMasterKey(masterPubKey)) { throw std::runtime_error(std::string(__func__) + ": Storing master key failed"); } } CPubKey newDefaultKey; if (walletInstance->GetKeyFromPool(newDefaultKey)) { walletInstance->SetDefaultKey(newDefaultKey); if (!walletInstance->SetAddressBook( walletInstance->vchDefaultKey.GetID(), "", "receive")) { InitError(_("Cannot write default address") += "\n"); return nullptr; } } walletInstance->SetBestChain(chainActive.GetLocator()); } else if (IsArgSet("-usehd")) { bool useHD = GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET); if (walletInstance->IsHDEnabled() && !useHD) { InitError(strprintf(_("Error loading %s: You can't disable HD on a " "already existing HD wallet"), walletFile)); return nullptr; } if (!walletInstance->IsHDEnabled() && useHD) { InitError(strprintf(_("Error loading %s: You can't enable HD on a " "already existing non-HD wallet"), walletFile)); return nullptr; } } LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart); RegisterValidationInterface(walletInstance); CBlockIndex *pindexRescan = chainActive.Tip(); if (GetBoolArg("-rescan", false)) { pindexRescan = chainActive.Genesis(); } else { CWalletDB walletdb(walletFile); CBlockLocator locator; if (walletdb.ReadBestBlock(locator)) { pindexRescan = FindForkInGlobalIndex(chainActive, locator); } else { pindexRescan = chainActive.Genesis(); } } if (chainActive.Tip() && chainActive.Tip() != pindexRescan) { // We can't rescan beyond non-pruned blocks, stop and throw an error. // This might happen if a user uses a old wallet within a pruned node or // if he ran -disablewallet for a longer time, then decided to // re-enable. if (fPruneMode) { CBlockIndex *block = chainActive.Tip(); while (block && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA) && block->pprev->nTx > 0 && pindexRescan != block) { block = block->pprev; } if (pindexRescan != block) { InitError(_("Prune: last wallet synchronisation goes beyond " "pruned data. You need to -reindex (download the " "whole blockchain again in case of pruned node)")); return nullptr; } } uiInterface.InitMessage(_("Rescanning...")); LogPrintf("Rescanning last %i blocks (from block %i)...\n", chainActive.Height() - pindexRescan->nHeight, pindexRescan->nHeight); nStart = GetTimeMillis(); walletInstance->ScanForWalletTransactions(pindexRescan, true); LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart); walletInstance->SetBestChain(chainActive.GetLocator()); CWalletDB::IncrementUpdateCounter(); // Restore wallet transaction metadata after -zapwallettxes=1 if (GetBoolArg("-zapwallettxes", false) && GetArg("-zapwallettxes", "1") != "2") { CWalletDB walletdb(walletFile); for (const CWalletTx &wtxOld : vWtx) { uint256 txid = wtxOld.GetId(); std::map::iterator mi = walletInstance->mapWallet.find(txid); if (mi != walletInstance->mapWallet.end()) { const CWalletTx *copyFrom = &wtxOld; CWalletTx *copyTo = &mi->second; copyTo->mapValue = copyFrom->mapValue; copyTo->vOrderForm = copyFrom->vOrderForm; copyTo->nTimeReceived = copyFrom->nTimeReceived; copyTo->nTimeSmart = copyFrom->nTimeSmart; copyTo->fFromMe = copyFrom->fFromMe; copyTo->strFromAccount = copyFrom->strFromAccount; copyTo->nOrderPos = copyFrom->nOrderPos; walletdb.WriteTx(*copyTo); } } } } walletInstance->SetBroadcastTransactions( GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST)); LOCK(walletInstance->cs_wallet); LogPrintf("setKeyPool.size() = %u\n", walletInstance->GetKeyPoolSize()); LogPrintf("mapWallet.size() = %u\n", walletInstance->mapWallet.size()); LogPrintf("mapAddressBook.size() = %u\n", walletInstance->mapAddressBook.size()); return walletInstance; } bool CWallet::InitLoadWallet() { if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { pwalletMain = nullptr; LogPrintf("Wallet disabled!\n"); return true; } std::string walletFile = GetArg("-wallet", DEFAULT_WALLET_DAT); CWallet *const pwallet = CreateWalletFromFile(walletFile); if (!pwallet) { return false; } pwalletMain = pwallet; return true; } std::atomic CWallet::fFlushThreadRunning(false); void CWallet::postInitProcess(boost::thread_group &threadGroup) { // Add wallet transactions that aren't already in a block to mempool. // Do this here as mempool requires genesis block to be loaded. ReacceptWalletTransactions(); // Run a thread to flush wallet periodically. if (!CWallet::fFlushThreadRunning.exchange(true)) { threadGroup.create_thread(ThreadFlushWalletDB); } } bool CWallet::ParameterInteraction() { if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { return true; } if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && SoftSetBoolArg("-walletbroadcast", false)) { LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting " "-walletbroadcast=0\n", __func__); } if (GetBoolArg("-salvagewallet", false) && SoftSetBoolArg("-rescan", true)) { // Rewrite just private keys: rescan to find transactions LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting " "-rescan=1\n", __func__); } // -zapwallettx implies a rescan if (GetBoolArg("-zapwallettxes", false) && SoftSetBoolArg("-rescan", true)) { LogPrintf("%s: parameter interaction: -zapwallettxes= -> setting " "-rescan=1\n", __func__); } if (GetBoolArg("-sysperms", false)) { return InitError("-sysperms is not allowed in combination with enabled " "wallet functionality"); } if (GetArg("-prune", 0) && GetBoolArg("-rescan", false)) { return InitError( _("Rescans are not possible in pruned mode. You will need to use " "-reindex which will download the whole blockchain again.")); } if (::minRelayTxFee.GetFeePerK() > HIGH_TX_FEE_PER_KB) { InitWarning( AmountHighWarn("-minrelaytxfee") + " " + _("The wallet will avoid paying less than the minimum relay fee.")); } if (IsArgSet("-mintxfee")) { CAmount n = 0; if (!ParseMoney(GetArg("-mintxfee", ""), n) || 0 == n) { return InitError(AmountErrMsg("mintxfee", GetArg("-mintxfee", ""))); } if (n > HIGH_TX_FEE_PER_KB) { InitWarning(AmountHighWarn("-mintxfee") + " " + _("This is the minimum transaction fee you pay on " "every transaction.")); } CWallet::minTxFee = CFeeRate(n); } if (IsArgSet("-fallbackfee")) { CAmount nFeePerK = 0; if (!ParseMoney(GetArg("-fallbackfee", ""), nFeePerK)) { return InitError( strprintf(_("Invalid amount for -fallbackfee=: '%s'"), GetArg("-fallbackfee", ""))); } if (nFeePerK > HIGH_TX_FEE_PER_KB) { InitWarning(AmountHighWarn("-fallbackfee") + " " + _("This is the transaction fee you may pay when fee " "estimates are not available.")); } CWallet::fallbackFee = CFeeRate(nFeePerK); } if (IsArgSet("-paytxfee")) { CAmount nFeePerK = 0; if (!ParseMoney(GetArg("-paytxfee", ""), nFeePerK)) { return InitError(AmountErrMsg("paytxfee", GetArg("-paytxfee", ""))); } if (nFeePerK > HIGH_TX_FEE_PER_KB) { InitWarning(AmountHighWarn("-paytxfee") + " " + _("This is the transaction fee you will pay if you " "send a transaction.")); } payTxFee = CFeeRate(nFeePerK, 1000); if (payTxFee < ::minRelayTxFee) { return InitError( strprintf(_("Invalid amount for -paytxfee=: '%s' (must " "be at least %s)"), GetArg("-paytxfee", ""), ::minRelayTxFee.ToString())); } } if (IsArgSet("-maxtxfee")) { CAmount nMaxFee = 0; if (!ParseMoney(GetArg("-maxtxfee", ""), nMaxFee)) { return InitError(AmountErrMsg("maxtxfee", GetArg("-maxtxfee", ""))); } if (nMaxFee > HIGH_MAX_TX_FEE) { InitWarning(_("-maxtxfee is set very high! Fees this large could " "be paid on a single transaction.")); } maxTxFee = nMaxFee; if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee) { return InitError( strprintf(_("Invalid amount for -maxtxfee=: '%s' (must " "be at least the minrelay fee of %s to prevent " "stuck transactions)"), GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString())); } } nTxConfirmTarget = GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); fSendFreeTransactions = GetBoolArg("-sendfreetransactions", DEFAULT_SEND_FREE_TRANSACTIONS); if (fSendFreeTransactions && GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) <= 0) { return InitError("Creation of free transactions with their relay " "disabled is not supported."); } return true; } bool CWallet::BackupWallet(const std::string &strDest) { if (!fFileBacked) { return false; } while (true) { { LOCK(bitdb.cs_db); if (!bitdb.mapFileUseCount.count(strWalletFile) || bitdb.mapFileUseCount[strWalletFile] == 0) { // Flush log data to the dat file. bitdb.CloseDb(strWalletFile); bitdb.CheckpointLSN(strWalletFile); bitdb.mapFileUseCount.erase(strWalletFile); // Copy wallet file. boost::filesystem::path pathSrc = GetDataDir() / strWalletFile; boost::filesystem::path pathDest(strDest); if (boost::filesystem::is_directory(pathDest)) { pathDest /= strWalletFile; } try { #if BOOST_VERSION >= 104000 boost::filesystem::copy_file( pathSrc, pathDest, boost::filesystem::copy_option::overwrite_if_exists); #else boost::filesystem::copy_file(pathSrc, pathDest); #endif LogPrintf("copied %s to %s\n", strWalletFile, pathDest.string()); return true; } catch (const boost::filesystem::filesystem_error &e) { LogPrintf("error copying %s to %s - %s\n", strWalletFile, pathDest.string(), e.what()); return false; } } } MilliSleep(100); } return false; } CKeyPool::CKeyPool() { nTime = GetTime(); } CKeyPool::CKeyPool(const CPubKey &vchPubKeyIn) { nTime = GetTime(); vchPubKey = vchPubKeyIn; } CWalletKey::CWalletKey(int64_t nExpires) { nTimeCreated = (nExpires ? GetTime() : 0); nTimeExpires = nExpires; } void CMerkleTx::SetMerkleBranch(const CBlockIndex *pindex, int posInBlock) { // Update the tx's hashBlock hashBlock = pindex->GetBlockHash(); // Set the position of the transaction in the block. nIndex = posInBlock; } int CMerkleTx::GetDepthInMainChain(const CBlockIndex *&pindexRet) const { if (hashUnset()) { return 0; } AssertLockHeld(cs_main); // Find the block it claims to be in. BlockMap::iterator mi = mapBlockIndex.find(hashBlock); if (mi == mapBlockIndex.end()) { return 0; } CBlockIndex *pindex = (*mi).second; if (!pindex || !chainActive.Contains(pindex)) { return 0; } pindexRet = pindex; return ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1); } int CMerkleTx::GetBlocksToMaturity() const { if (!IsCoinBase()) { return 0; } return std::max(0, (COINBASE_MATURITY + 1) - GetDepthInMainChain()); } bool CMerkleTx::AcceptToMemoryPool(const CAmount &nAbsurdFee, CValidationState &state) { return ::AcceptToMemoryPool(GetConfig(), mempool, state, tx, true, nullptr, nullptr, false, nAbsurdFee); }