diff --git a/.gitignore b/.gitignore index fbe1f8f..2d8f6ad 100644 --- a/.gitignore +++ b/.gitignore @@ -68,7 +68,6 @@ libconftest.dylib* *.pyc *.o *.o-* -*.patch .xdna *.a *.pb.cc diff --git a/COPYING b/COPYING index 07ac687..308d671 100644 --- a/COPYING +++ b/COPYING @@ -1,7 +1,7 @@ Copyright (c) 2009-2015 Bitcoin Developers Copyright (c) 2014-2015 Dash Developers Copyright (c) 2015-2017 PIVX Developers -Copyright (c) 2017-2018 The XDNA Core developers +Copyright (c) 2017-2019 The XDNA Core developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile.am b/Makefile.am index 57a80f8..7b0be09 100644 --- a/Makefile.am +++ b/Makefile.am @@ -160,6 +160,7 @@ $(BITCOIN_CLI_BIN): FORCE $(MAKE) -C src $(@F) if USE_LCOV +LCOV_FILTER_PATTERN=-p "/usr/include/" -p "/usr/lib/" -p "src/leveldb/" -p "src/univalue" -p "src/secp256k1" baseline.info: $(LCOV) -c -i -d $(abs_builddir)/src -o $@ diff --git a/README.md b/README.md index 9776b11..6f39f23 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ More information at [xdna.io](https://xdna.io) Visit our ANN thread at [BitcoinT Name & tickerXDNA Consensus algorithmPoW/POS POW phase duration1440000 blocks -PoW block rewardDynamic from 4 to 109 coins with lowering by 6.94% each month +PoW block rewardDynamic from 4 to 511 XDNA Instamine protectionFirst 720 blocks Hashing algorithmHEX Estimated XDNA supply during POW138,000,000 @@ -34,20 +34,20 @@ More information at [xdna.io](https://xdna.io) Visit our ANN thread at [BitcoinT - - - - - - - - - - - - - - - + + + + + + + + + + + + + + +
LevelNetwork hashrate (Gh/s)Block reward
1103.6
2204.4
3306.2
4508.8
58012.2
613016.5
721021.7
834027.8
955034.8
1089042.5
111.44051.2
122.33060.7
133.77071.1
146.10082.4
159.87094.5
1104
2205
3307
45010
58014
613019
721025
834032
955040
1089049
111.44059
122.33070
133.77082
146.10095
159.870109
-![](https://github.com/XDNA-Core/XDNA/blob/master/doc/POW.png) +![](doc/POW.png) diff --git a/configure.ac b/configure.ac index 714aaab..e9e90cb 100644 --- a/configure.ac +++ b/configure.ac @@ -1,9 +1,9 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) -define(_CLIENT_VERSION_MAJOR, 1) -define(_CLIENT_VERSION_MINOR, 3) +define(_CLIENT_VERSION_MAJOR, 2) +define(_CLIENT_VERSION_MINOR, 0) define(_CLIENT_VERSION_REVISION, 0) -define(_CLIENT_VERSION_BUILD, 1) +define(_CLIENT_VERSION_BUILD, 0) define(_CLIENT_VERSION_IS_RELEASE, true) define(_COPYRIGHT_YEAR, 2019) AC_INIT([XDNA Core],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_REVISION],[www.xdna.io],[xdna]) @@ -17,6 +17,12 @@ BITCOIN_GUI_NAME=xdna-qt BITCOIN_CLI_NAME=xdna-cli BITCOIN_TX_NAME=xdna-tx +dnl Unless the user specified ARFLAGS, force it to be cr +AC_ARG_VAR(ARFLAGS, [Flags for the archiver, defaults to if not set]) +if test "x${ARFLAGS+set}" != "xset"; then + ARFLAGS="cr" +fi + AC_CANONICAL_HOST AH_TOP([#ifndef XDNA_CONFIG_H]) @@ -54,7 +60,9 @@ case $host in ;; esac dnl Require C++11 compiler (no GNU extensions) -AX_CXX_COMPILE_STDCXX([11], [noext], [mandatory]) +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. @@ -84,16 +92,14 @@ AC_PATH_PROG(HEXDUMP,hexdump) AC_PATH_TOOL(READELF, readelf) AC_PATH_TOOL(CPPFILT, c++filt) AC_PATH_TOOL(OBJCOPY, objcopy) +AC_PATH_PROG(DOXYGEN, doxygen) +if test -z "$DOXYGEN"; then + AC_MSG_WARN([Doxygen not found]) +fi +AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"]) AC_ARG_VAR(PYTHONPATH, Augments the default search path for python module files) -# This m4 will only be used if a system copy cannot be found. This is helpful -# on systems where autotools are installed but the pkg-config macros are not in -# a default location. It is currently used for building on OSX where autotools -# are preinstalled but pkg-config comes from macports or homebrew. It should -# probably be removed when building on <= 10.6 is no longer supported. -m4_include([pkg.m4]) - # Enable wallet AC_ARG_ENABLE([wallet], [AS_HELP_STRING([--enable-wallet], @@ -118,10 +124,15 @@ AC_ARG_ENABLE(tests, [use_tests=$enableval], [use_tests=no]) -AC_ARG_WITH([comparison-tool], - AS_HELP_STRING([--with-comparison-tool],[path to java comparison tool (requires --enable-tests)]), - [use_comparison_tool=$withval], - [use_comparison_tool=no]) +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=no]) AC_ARG_ENABLE([comparison-tool-reorg-tests], AS_HELP_STRING([--enable-comparison-tool-reorg-tests],[enable expensive reorg tests in the comparison tool (default no)]), @@ -158,11 +169,12 @@ AC_ARG_ENABLE([lcov], [use_lcov=yes], [use_lcov=no]) -AC_ARG_ENABLE([glibc-back-compat], - [AS_HELP_STRING([--enable-glibc-back-compat], - [enable backwards compatibility with glibc and libstdc++])], - [use_glibc_compat=$enableval], - [use_glibc_compat=no]) +AC_ARG_ENABLE([lcov-branch-coverage], + [AS_HELP_STRING([--enable-lcov-branch-coverage], + [enable lcov testing branch coverage (default is no)])], + [use_lcov_branch=yes], + [use_lcov_branch=no]) + AC_ARG_ENABLE([zmq], [AS_HELP_STRING([--disable-zmq], @@ -672,7 +684,43 @@ 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_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 + #include + #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 + ]],[[ + #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 @@ -814,6 +862,12 @@ AC_CHECK_LIB([crypto],[RAND_egd],[],[ 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 @@ -821,7 +875,7 @@ if test x$system_univalue != xno ; then m4_ifdef( [PKG_CHECK_MODULES], [ - PKG_CHECK_MODULES([UNIVALUE],[libunivalue],[found_univalue=yes],[true]) + PKG_CHECK_MODULES([UNIVALUE],[libunivalue >= 1.0.4],[found_univalue=yes],[true]) ] ) else @@ -835,6 +889,7 @@ if test x$system_univalue != xno ; then 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 @@ -842,11 +897,14 @@ if test x$system_univalue != xno ; then fi fi -if test x$system_univalue = xno ; then +if test x$need_bundled_univalue = xyes ; then UNIVALUE_CFLAGS='-I$(srcdir)/univalue/include' UNIVALUE_LIBS='univalue/libunivalue.la' fi -AM_CONDITIONAL([EMBEDDED_UNIVALUE],[test x$system_univalue = xno]) + +fi + +AM_CONDITIONAL([EMBEDDED_UNIVALUE],[test x$need_bundled_univalue = xyes]) AC_SUBST(UNIVALUE_CFLAGS) AC_SUBST(UNIVALUE_LIBS) @@ -1073,7 +1131,7 @@ PKGCONFIG_LIBDIR_TEMP="$PKG_CONFIG_LIBDIR" unset PKG_CONFIG_LIBDIR PKG_CONFIG_LIBDIR="$PKGCONFIG_LIBDIR_TEMP" -if test x$system_univalue = xno; then +if test x$need_bundled_univalue = xyes; then AC_CONFIG_SUBDIRS([src/univalue]) fi diff --git a/depends/Makefile b/depends/Makefile index dedb067..0ddd348 100644 --- a/depends/Makefile +++ b/depends/Makefile @@ -134,6 +134,7 @@ $(host_prefix)/share/config.site : config.site.in $(host_prefix)/.stamp_$(final_ -e 's|@CXXFLAGS@|$(strip $(host_CXXFLAGS) $(host_$(release_type)_CXXFLAGS))|' \ -e 's|@CPPFLAGS@|$(strip $(host_CPPFLAGS) $(host_$(release_type)_CPPFLAGS))|' \ -e 's|@LDFLAGS@|$(strip $(host_LDFLAGS) $(host_$(release_type)_LDFLAGS))|' \ + -e 's|@allow_host_packages@|$(ALLOW_HOST_PACKAGES)|' \ -e 's|@no_qt@|$(NO_QT)|' \ -e 's|@no_wallet@|$(NO_WALLET)|' \ -e 's|@no_upnp@|$(NO_UPNP)|' \ diff --git a/depends/README.md b/depends/README.md index 6053c53..99eef19 100644 --- a/depends/README.md +++ b/depends/README.md @@ -28,6 +28,22 @@ Common `host-platform-triplets` for cross compilation are: No other options are needed, the paths are automatically configured. +Install the required dependencies: Ubuntu & Debian +-------------------------------------------------- + +For macOS cross compilation: + + sudo apt-get install curl librsvg2-bin libtiff-tools bsdmainutils cmake imagemagick libcap-dev libz-dev libbz2-dev python-setuptools + +For Win32/Win64 cross compilation: + +- see [build-windows.md](../doc/build-windows.md#cross-compilation-for-ubuntu-and-windows-subsystem-for-linux) + +For linux (including i386, ARM) cross compilation: + + sudo apt-get install curl g++-aarch64-linux-gnu g++-4.8-aarch64-linux-gnu gcc-4.8-aarch64-linux-gnu binutils-aarch64-linux-gnu g++-arm-linux-gnueabihf g++-4.8-arm-linux-gnueabihf gcc-4.8-arm-linux-gnueabihf binutils-arm-linux-gnueabihf g++-4.8-multilib gcc-4.8-multilib binutils-gold bsdmainutils + + Dependency Options: The following can be set when running make: make FOO=bar diff --git a/depends/config.guess b/depends/config.guess index bbd48b6..f5710e4 100755 --- a/depends/config.guess +++ b/depends/config.guess @@ -837,10 +837,11 @@ EOF UNAME_PROCESSOR=`/usr/bin/uname -p` case ${UNAME_PROCESSOR} in amd64) - echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; - *) - echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + UNAME_PROCESSOR=x86_64 ;; + i386) + UNAME_PROCESSOR=i586 ;; esac + echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin @@ -1038,6 +1039,9 @@ EOF riscv32:Linux:*:* | riscv64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; + riscv32:Linux:*:* | riscv64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux-${LIBC} exit ;; @@ -1343,6 +1347,9 @@ EOF NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; + NSX-?:NONSTOP_KERNEL:*:*) + echo nsx-tandem-nsk${UNAME_RELEASE} + exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; diff --git a/depends/config.site.in b/depends/config.site.in index 3d7c9fd..0a4a9c3 100644 --- a/depends/config.site.in +++ b/depends/config.site.in @@ -13,10 +13,10 @@ fi if test -z $with_qt_translationdir; then with_qt_translationdir=$depends_prefix/translations fi -if test -z $with_qt_bindir; then +if test -z $with_qt_bindir && test -z "@no_qt@"; then with_qt_bindir=$depends_prefix/native/bin fi -if test -z $with_protoc_bindir; then +if test -z $with_protoc_bindir && test -z "@no_qt@"; then with_protoc_bindir=$depends_prefix/native/bin fi @@ -53,9 +53,10 @@ PKG_CONFIG="`which pkg-config` --static" # These two need to remain exported because pkg-config does not see them # otherwise. That means they must be unexported at the end of configure.ac to # avoid ruining the cache. Sigh. - -export PKG_CONFIG_LIBDIR=$depends_prefix/lib/pkgconfig -export PKG_CONFIG_PATH=$depends_prefix/share/pkgconfig +export PKG_CONFIG_PATH=$depends_prefix/share/pkgconfig:$depends_prefix/lib/pkgconfig +if test -z "@allow_host_packages@"; then + export PKGCONFIG_LIBDIR= +fi CPPFLAGS="-I$depends_prefix/include/ $CPPFLAGS" LDFLAGS="-L$depends_prefix/lib $LDFLAGS" diff --git a/depends/config.sub b/depends/config.sub index 7e792b4..38faf30 100755 --- a/depends/config.sub +++ b/depends/config.sub @@ -263,7 +263,7 @@ case $basic_machine in | fido | fr30 | frv | ft32 \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ - | i370 | i860 | i960 | ia64 \ + | i370 | i860 | i960 | ia16 | ia64 \ | ip2k | iq2000 \ | k1om \ | le32 | le64 \ @@ -315,6 +315,7 @@ case $basic_machine in | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | visium \ + | wasm32 \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) @@ -388,7 +389,7 @@ case $basic_machine in | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ - | i*86-* | i860-* | i960-* | ia64-* \ + | i*86-* | i860-* | i960-* | ia16-* | ia64-* \ | ip2k-* | iq2000-* \ | k1om-* \ | le32-* | le64-* \ @@ -446,6 +447,7 @@ case $basic_machine in | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | visium-* \ + | wasm32-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ @@ -653,6 +655,14 @@ case $basic_machine in basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` os=$os"spe" ;; + e500v[12]) + basic_machine=powerpc-unknown + os=$os"spe" + ;; + e500v[12]-*) + basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + os=$os"spe" + ;; ebmon29k) basic_machine=a29k-amd os=-ebmon @@ -948,6 +958,9 @@ case $basic_machine in nsr-tandem) basic_machine=nsr-tandem ;; + nsx-tandem) + basic_machine=nsx-tandem + ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf @@ -1243,6 +1256,9 @@ case $basic_machine in basic_machine=a29k-wrs os=-vxworks ;; + wasm32) + basic_machine=wasm32-unknown + ;; w65*) basic_machine=w65-wdc os=-none @@ -1641,6 +1657,9 @@ case $basic_machine in pru-*) os=-elf ;; + pru-*) + os=-elf + ;; *-be) os=-beos ;; diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk index 57d96e4..bf773cc 100644 --- a/depends/packages/boost.mk +++ b/depends/packages/boost.mk @@ -1,8 +1,8 @@ package=boost -$(package)_version=1_63_0 -$(package)_download_path=https://sourceforge.net/projects/boost/files/boost/1.63.0 +$(package)_version=1_64_0 +$(package)_download_path=https://dl.bintray.com/boostorg/release/1.64.0/source/ $(package)_file_name=$(package)_$($(package)_version).tar.bz2 -$(package)_sha256_hash=beae2529f759f6b3bf3f4969a19c2e9d6f0c503edcb2de4a61d1428519fcb3b0 +$(package)_sha256_hash=7bcc5caace97baa948931d712ea5f37038dbb1c5d89b43ad4def4ed7cb683332 define $(package)_set_vars $(package)_config_opts_release=variant=release diff --git a/depends/packages/dbus.mk b/depends/packages/dbus.mk index 90ddcb9..bbe0375 100644 --- a/depends/packages/dbus.mk +++ b/depends/packages/dbus.mk @@ -1,8 +1,8 @@ package=dbus -$(package)_version=1.10.14 -$(package)_download_path=http://dbus.freedesktop.org/releases/dbus +$(package)_version=1.10.18 +$(package)_download_path=https://dbus.freedesktop.org/releases/dbus $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=23238f70353e38ce5ca183ebc9525c0d97ac00ef640ad29cf794782af6e6a083 +$(package)_sha256_hash=6049ddd5f3f3e2618f615f1faeda0a115104423a7996b7aa73e2f36e38cc514a $(package)_dependencies=expat define $(package)_set_vars diff --git a/depends/packages/expat.mk b/depends/packages/expat.mk index 81a660e..7f48472 100644 --- a/depends/packages/expat.mk +++ b/depends/packages/expat.mk @@ -1,8 +1,8 @@ package=expat -$(package)_version=2.2.0 +$(package)_version=2.2.1 $(package)_download_path=https://downloads.sourceforge.net/project/expat/expat/$($(package)_version) $(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=d9e50ff2d19b3538bd2127902a89987474e1a4db8e43a66a4d1a712ab9a504ff +$(package)_sha256_hash=1868cadae4c82a018e361e2b2091de103cd820aaacb0d6cfa49bd2cd83978885 define $(package)_set_vars $(package)_config_opts=--disable-static diff --git a/depends/packages/gmp.mk b/depends/packages/gmp.mk new file mode 100644 index 0000000..afc3f39 --- /dev/null +++ b/depends/packages/gmp.mk @@ -0,0 +1,24 @@ +package=gmp +$(package)_version=6.1.2 +$(package)_download_path=https://gmplib.org/download/gmp +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 +$(package)_sha256_hash=5275bb04f4863a13516b2f39392ac5e272f5e1bb8057b18aec1c9b79d73d8fb2 + +define $(package)_set_vars +$(package)_config_opts=--disable-shared +$(package)_config_opts_mingw32=--enable-mingw +$(package)_config_opts_linux=--with-pic +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef + diff --git a/depends/packages/libevent.mk b/depends/packages/libevent.mk index 70f345e..5f622f8 100644 --- a/depends/packages/libevent.mk +++ b/depends/packages/libevent.mk @@ -1,15 +1,15 @@ package=libevent -$(package)_version=2.1.7 +$(package)_version=2.1.8-stable $(package)_download_path=https://github.com/libevent/libevent/archive/ -$(package)_file_name=release-$($(package)_version)-rc.tar.gz -$(package)_sha256_hash=548362d202e22fe24d4c3fad38287b4f6d683e6c21503341373b89785fa6f991 +$(package)_file_name=release-$($(package)_version).tar.gz +$(package)_sha256_hash=316ddb401745ac5d222d7c529ef1eada12f58f6376a66c1118eee803cb70f83d define $(package)_preprocess_cmds ./autogen.sh endef define $(package)_set_vars - $(package)_config_opts=--disable-shared --disable-openssl --disable-libevent-regress + $(package)_config_opts=--disable-shared --disable-openssl --disable-libevent-regress --disable-samples $(package)_config_opts_release=--disable-debug-mode $(package)_config_opts_linux=--with-pic endef diff --git a/depends/packages/native_ccache.mk b/depends/packages/native_ccache.mk index 4ed61a4..966804c 100644 --- a/depends/packages/native_ccache.mk +++ b/depends/packages/native_ccache.mk @@ -1,8 +1,8 @@ package=native_ccache -$(package)_version=3.3.3 +$(package)_version=3.3.4 $(package)_download_path=https://samba.org/ftp/ccache $(package)_file_name=ccache-$($(package)_version).tar.bz2 -$(package)_sha256_hash=2985bc5e32ebe38d2958d508eb54ddcad39eed909489c0c2988035214597ca54 +$(package)_sha256_hash=fa9d7f38367431bc86b19ad107d709ca7ecf1574fdacca01698bdf0a47cd8567 define $(package)_set_vars $(package)_config_opts= diff --git a/depends/packages/native_ds_store.mk b/depends/packages/native_ds_store.mk index 49f5829..116fa25 100644 --- a/depends/packages/native_ds_store.mk +++ b/depends/packages/native_ds_store.mk @@ -1,9 +1,8 @@ package=native_ds_store -$(package)_version=1.1.0 -$(package)_download_path=https://bitbucket.org/al45tair/ds_store/get -$(package)_download_file=v$($(package)_version).tar.bz2 -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=921596764d71d1bbd3297a90ef6d286f718794d667e4f81d91d14053525d64c1 +$(package)_version=1.1.2 +$(package)_download_path=https://github.com/al45tair/ds_store/archive/ +$(package)_file_name=v$($(package)_version).tar.gz +$(package)_sha256_hash=3b3ecb7bf0a5157f5b6010bc3af7c141fb0ad3527084e63336220d22744bc20c $(package)_install_libdir=$(build_prefix)/lib/python/dist-packages $(package)_dependencies=native_biplist diff --git a/depends/packages/native_mac_alias.mk b/depends/packages/native_mac_alias.mk index 85a8a40..488ec8b 100644 --- a/depends/packages/native_mac_alias.mk +++ b/depends/packages/native_mac_alias.mk @@ -1,14 +1,13 @@ package=native_mac_alias -$(package)_version=1.1.0 -$(package)_download_path=https://bitbucket.org/al45tair/mac_alias/get -$(package)_download_file=v$($(package)_version).tar.bz2 -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=87ad827e66790028361e43fc754f68ed041a9bdb214cca03c853f079b04fb120 +$(package)_version=2.0.6 +$(package)_download_path=https://github.com/al45tair/mac_alias/archive/ +$(package)_file_name=v$($(package)_version).tar.gz +$(package)_sha256_hash=78a3332d9a597eebf09ae652d38ad1e263b28db5c2e6dd725fad357b03b77371 $(package)_install_libdir=$(build_prefix)/lib/python/dist-packages $(package)_patches=python3.patch define $(package)_preprocess_cmds - patch -p1 < $($(package)_patch_dir)/python3.patch + patch -p1 < $($(package)_patch_dir)/python3.patch endef define $(package)_build_cmds diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index 11c2e55..c82944d 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -1,4 +1,4 @@ -packages:=boost openssl libevent zeromq +packages:=boost openssl libevent zeromq gmp native_packages := native_ccache qt_native_packages = native_protobuf diff --git a/depends/packages/qrencode.mk b/depends/packages/qrencode.mk index 7b21247..44fdf1c 100644 --- a/depends/packages/qrencode.mk +++ b/depends/packages/qrencode.mk @@ -1,7 +1,7 @@ package=qrencode $(package)_version=3.4.4 $(package)_download_path=https://fukuchi.org/works/qrencode/ -$(package)_file_name=qrencode-$(qrencode_version).tar.bz2 +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 $(package)_sha256_hash=efe5188b1ddbcbf98763b819b146be6a90481aac30cfc8d858ab78a19cde1fa5 define $(package)_set_vars diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index 858898b..cd0d6cc 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -1,6 +1,6 @@ PACKAGE=qt $(package)_version=5.7.1 -$(package)_download_path=http://download.qt.io/official_releases/qt/5.7/$($(package)_version)/submodules +$(package)_download_path=https://download.qt.io/archive/qt/5.7/$($(package)_version)/submodules $(package)_suffix=opensource-src-$($(package)_version).tar.gz $(package)_file_name=qtbase-$($(package)_suffix) $(package)_sha256_hash=95f83e532d23b3ddbde7973f380ecae1bac13230340557276f75f2e37984e410 diff --git a/depends/packages/zeromq.mk b/depends/packages/zeromq.mk index 01146c2..3f97221 100644 --- a/depends/packages/zeromq.mk +++ b/depends/packages/zeromq.mk @@ -1,19 +1,18 @@ package=zeromq -$(package)_version=4.1.5 -$(package)_download_path=https://github.com/zeromq/zeromq4-1/releases/download/v$($(package)_version)/ +$(package)_version=4.2.2 +$(package)_download_path=https://github.com/zeromq/libzmq/releases/download/v$($(package)_version)/ $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=04aac57f081ffa3a2ee5ed04887be9e205df3a7ddade0027460b8042432bdbcf -$(package)_patches=9114d3957725acd34aa8b8d011585812f3369411.patch 9e6745c12e0b100cd38acecc16ce7db02905e27c.patch +$(package)_sha256_hash=5b23f4ca9ef545d5bd3af55d305765e3ee06b986263b31967435d285a3e6df6b +$(package)_patches=0001-fix-build-with-older-mingw64.patch define $(package)_set_vars - $(package)_config_opts=--without-documentation --disable-shared --without-libsodium --disable-curve + $(package)_config_opts=--without-docs --disable-shared --without-libsodium --disable-curve --disable-curve-keygen --disable-perf $(package)_config_opts_linux=--with-pic $(package)_cxxflags=-std=c++11 endef define $(package)_preprocess_cmds - patch -p1 < $($(package)_patch_dir)/9114d3957725acd34aa8b8d011585812f3369411.patch && \ - patch -p1 < $($(package)_patch_dir)/9e6745c12e0b100cd38acecc16ce7db02905e27c.patch && \ + patch -p1 < $($(package)_patch_dir)/0001-fix-build-with-older-mingw64.patch && \ ./autogen.sh endef @@ -22,7 +21,7 @@ define $(package)_config_cmds endef define $(package)_build_cmds - $(MAKE) libzmq.la + $(MAKE) src/libzmq.la endef define $(package)_stage_cmds @@ -30,5 +29,6 @@ define $(package)_stage_cmds endef define $(package)_postprocess_cmds + sed -i.old "s/ -lstdc++//" lib/pkgconfig/libzmq.pc && \ rm -rf bin share endef diff --git a/depends/patches/native_mac_alias/python3.patch b/depends/patches/native_mac_alias/python3.patch index 1a32340..6f2f553 100644 --- a/depends/patches/native_mac_alias/python3.patch +++ b/depends/patches/native_mac_alias/python3.patch @@ -1,7 +1,7 @@ diff -dur a/mac_alias/alias.py b/mac_alias/alias.py ---- a/mac_alias/alias.py 2015-10-19 12:12:48.000000000 +0200 -+++ b/mac_alias/alias.py 2016-04-03 12:13:12.037159417 +0200 -@@ -243,10 +243,10 @@ +--- a/mac_alias/alias.py ++++ b/mac_alias/alias.py +@@ -258,10 +258,10 @@ alias = Alias() alias.appinfo = appinfo @@ -14,7 +14,7 @@ diff -dur a/mac_alias/alias.py b/mac_alias/alias.py folder_cnid, cnid, crdate, creator_code, type_code) alias.target.levels_from = levels_from -@@ -261,9 +261,9 @@ +@@ -276,9 +276,9 @@ b.read(1) if tag == TAG_CARBON_FOLDER_NAME: @@ -26,7 +26,7 @@ diff -dur a/mac_alias/alias.py b/mac_alias/alias.py value) elif tag == TAG_CARBON_PATH: alias.target.carbon_path = value -@@ -298,9 +298,9 @@ +@@ -313,9 +313,9 @@ alias.target.creation_date \ = mac_epoch + datetime.timedelta(seconds=seconds) elif tag == TAG_POSIX_PATH: @@ -38,23 +38,7 @@ diff -dur a/mac_alias/alias.py b/mac_alias/alias.py elif tag == TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE: alias.volume.disk_image_alias = Alias.from_bytes(value) elif tag == TAG_USER_HOME_LENGTH_PREFIX: -@@ -422,13 +422,13 @@ - # (so doing so is ridiculous, and nothing could rely on it). - b.write(struct.pack(b'>h28pI2shI64pII4s4shhI2s10s', - self.target.kind, -- carbon_volname, voldate, -+ carbon_volname, int(voldate), - self.volume.fs_type, - self.volume.disk_type, - self.target.folder_cnid, - carbon_filename, - self.target.cnid, -- crdate, -+ int(crdate), - self.target.creator_code, - self.target.type_code, - self.target.levels_from, -@@ -449,12 +449,12 @@ +@@ -467,12 +467,12 @@ b.write(struct.pack(b'>hhQhhQ', TAG_HIGH_RES_VOLUME_CREATION_DATE, diff --git a/depends/patches/qt/mac-qmake.conf b/depends/patches/qt/mac-qmake.conf index ca70d30..236988f 100644 --- a/depends/patches/qt/mac-qmake.conf +++ b/depends/patches/qt/mac-qmake.conf @@ -1,6 +1,7 @@ MAKEFILE_GENERATOR = UNIX CONFIG += app_bundle incremental global_init_link_order lib_version_first plugin_no_soname absolute_library_soname QMAKE_INCREMENTAL_STYLE = sublib +QMAKE_APPLE_DEVICE_ARCHS = x86_64 include(../common/macx.conf) include(../common/gcc-base-mac.conf) include(../common/clang.conf) @@ -8,12 +9,12 @@ include(../common/clang-mac.conf) QMAKE_MAC_SDK_PATH=$${MAC_SDK_PATH} QMAKE_XCODE_VERSION=4.3 QMAKE_XCODE_DEVELOPER_PATH=/Developer -QMAKE_MACOSX_DEPLOYMENT_TARGET = $${MAC_MIN_VERSION} QMAKE_MAC_SDK=macosx QMAKE_MAC_SDK.macosx.Path = $${MAC_SDK_PATH} QMAKE_MAC_SDK.macosx.platform_name = macosx QMAKE_MAC_SDK.macosx.SDKVersion = $${MAC_SDK_VERSION} QMAKE_MAC_SDK.macosx.PlatformPath = /phony +QMAKE_MACOSX_DEPLOYMENT_TARGET = $${MAC_MIN_VERSION} !host_build: QMAKE_CFLAGS += -target $${MAC_TARGET} !host_build: QMAKE_OBJECTIVE_CFLAGS += $$QMAKE_CFLAGS !host_build: QMAKE_CXXFLAGS += $$QMAKE_CFLAGS diff --git a/depends/patches/zeromq/0001-fix-build-with-older-mingw64.patch b/depends/patches/zeromq/0001-fix-build-with-older-mingw64.patch new file mode 100644 index 0000000..a6c508f --- /dev/null +++ b/depends/patches/zeromq/0001-fix-build-with-older-mingw64.patch @@ -0,0 +1,30 @@ +From 1a159c128c69a42d90819375c06a39994f3fbfc1 Mon Sep 17 00:00:00 2001 +From: Cory Fields +Date: Tue, 28 Nov 2017 20:33:25 -0500 +Subject: [PATCH] fix build with older mingw64 + +--- + src/windows.hpp | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/src/windows.hpp b/src/windows.hpp +index 99e889d..e69038e 100644 +--- a/src/windows.hpp ++++ b/src/windows.hpp +@@ -55,6 +55,13 @@ + #include + #include + #include ++ ++#if defined __MINGW64_VERSION_MAJOR && __MINGW64_VERSION_MAJOR < 4 ++// Workaround for mingw-w64 < v4.0 which did not include ws2ipdef.h in iphlpapi.h. ++// Fixed in mingw-w64 by 9bd8fe9148924840d315b4c915dd099955ea89d1. ++#include ++#include ++#endif + #include + + #if !defined __MINGW32__ +-- +2.7.4 + diff --git a/doc/README.md b/doc/README.md index 9696933..2247bb5 100644 --- a/doc/README.md +++ b/doc/README.md @@ -26,9 +26,7 @@ Drag XDNA-Qt to your applications folder, and then run XDNA-Qt. ### Need Help? -* See the documentation at the [XDNA Wiki](https://en.xdna.io/wiki/Main_Page) ***TODO*** -for help and more information. -* Ask for help on [BitcoinTalk](https://bitcointalk.org) or on the [XDNA Forum](http://forum.xdna.io/). +* Ask for help on [BitcoinTalk](https://bitcointalk.org/index.php?topic=1262920.0). * Join our Discord server [Discord Server](https://discord.gg/S9adMgS) Building @@ -48,7 +46,6 @@ The XDNA repo's [root README](https://github.com/XDNA-Core/XDNA/blob/master/READ - [Multiwallet Qt Development](multiwallet-qt.md) - [Release Notes](release-notes.md) - [Release Process](release-process.md) -- [Source Code Documentation (External Link)](https://dev.visucore.com/bitcoin/doxygen/) ***TODO*** - [Unit Tests](unit-tests.md) - [Unauthenticated REST Interface](REST-interface.md) - [Dnsseed Policy](dnsseed-policy.md) @@ -56,7 +53,7 @@ The XDNA repo's [root README](https://github.com/XDNA-Core/XDNA/blob/master/READ ### Resources -* Discuss on the [BitcoinTalk](https://bitcointalk.org/index.php?topic=1262920.0) or the [XDNA](http://forum.xdna.io/) forum. +* Discuss on the [BitcoinTalk](https://bitcointalk.org/index.php?topic=1262920.0). ### Miscellaneous - [Assets Attribution](assets-attribution.md) diff --git a/doc/build-windows.md b/doc/build-windows.md index 9740102..33068ed 100644 --- a/doc/build-windows.md +++ b/doc/build-windows.md @@ -32,6 +32,7 @@ To build executables for Windows 32-bit: make HOST=i686-w64-mingw32 -j4 cd .. ./configure --prefix=`pwd`/depends/i686-w64-mingw32 + sudo update-alternatives --config i686-w64-mingw32-g++ #For Ubuntu Xenial 16.04, Ubuntu Artful 17.10 and Windows Subsystem for Linux: Set the default mingw32 g++ compiler option to posix. make To build executables for Windows 64-bit: @@ -42,6 +43,7 @@ To build executables for Windows 64-bit: make HOST=x86_64-w64-mingw32 -j4 cd .. ./configure --prefix=`pwd`/depends/x86_64-w64-mingw32 + sudo update-alternatives --config x86_64-w64-mingw32-g++ # Ubuntu Xenial 16.04 and Windows Subsystem for Linux: Set the default mingw32 g++ compiler option to posix. make For further documentation on the depends system see [README.md](../depends/README.md) in the depends directory. diff --git a/doc/release-notes.md b/doc/release-notes.md index c81811e..20098bd 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -1,4 +1,4 @@ -XDNA Core version 1.0 is now available from: +XDNA Core version 2.0 is now available from: @@ -13,7 +13,7 @@ Compatibility ============== XDNA Core is extensively tested on multiple operating systems using -the Linux kernel, macOS 10.8+, and Windows Vista and later. +the Linux kernel, macOS 10.8+, and Windows 7 and later. Microsoft ended support for Windows XP on [April 8th, 2014](https://www.microsoft.com/en-us/WindowsForBusiness/end-of-xp-support), No attempt is made to prevent installing or running the software on Windows XP, you diff --git a/doc/release-process.md b/doc/release-process.md index 0238ec9..1bd2ed5 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -3,8 +3,6 @@ Release Process Before every release candidate: -* Update translations (ping Fuzzbawls on Slack) see [translation_process.md](https://github.com/XDNA-Core/XDNA/blob/master/doc/translation_process.md#synchronising-translations). - Before every minor and major release: * Update version in `configure.ac` (don't forget to set `CLIENT_VERSION_IS_RELEASE` to `true`) diff --git a/src/Makefile.am b/src/Makefile.am index 2c86a33..fead977 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -106,7 +106,7 @@ BITCOIN_CORE_H = \ crypter.h \ obfuscation.h \ obfuscation-relay.h \ - db.h \ + wallet/db.h \ eccryptoverify.h \ ecwrapper.h \ hash.h \ @@ -133,9 +133,10 @@ BITCOIN_CORE_H = \ protocol.h \ pubkey.h \ random.h \ - rpcclient.h \ - rpcprotocol.h \ - rpcserver.h \ + reverse_iterate.h \ + rpc/client.h \ + rpc/protocol.h \ + rpc/server.h \ script/interpreter.h \ script/script.h \ script/sigcache.h \ @@ -144,11 +145,13 @@ BITCOIN_CORE_H = \ script/script_error.h \ serialize.h \ spork.h \ + sporkdb.h \ streams.h \ sync.h \ threadsafety.h \ timedata.h \ tinyformat.h \ + torcontrol.h \ txdb.h \ txmempool.h \ ui_interface.h \ @@ -160,18 +163,19 @@ BITCOIN_CORE_H = \ utiltime.h \ validationinterface.h \ version.h \ - wallet.h \ - wallet_ismine.h \ - walletdb.h \ + wallet/wallet.h \ + wallet/wallet_ismine.h \ + wallet/walletdb.h \ zmq/zmqabstractnotifier.h \ zmq/zmqconfig.h \ zmq/zmqnotificationinterface.h \ - zmq/zmqpublishnotifier.h + zmq/zmqpublishnotifier.h \ + compat/sanity.h obj/build.h: FORCE @$(MKDIR_P) $(builddir)/obj - @$(top_srcdir)/share/genbuild.sh "$(abs_top_builddir)/src/obj/build.h" \ - "$(abs_top_srcdir)" + @$(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 xdnad and xdna-qt @@ -192,15 +196,17 @@ libbitcoin_server_a_SOURCES = \ noui.cpp \ pow.cpp \ rest.cpp \ - rpcblockchain.cpp \ - rpcmasternode.cpp \ - rpcmining.cpp \ - rpcmisc.cpp \ - rpcnet.cpp \ - rpcrawtransaction.cpp \ - rpcserver.cpp \ + rpc/blockchain.cpp \ + rpc/masternode.cpp \ + rpc/mining.cpp \ + rpc/misc.cpp \ + rpc/net.cpp \ + rpc/rawtransaction.cpp \ + rpc/server.cpp \ script/sigcache.cpp \ + sporkdb.cpp \ timedata.cpp \ + torcontrol.cpp \ txdb.cpp \ txmempool.cpp \ validationinterface.cpp \ @@ -224,7 +230,7 @@ libbitcoin_wallet_a_SOURCES = \ bip38.cpp \ obfuscation.cpp \ obfuscation-relay.cpp \ - db.cpp \ + wallet/db.cpp \ crypter.cpp \ swifttx.cpp \ masternode.cpp \ @@ -232,12 +238,12 @@ libbitcoin_wallet_a_SOURCES = \ masternode-sync.cpp \ masternodeconfig.cpp \ masternodeman.cpp \ - rpcdump.cpp \ - rpcwallet.cpp \ + wallet/rpcdump.cpp \ + wallet/rpcwallet.cpp \ kernel.cpp \ - wallet.cpp \ - wallet_ismine.cpp \ - walletdb.cpp \ + wallet/wallet.cpp \ + wallet/wallet_ismine.cpp \ + wallet/walletdb.cpp \ $(BITCOIN_CORE_H) # crypto primitives library @@ -344,6 +350,7 @@ libbitcoin_common_a_SOURCES = \ script/standard.cpp \ script/script_error.cpp \ spork.cpp \ + sporkdb.cpp \ $(BITCOIN_CORE_H) # util: shared between all executables. @@ -359,7 +366,7 @@ libbitcoin_util_a_SOURCES = \ chainparamsbase.cpp \ clientversion.cpp \ random.cpp \ - rpcprotocol.cpp \ + rpc/protocol.cpp \ sync.cpp \ uint256.cpp \ util.cpp \ @@ -377,7 +384,7 @@ endif libbitcoin_cli_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_cli_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_cli_a_SOURCES = \ - rpcclient.cpp \ + rpc/client.cpp \ $(BITCOIN_CORE_H) nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h @@ -503,14 +510,6 @@ clean-local: @test -f $(PROTOC) $(AM_V_GEN) $(PROTOC) --cpp_out=$(@D) --proto_path=$(abspath $(vin != vin) - { - errorMessage = strprintf("Duplicate Masternode address: %s", service.ToString()); - LogPrintf("CActiveMasternode::Register() - %s\n", errorMessage); - return false; - } + // wait for reindex and/or import to finish + if (fImporting || fReindex) return false; CMasternodePing mnp(vin); if (!mnp.Sign(keyMasternode, pubKeyMasternode)) { errorMessage = strprintf("Failed to sign ping, vin: %s", vin.ToString()); - LogPrintf("CActiveMasternode::Register() - %s\n", errorMessage); + LogPrintf("CActiveMasternode::CreateBroadcast() - %s\n", errorMessage); + mnb = CMasternodeBroadcast(); return false; } - mnodeman.mapSeenMasternodePing.insert(make_pair(mnp.GetHash(), mnp)); - LogPrintf("CActiveMasternode::Register() - Adding to Masternode list\n service: %s\n vin: %s\n", service.ToString(), vin.ToString()); - CMasternodeBroadcast mnb(service, vin, pubKeyCollateralAddress, pubKeyMasternode, PROTOCOL_VERSION); + mnb = CMasternodeBroadcast(service, vin, pubKeyCollateralAddress, pubKeyMasternode, PROTOCOL_VERSION); mnb.lastPing = mnp; if (!mnb.Sign(keyCollateralAddress)) { errorMessage = strprintf("Failed to sign broadcast, vin: %s", vin.ToString()); - LogPrintf("CActiveMasternode::Register() - %s\n", errorMessage); + LogPrintf("CActiveMasternode::CreateBroadcast() - %s\n", errorMessage); + mnb = CMasternodeBroadcast(); return false; } - mnodeman.mapSeenMasternodeBroadcast.insert(make_pair(mnb.GetHash(), mnb)); - masternodeSync.AddedMasternodeList(mnb.GetHash()); - - CMasternode* pmn = mnodeman.Find(vin); - if (!pmn) { - CMasternode mn(mnb); - mnodeman.Add(mn); - } else { - pmn->UpdateFromNewBroadcast(mnb); - } - - //send to all peers - LogPrintf("CActiveMasternode::Register() - RelayElectionEntry vin = %s\n", vin.ToString()); - mnb.Relay(); return true; } @@ -294,6 +276,9 @@ bool CActiveMasternode::GetMasterNodeVin(CTxIn& vin, CPubKey& pubkey, CKey& secr bool CActiveMasternode::GetMasterNodeVin(CTxIn& vin, CPubKey& pubkey, CKey& secretKey, std::string strTxHash, std::string strOutputIndex) { + // wait for reindex and/or import to finish + if (fImporting || fReindex) return false; + // Find possible candidates TRY_LOCK(pwalletMain->cs_wallet, fWallet); if (!fWallet) return false; @@ -314,12 +299,12 @@ bool CActiveMasternode::GetMasterNodeVin(CTxIn& vin, CPubKey& pubkey, CKey& secr return false; } - BOOST_FOREACH (COutput& out, possibleCoins) { + for (COutput& out : possibleCoins) { if(out.tx->GetHash() != txHash || out.i != outputIndex) continue; - if(!CMasternode::Level(out.tx->vout[out.i].nValue)) + if(!CMasternode::Level(out.tx->vout[out.i].nValue, chainActive.Height())) continue; selectedOutput = &out; @@ -339,14 +324,14 @@ bool CActiveMasternode::GetMasterNodeVin(CTxIn& vin, CPubKey& pubkey, CKey& secr selectedOutput = &possibleCoins[0]; - auto selected_level = CMasternode::Level(selectedOutput->tx->vout[selectedOutput->i].nValue); + auto selected_level = CMasternode::Level(selectedOutput->tx->vout[selectedOutput->i].nValue, chainActive.Height()); for(auto& out : possibleCoins) { if(selected_level == 3u) break; - if(CMasternode::Level(out.tx->vout[out.i].nValue) > selected_level) + if(CMasternode::Level(out.tx->vout[out.i].nValue, chainActive.Height()) > selected_level) selectedOutput = &out; } } @@ -358,6 +343,9 @@ bool CActiveMasternode::GetMasterNodeVin(CTxIn& vin, CPubKey& pubkey, CKey& secr // Extract Masternode vin information from output bool CActiveMasternode::GetVinFromOutput(COutput out, CTxIn& vin, CPubKey& pubkey, CKey& secretKey) { + // wait for reindex and/or import to finish + if (fImporting || fReindex) return false; + CScript pubScript; vin = CTxIn(out.tx->GetHash(), out.i); @@ -392,7 +380,7 @@ vector CActiveMasternode::SelectCoinsMasternode() // Temporary unlock MN coins from masternode.conf if (GetBoolArg("-mnconflock", true)) { uint256 mnTxHash; - BOOST_FOREACH (CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) { + for (CMasternodeConfig::CMasternodeEntry mne : masternodeConfig.getEntries()) { mnTxHash.SetHex(mne.getTxHash()); int nIndex; @@ -410,12 +398,12 @@ vector CActiveMasternode::SelectCoinsMasternode() // Lock MN coins from masternode.conf back if they where temporary unlocked if (!confLockedCoins.empty()) { - BOOST_FOREACH (COutPoint outpoint, confLockedCoins) + for (COutPoint outpoint : confLockedCoins) pwalletMain->LockCoin(outpoint); } // Filter - BOOST_FOREACH (const COutput& out, vCoins) { + for (const COutput& out : vCoins) { if(CMasternode::IsDepositCoins(out.tx->vout[out.i].nValue)) filteredCoins.push_back(out); diff --git a/src/activemasternode.h b/src/activemasternode.h index 1e7238e..8a011cc 100644 --- a/src/activemasternode.h +++ b/src/activemasternode.h @@ -1,6 +1,6 @@ // Copyright (c) 2014-2016 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -14,7 +14,7 @@ #include "net.h" #include "obfuscation.h" #include "sync.h" -#include "wallet.h" +#include "wallet/wallet.h" #define ACTIVE_MASTERNODE_INITIAL 0 // initial state #define ACTIVE_MASTERNODE_SYNC_IN_PROCESS 1 @@ -32,8 +32,8 @@ class CActiveMasternode /// Ping Masternode bool SendMasternodePing(std::string& errorMessage); - /// Register any Masternode - bool Register(CTxIn vin, CService service, CKey key, CPubKey pubKey, CKey keyMasternode, CPubKey pubKeyMasternode, std::string& errorMessage); + /// Create Masternode broadcast, needs to be relayed manually after that + bool CreateBroadcast(CTxIn vin, CService service, CKey key, CPubKey pubKey, CKey keyMasternode, CPubKey pubKeyMasternode, std::string& errorMessage, CMasternodeBroadcast &mnb); /// Get XDNA input that can be used for the Masternode bool GetMasterNodeVin(CTxIn& vin, CPubKey& pubkey, CKey& secretKey, std::string strTxHash, std::string strOutputIndex); @@ -60,8 +60,8 @@ class CActiveMasternode void ManageStatus(); std::string GetStatus(); - /// Register remote Masternode - bool Register(std::string strService, std::string strKey, std::string strTxHash, std::string strOutputIndex, std::string& errorMessage); + /// Create Masternode broadcast, needs to be relayed manually after that + bool CreateBroadcast(std::string strService, std::string strKey, std::string strTxHash, std::string strOutputIndex, std::string& errorMessage, CMasternodeBroadcast &mnb, bool fOffline = false); /// Get XDNA input that can be used for the Masternode bool GetMasterNodeVin(CTxIn& vin, CPubKey& pubkey, CKey& secretKey); diff --git a/src/addrman.cpp b/src/addrman.cpp index f479f3b..be0a105 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -451,17 +451,12 @@ int CAddrMan::Check_() void CAddrMan::GetAddr_(std::vector& vAddr) { unsigned int nNodes = ADDRMAN_GETADDR_MAX_PCT * vRandom.size() / 100; - - if(nNodes < 100 / ADDRMAN_GETADDR_MAX_PCT) - nNodes = 100 / ADDRMAN_GETADDR_MAX_PCT; - - if(nNodes > ADDRMAN_GETADDR_MAX) + 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) + for (unsigned int n = 0; n < vRandom.size(); n++) { + if (vAddr.size() >= nNodes) break; int nRndPos = GetRandInt(vRandom.size() - n) + n; diff --git a/src/chain.cpp b/src/chain.cpp index 7e2cc67..5388fc1 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -13,8 +13,6 @@ using namespace std; int64_t CChain::GetNetworkHashPS(int lookup, int height) { - --height; - CBlockIndex *pb = this->Tip(); if (height >= 0 && height < this->Height()) diff --git a/src/chain.h b/src/chain.h index 27e9db8..49f8998 100644 --- a/src/chain.h +++ b/src/chain.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 44bc662..4356619 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -2,7 +2,7 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -58,12 +58,13 @@ static Checkpoints::MapCheckpoints mapCheckpoints = (50, uint256("000000224104db4572f767923cafd543f36b9a4d1eee117c4dc3e1961ca6371b")) (60200, uint256("00000000000031ba24b923e1966fbe17ae0cdff1efa9d1efc556e2a3cafb5c55")) (134000, uint256("0000000000c08ddeef3b04f5ead225a8d5f4765756d32407b74da5ca9c708b80")) - (165000, uint256("00000000010f679ac3889908996bed4716f33de4608eb48594fd36b65f03831b")); + (165000, uint256("00000000010f679ac3889908996bed4716f33de4608eb48594fd36b65f03831b")) + (680700, uint256("0000000007151c76d2da2416f700269ef7f030a3ad8d0f788cb7107887df40fe")); static const Checkpoints::CCheckpointData data = { &mapCheckpoints, - 1540098739, // * UNIX timestamp of last checkpoint block - 234944, // * total number of transactions between genesis and last checkpoint + 1572678180, // * UNIX timestamp of last checkpoint block + 844756, // * total number of transactions between genesis and last checkpoint // (the tx=... number in the SetBestChain debug.log lines) 2000 // * estimated number of transactions per day after checkpoint }; @@ -237,8 +238,12 @@ class CMainParams : public CChainParams nStartMasternodePaymentsBlock = 7001; /** Height or Time Based Activations **/ - nLastPOWBlock = 1440000; + nLastPOWBlock = 682500; nModifierUpdateBlock = std::numeric_limits::max(); + nStartMasternodePayments = 1403728576; //Wed, 25 Jun 2014 20:36:16 GMT + nHEXHashTimestamp = 1533567600; // 6 August 2018, 15:00:00 GMT+00:00 + nF2Timestamp = 1540728000; // 28 October 2018, 12:00:00 GMT+00:00 + nF3ActivationHeight = 682500; /** * Build the genesis block. Note that the output of the genesis coinbase cannot @@ -294,10 +299,7 @@ class CMainParams : public CChainParams nPoolMaxTransactions = 3; strSporkKey = "04520C1E6A46596DD9CA9A1A69B96D630410CBA2A1047FC462ADAA5D3BE451CC43B2E30C64A03513F31B3DB9450A3FC2F742DCB4AD99450575219549890392F465"; strObfuscationPoolDummyAddress = "X87q2gC9j6nNrnzCsg4aY6bHMLsT9nUhEw"; - nStartMasternodePayments = 1403728576; //Wed, 25 Jun 2014 20:36:16 GMT - nHEXHashTimestamp = 1533567600; // 6 August 2018, 15:00:00 GMT+00:00 - nF2Timestamp = 1540728000; // 28 October 2018, 12:00:00 GMT+00:00 } const Checkpoints::CCheckpointData& Checkpoints() const diff --git a/src/chainparams.h b/src/chainparams.h index a6ffcb8..b16c98a 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -2,7 +2,7 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX Core developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -107,12 +107,13 @@ class CChainParams /** Height or Time Based Activations **/ //todo: ModifierUpgradeBlock affect POS - int ModifierUpgradeBlock() const { return nModifierUpdateBlock; } - int LAST_POW_BLOCK() const { return nLastPOWBlock; } - int StartMNPaymentsBlock() const {return nStartMasternodePaymentsBlock; } + int ModifierUpgradeBlock() const { return nModifierUpdateBlock; } + int LAST_POW_BLOCK() const { return nLastPOWBlock; } + int StartMNPaymentsBlock() const { return nStartMasternodePaymentsBlock; } + int F3Activation() const { return nF3ActivationHeight; } - uint32_t HEXHashActivationTime() const {return nHEXHashTimestamp;} - uint32_t F2ActivationTime() const {return nF2Timestamp;} + uint32_t HEXHashActivationTime() const { return nHEXHashTimestamp; } + uint32_t F2ActivationTime() const { return nF2Timestamp; } const SubsidySwitchPoints& GetSubsidySwitchPoints(uint32_t nTime, int nHeight) const; @@ -142,6 +143,7 @@ class CChainParams int nMasternodeCountDrift; int nMaturity; int nModifierUpdateBlock; + int nF3ActivationHeight; CAmount nMaxMoneyOut; int nMinerThreads; std::vector vSeeds; diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h index 4635ecf..90bb25f 100644 --- a/src/chainparamsseeds.h +++ b/src/chainparamsseeds.h @@ -52,6 +52,7 @@ static SeedSpec6 pnSeed6_test[] = { {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x95,0x1c,0x64,0x7c}, 11945}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x4c,0xbe,0x23}, 11945}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xca,0xb6,0x71,0x0c}, 11945} + }; #endif // BITCOIN_CHAINPARAMSSEEDS_H diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index 4a601ec..51e3855 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/checkqueue.h b/src/checkqueue.h index 9648347..a66f0b3 100644 --- a/src/checkqueue.h +++ b/src/checkqueue.h @@ -119,7 +119,7 @@ class CCheckQueue fOk = fAllOk; } // execute work - BOOST_FOREACH (T& check, vChecks) + for (T& check : vChecks) if (fOk) fOk = check(); vChecks.clear(); @@ -146,7 +146,7 @@ class CCheckQueue void Add(std::vector& vChecks) { boost::unique_lock lock(mutex); - BOOST_FOREACH (T& check, vChecks) { + for (T& check : vChecks) { queue.push_back(T()); check.swap(queue.back()); } diff --git a/src/clientversion.cpp b/src/clientversion.cpp index 1841599..6f34d53 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -39,13 +39,13 @@ const std::string CLIENT_NAME("XDNA Core"); //! First, include build.h if requested #ifdef HAVE_BUILD_INFO -#include "build.h" +#include "obj/build.h" #endif -//! git will put "#define GIT_ARCHIVE 1" on the next line inside archives. +//! git will put "#define GIT_ARCHIVE 1" on the next line inside archives. $Format:%n#define GIT_ARCHIVE 1$ #ifdef GIT_ARCHIVE -#define GIT_COMMIT_ID "cbcb549" -#define GIT_COMMIT_DATE "Tue, 9 Feb 2016 16:54:57 -0500" +#define GIT_COMMIT_ID "$Format:%H$" +#define GIT_COMMIT_DATE "$Format:%cD$" #endif #define BUILD_DESC_WITH_SUFFIX(maj, min, rev, build, suffix) \ diff --git a/src/clientversion.h b/src/clientversion.h index 2716985..293b51e 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -14,10 +14,10 @@ */ //! These need to be macros, as clientversion.cpp's and xdna*-res.rc's voodoo requires it -#define CLIENT_VERSION_MAJOR 1 -#define CLIENT_VERSION_MINOR 3 +#define CLIENT_VERSION_MAJOR 2 +#define CLIENT_VERSION_MINOR 0 #define CLIENT_VERSION_REVISION 0 -#define CLIENT_VERSION_BUILD 1 +#define CLIENT_VERSION_BUILD 0 //! Set to true for release, false for prerelease or test build #define CLIENT_VERSION_IS_RELEASE true diff --git a/src/coincontrol.h b/src/coincontrol.h index 0cfa92f..0e9ffdf 100644 --- a/src/coincontrol.h +++ b/src/coincontrol.h @@ -36,7 +36,7 @@ class CCoinControl destChange = CNoDestination(); setSelected.clear(); useSwiftTX = false; - useObfuScation = true; + useObfuScation = false; fAllowOtherInputs = false; fAllowWatchOnly = true; nMinimumTotalFee = 0; diff --git a/src/core_write.cpp b/src/core_write.cpp index f0af3e6..6421939 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -91,10 +91,11 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry) { entry.pushKV("txid", tx.GetHash().GetHex()); entry.pushKV("version", tx.nVersion); + entry.pushKV("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION)); entry.pushKV("locktime", (int64_t)tx.nLockTime); UniValue vin(UniValue::VARR); - BOOST_FOREACH (const CTxIn& txin, tx.vin) { + for (const CTxIn& txin : tx.vin) { UniValue in(UniValue::VOBJ); if (tx.IsCoinBase()) in.pushKV("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())); diff --git a/src/crypto/hex/hex.c b/src/crypto/hex/hex.c index 5e20266..9081ff8 100644 --- a/src/crypto/hex/hex.c +++ b/src/crypto/hex/hex.c @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/crypto/hex/hex.h b/src/crypto/hex/hex.h index 4d45a97..7f843fa 100644 --- a/src/crypto/hex/hex.h +++ b/src/crypto/hex/hex.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/hash.h b/src/hash.h index 77fbfdf..bd506e2 100644 --- a/src/hash.h +++ b/src/hash.h @@ -2,7 +2,7 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/init.cpp b/src/init.cpp index a677df2..8113216 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2,7 +2,7 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -24,18 +24,21 @@ #include "masternodeman.h" #include "miner.h" #include "net.h" -#include "rpcserver.h" +#include "rpc/server.h" #include "script/standard.h" #include "spork.h" +#include "sporkdb.h" #include "txdb.h" +#include "torcontrol.h" #include "ui_interface.h" #include "util.h" #include "utilmoneystr.h" #include "validationinterface.h" + #ifdef ENABLE_WALLET -#include "db.h" -#include "wallet.h" -#include "walletdb.h" +#include "wallet/db.h" +#include "wallet/wallet.h" +#include "wallet/walletdb.h" #endif #include @@ -51,6 +54,7 @@ #include #include #include +#include #include #if ENABLE_ZMQ @@ -156,6 +160,12 @@ class CCoinsViewErrorCatcher : public CCoinsViewBacked static CCoinsViewDB* pcoinsdbview = NULL; static CCoinsViewErrorCatcher* pcoinscatcher = NULL; +static boost::thread_group threadGroup; + +void Interrupt() +{ + InterruptTorControl(); +} /** Preparing steps before shutting down or restarting the wallet */ void PrepareShutdown() @@ -184,6 +194,11 @@ void PrepareShutdown() DumpMasternodes(); UnregisterNodeSignals(GetNodeSignals()); + // After everything has been shut down, but before things get flushed, stop the + // CScheduler/checkqueue threadGroup + threadGroup.interrupt_all(); + threadGroup.join_all(); + if (fFeeEstimatesInitialized) { boost::filesystem::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME; CAutoFile est_fileout(fopen(est_path.string().c_str(), "wb"), SER_DISK, CLIENT_VERSION); @@ -210,6 +225,8 @@ void PrepareShutdown() pcoinsdbview = NULL; delete pblocktree; pblocktree = NULL; + delete pSporkDB; + pSporkDB = NULL; } #ifdef ENABLE_WALLET if (pwalletMain) @@ -225,7 +242,11 @@ void PrepareShutdown() #endif #ifndef WIN32 + try { boost::filesystem::remove(GetPidFile()); + } catch (const boost::filesystem::filesystem_error& e) { + LogPrintf("%s: Unable to remove pidfile: %s\n", __func__, e.what()); + } #endif UnregisterAllValidationInterfaces(); } @@ -246,7 +267,8 @@ void Shutdown() PrepareShutdown(); } -// Shutdown part 2: delete wallet instance + // Shutdown part 2: Stop TOR thread and delete wallet instance + StopTorControl(); #ifdef ENABLE_WALLET delete pwalletMain; pwalletMain = NULL; @@ -303,7 +325,6 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-alerts", strprintf(_("Receive and display P2P network alerts (default: %u)"), DEFAULT_ALERTS)); strUsage += HelpMessageOpt("-blocknotify=", _("Execute command when the best block changes (%s in cmd is replaced by block hash)")); strUsage += HelpMessageOpt("-checkblocks=", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), 500)); - strUsage += HelpMessageOpt("-checklevel=", strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), 3)); strUsage += HelpMessageOpt("-conf=", strprintf(_("Specify configuration file (default: %s)"), "xdna.conf")); if (mode == HMM_BITCOIND) { #if !defined(WIN32) @@ -313,12 +334,15 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-datadir=", _("Specify data directory")); strUsage += HelpMessageOpt("-dbcache=", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache)); strUsage += HelpMessageOpt("-loadblock=", _("Imports blocks from external blk000??.dat file") + " " + _("on startup")); + strUsage += HelpMessageOpt("-maxreorg=", strprintf(_("Set the Maximum reorg depth (default: %u)"), Params(CBaseChainParams::MAIN).MaxReorganizationDepth())); strUsage += HelpMessageOpt("-maxorphantx=", strprintf(_("Keep at most unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS)); strUsage += HelpMessageOpt("-par=", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"), -(int)boost::thread::hardware_concurrency(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS)); #ifndef WIN32 strUsage += HelpMessageOpt("-pid=", strprintf(_("Specify pid file (default: %s)"), "xdnad.pid")); #endif strUsage += HelpMessageOpt("-reindex", _("Rebuild block chain index from current blk000??.dat files") + " " + _("on startup")); + strUsage += HelpMessageOpt("-reindexmoneysupply", _("Reindex the XDNA money supply statistics") + " " + _("on startup")); + strUsage += HelpMessageOpt("-resync", _("Delete blockchain folders and resync from scratch") + " " + _("on startup")); #if !defined(WIN32) strUsage += HelpMessageOpt("-sysperms", _("Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)")); #endif @@ -337,6 +361,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-externalip=", _("Specify your own public address")); strUsage += HelpMessageOpt("-forcednsseed", strprintf(_("Always query for peer addresses via DNS lookup (default: %u)"), 0)); strUsage += HelpMessageOpt("-listen", _("Accept connections from outside (default: 1 if no -proxy or -connect)")); + strUsage += HelpMessageOpt("-listenonion", strprintf(_("Automatically create Tor hidden service (default: %d)"), DEFAULT_LISTEN_ONION)); strUsage += HelpMessageOpt("-maxconnections=", strprintf(_("Maintain at most connections to peers (default: %u)"), 125)); strUsage += HelpMessageOpt("-maxreceivebuffer=", strprintf(_("Maximum per-connection receive buffer, *1000 bytes (default: %u)"), 5000)); strUsage += HelpMessageOpt("-maxsendbuffer=", strprintf(_("Maximum per-connection send buffer, *1000 bytes (default: %u)"), 1000)); @@ -346,8 +371,11 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-peerbloomfilters", strprintf(_("Support filtering of blocks and transaction with bloom filters (default: %u)"), DEFAULT_PEERBLOOMFILTERS)); strUsage += HelpMessageOpt("-port=", strprintf(_("Listen for connections on (default: %u or testnet: %u)"), 1945, 11945)); strUsage += HelpMessageOpt("-proxy=", _("Connect through SOCKS5 proxy")); + strUsage += HelpMessageOpt("-proxyrandomize", strprintf(_("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)"), 1)); strUsage += HelpMessageOpt("-seednode=", _("Connect to a node to retrieve peer addresses, and disconnect")); strUsage += HelpMessageOpt("-timeout=", strprintf(_("Specify connection timeout in milliseconds (minimum: 1, default: %d)"), DEFAULT_CONNECT_TIMEOUT)); + strUsage += HelpMessageOpt("-torcontrol=:", strprintf(_("Tor control port to use if onion listening enabled (default: %s)"), DEFAULT_TOR_CONTROL)); + strUsage += HelpMessageOpt("-torpassword=", _("Tor control port password (default: empty)")); #ifdef USE_UPNP #if USE_UPNP strUsage += HelpMessageOpt("-upnp", _("Use UPnP to map the listening port (default: 1 when listening)")); @@ -373,6 +401,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet.dat") + " " + _("on startup")); strUsage += HelpMessageOpt("-sendfreetransactions", strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), 0)); strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), 1)); + strUsage += HelpMessageOpt("-disablesystemnotifications", strprintf(_("Disable OS notifications for incoming transactions (default: %u)"), 0)); strUsage += HelpMessageOpt("-txconfirmtarget=", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), 1)); strUsage += HelpMessageOpt("-maxtxfee=", strprintf(_("Maximum total fees to use in a single wallet transaction, setting too low may abort large transactions (default: %s)"), FormatMoney(maxTxFee))); @@ -396,6 +425,7 @@ std::string HelpMessage(HelpMessageMode mode) #endif strUsage += HelpMessageGroup(_("Debugging/Testing options:")); + strUsage += HelpMessageOpt("-uacomment=", _("Append comment to the user agent string")); if (GetBoolArg("-help-debug", false)) { strUsage += HelpMessageOpt("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive and mapBlocksUnlinked occasionally. Also sets -checkmempool (default: %u)", Params(CBaseChainParams::MAIN).DefaultConsistencyChecks())); strUsage += HelpMessageOpt("-checkmempool=", strprintf("Run checks every transactions (default: %u)", Params(CBaseChainParams::MAIN).DefaultConsistencyChecks())); @@ -406,10 +436,11 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-dropmessagestest=", _("Randomly drop 1 of every network messages")); strUsage += HelpMessageOpt("-fuzzmessagestest=", _("Randomly fuzz 1 of every network messages")); strUsage += HelpMessageOpt("-flushwallet", strprintf(_("Run a thread to flush wallet periodically (default: %u)"), 1)); + strUsage += HelpMessageOpt("-maxreorg", strprintf(_("Use a custom max chain reorganization depth (default: %u)"), 100)); strUsage += HelpMessageOpt("-stopafterblockimport", strprintf(_("Stop running after importing blocks from disk (default: %u)"), 0)); strUsage += HelpMessageOpt("-sporkkey=", _("Enable spork administration functionality with the appropriate private key.")); } - string debugCategories = "addrman, alert, bench, coindb, db, lock, rand, rpc, selectcoins, mempool, net, proxy, xdna, (obfuscation, swiftx, masternode, mnpayments)"; // Don't translate these and qt below + string debugCategories = "addrman, alert, bench, coindb, db, lock, rand, rpc, selectcoins, tor, mempool, net, proxy, xdna, (obfuscation, swiftx, masternode, mnpayments)"; // Don't translate these and qt below if (mode == HMM_BITCOIN_QT) debugCategories += ", qt"; strUsage += HelpMessageOpt("-debug=", strprintf(_("Output debugging information (default: %u, supplying is optional)"), 0) + ". " + @@ -428,6 +459,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-relaypriority", strprintf(_("Require high priority for relaying free or low-fee transactions (default:%u)"), 1)); strUsage += HelpMessageOpt("-maxsigcachesize=", strprintf(_("Limit size of signature cache to entries (default: %u)"), 50000)); } + strUsage += HelpMessageOpt("-maxtipage=", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE)); strUsage += HelpMessageOpt("-minrelaytxfee=", strprintf(_("Fees (in XDNA/Kb) smaller than this are considered zero fee for relaying (default: %s)"), FormatMoney(::minRelayTxFee.GetFeePerK()))); strUsage += HelpMessageOpt("-printtoconsole", strprintf(_("Send trace/debug info to console instead of debug.log file (default: %u)"), 0)); if (GetBoolArg("-help-debug", false)) { @@ -439,7 +471,7 @@ std::string HelpMessage(HelpMessageMode mode) } strUsage += HelpMessageOpt("-shrinkdebugfile", _("Shrink debug.log file on client startup (default: 1 when no -debug)")); strUsage += HelpMessageOpt("-testnet", _("Use the test network")); - strUsage += HelpMessageOpt("-litemode=", strprintf(_("Disable all XDNA specific functionality (Masternodes, Obfuscation, SwiftX) (0-1, default: %u)"), 0)); + strUsage += HelpMessageOpt("-litemode=", strprintf(_("Disable all XDNA specific functionality (Masternodes, SwiftX) (0-1, default: %u)"), 0)); #ifdef ENABLE_WALLET strUsage += HelpMessageGroup(_("Staking options:")); @@ -458,12 +490,6 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-masternodeprivkey=", _("Set the masternode private key")); strUsage += HelpMessageOpt("-masternodeaddr=", strprintf(_("Set external address:port to get to this masternode (example: %s)"), "128.127.106.235:1945")); - strUsage += HelpMessageGroup(_("Obfuscation options:")); - strUsage += HelpMessageOpt("-enableobfuscation=", strprintf(_("Enable use of automated obfuscation for funds stored in this wallet (0-1, default: %u)"), 0)); - strUsage += HelpMessageOpt("-obfuscationrounds=", strprintf(_("Use N separate masternodes to anonymize funds (2-8, default: %u)"), 2)); - strUsage += HelpMessageOpt("-anonymizexdnaamount=", strprintf(_("Keep N XDNA anonymized (default: %u)"), 0)); - strUsage += HelpMessageOpt("-liquidityprovider=", strprintf(_("Provide liquidity to Obfuscation by infrequently mixing coins on a continual basis (0-100, default: %u, 1=very frequent, high fees, 100=very infrequent, low fees)"), 0)); - strUsage += HelpMessageGroup(_("SwiftX options:")); strUsage += HelpMessageOpt("-enableswifttx=", strprintf(_("Enable SwiftX, show confirmations for locked transactions (bool, default: %s)"), "true")); strUsage += HelpMessageOpt("-swifttxdepth=", strprintf(_("Show N confirmations for a successfully locked transaction (0-9999, default: %u)"), nSwiftTXDepth)); @@ -582,7 +608,7 @@ void ThreadImport(std::vector vImportFiles) } // -loadblock= - BOOST_FOREACH (boost::filesystem::path& path, vImportFiles) { + for (boost::filesystem::path& path : vImportFiles) { FILE* file = fopen(path.string().c_str(), "rb"); if (file) { CImportingNow imp; @@ -606,8 +632,7 @@ void ThreadImport(std::vector vImportFiles) bool InitSanityCheck(void) { if (!ECC_InitSanityCheck()) { - InitError("OpenSSL appears to lack support for elliptic curve cryptography. For more " - "information, visit https://en.bitcoin.it/wiki/OpenSSL_and_EC_Libraries"); + InitError("Elliptic curve cryptography sanity check failure. Aborting."); return false; } if (!glibc_sanity_test() || !glibcxx_sanity_test()) @@ -620,7 +645,7 @@ bool InitSanityCheck(void) /** Initialize XDNA. * @pre Parameters should be parsed and config file should be read. */ -bool AppInit2(boost::thread_group& threadGroup) +bool AppInit2() { // ********************************************************* Step 1: setup #ifdef _MSC_VER @@ -725,6 +750,8 @@ bool AppInit2(boost::thread_group& threadGroup) LogPrintf("AppInit2 : parameter interaction: -listen=0 -> setting -upnp=0\n"); if (SoftSetBoolArg("-discover", false)) LogPrintf("AppInit2 : parameter interaction: -listen=0 -> setting -discover=0\n"); + if (SoftSetBoolArg("-listenonion", false)) + LogPrintf("AppInit2 : parameter interaction: -listen=0 -> setting -listenonion=0\n"); } if (mapArgs.count("-externalip")) { @@ -784,6 +811,9 @@ bool AppInit2(boost::thread_group& threadGroup) // Check for -tor - as this is a privacy risk to continue, exit here if (GetBoolArg("-tor", false)) return InitError(_("Error: Unsupported argument -tor found, use -onion.")); + // Check level must be 4 for zerocoin checks + if (mapArgs.count("-checklevel")) + return InitError(_("Error: Unsupported argument -checklevel found. Checklevel must be level 4.")); if (GetBoolArg("-benchmark", false)) InitWarning(_("Warning: Unsupported argument -benchmark ignored, use -debug=bench.")); @@ -867,13 +897,14 @@ bool AppInit2(boost::thread_group& threadGroup) } } nTxConfirmTarget = GetArg("-txconfirmtarget", 1); - bSpendZeroConfChange = GetArg("-spendzeroconfchange", true); - fSendFreeTransactions = GetArg("-sendfreetransactions", false); + bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", false); + bdisableSystemnotifications = GetBoolArg("-disablesystemnotifications", false); + fSendFreeTransactions = GetBoolArg("-sendfreetransactions", false); std::string strWalletFile = GetArg("-wallet", "wallet.dat"); #endif // ENABLE_WALLET - fIsBareMultisigStd = GetArg("-permitbaremultisig", true) != 0; + fIsBareMultisigStd = GetBoolArg("-permitbaremultisig", true) != 0; nMaxDatacarrierBytes = GetArg("-datacarriersize", nMaxDatacarrierBytes); fAlerts = GetBoolArg("-alerts", DEFAULT_ALERTS); @@ -1018,6 +1049,37 @@ bool AppInit2(boost::thread_group& threadGroup) } } + if (GetBoolArg("-resync", false)) { + uiInterface.InitMessage(_("Preparing for resync...")); + // Delete the local blockchain folders to force a resync from scratch to get a consitent blockchain-state + filesystem::path blocksDir = GetDataDir() / "blocks"; + filesystem::path chainstateDir = GetDataDir() / "chainstate"; + filesystem::path sporksDir = GetDataDir() / "sporks"; + filesystem::path zerocoinDir = GetDataDir() / "zerocoin"; + + LogPrintf("Deleting blockchain folders blocks, chainstate, sporks and zerocoin\n"); + // We delete in 4 individual steps in case one of the folder is missing already + try { + if (filesystem::exists(blocksDir)){ + boost::filesystem::remove_all(blocksDir); + LogPrintf("-resync: folder deleted: %s\n", blocksDir.string().c_str()); + } + + if (filesystem::exists(chainstateDir)){ + boost::filesystem::remove_all(chainstateDir); + LogPrintf("-resync: folder deleted: %s\n", chainstateDir.string().c_str()); + } + + if (filesystem::exists(sporksDir)){ + boost::filesystem::remove_all(sporksDir); + LogPrintf("-resync: folder deleted: %s\n", sporksDir.string().c_str()); + } + + } catch (boost::filesystem::filesystem_error& error) { + LogPrintf("Failed to delete blockchain folders %s\n", error.what()); + } + } + LogPrintf("Using wallet %s\n", strWalletFile); uiInterface.InitMessage(_("Verifying wallet...")); @@ -1066,9 +1128,24 @@ bool AppInit2(boost::thread_group& threadGroup) RegisterNodeSignals(GetNodeSignals()); + // sanitize comments per BIP-0014, format user agent and check total size + std::vector uacomments; + for (const std::string& cmt : mapMultiArgs["-uacomment"]) { + if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT)) + return InitError(strprintf(_("User Agent comment (%s) contains unsafe characters."), cmt)); + uacomments.push_back(cmt); + } + + // format user agent, check total size + strSubVersion = FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, uacomments); + if (strSubVersion.size() > MAX_SUBVERSION_LENGTH) { + return InitError(strprintf(_("Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments."), + strSubVersion.size(), MAX_SUBVERSION_LENGTH)); + } + if (mapArgs.count("-onlynet")) { std::set nets; - BOOST_FOREACH (std::string snet, mapMultiArgs["-onlynet"]) { + for (std::string snet : mapMultiArgs["-onlynet"]) { enum Network net = ParseNetwork(snet); if (net == NET_UNROUTABLE) return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet)); @@ -1082,7 +1159,7 @@ bool AppInit2(boost::thread_group& threadGroup) } if (mapArgs.count("-whitelist")) { - BOOST_FOREACH (const std::string& net, mapMultiArgs["-whitelist"]) { + for (const std::string& net : mapMultiArgs["-whitelist"]) { CSubNet subnet(net); if (!subnet.IsValid()) return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net)); @@ -1097,21 +1174,22 @@ bool AppInit2(boost::thread_group& threadGroup) // -proxy sets a proxy for all outgoing network traffic // -noproxy (or -proxy=0) as well as the empty string can be used to not set a proxy, this is the default std::string proxyArg = GetArg("-proxy", ""); + SetLimited(NET_TOR); if (proxyArg != "" && proxyArg != "0") { CService proxyAddr; if (!Lookup(proxyArg.c_str(), proxyAddr, 9050, fNameLookup)) { - return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg)); + return InitError(strprintf(_("Lookup(): Invalid -proxy address or hostname: '%s'"), proxyArg)); } proxyType addrProxy = proxyType(proxyAddr, proxyRandomize); if (!addrProxy.IsValid()) - return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg)); + return InitError(strprintf(_("isValid(): Invalid -proxy address or hostname: '%s'"), proxyArg)); SetProxy(NET_IPV4, addrProxy); SetProxy(NET_IPV6, addrProxy); SetProxy(NET_TOR, addrProxy); SetNameProxy(addrProxy); - SetReachable(NET_TOR); // by default, -proxy sets onion as reachable, unless -noonion later + SetLimited(NET_TOR, false); // by default, -proxy sets onion as reachable, unless -noonion later } // -onion can be used to set only a proxy for .onion, or override normal proxy for .onion addresses @@ -1120,7 +1198,7 @@ bool AppInit2(boost::thread_group& threadGroup) std::string onionArg = GetArg("-onion", ""); if (onionArg != "") { if (onionArg == "0") { // Handle -noonion/-onion=0 - SetReachable(NET_TOR, false); // set onions as unreachable + SetLimited(NET_TOR); // set onions as unreachable } else { CService onionProxy; if (!Lookup(onionArg.c_str(), onionProxy, 9050, fNameLookup)) { @@ -1130,25 +1208,24 @@ bool AppInit2(boost::thread_group& threadGroup) if (!addrOnion.IsValid()) return InitError(strprintf(_("Invalid -onion address or hostname: '%s'"), onionArg)); SetProxy(NET_TOR, addrOnion); - SetReachable(NET_TOR); + SetLimited(NET_TOR, false); } } // see Step 2: parameter interactions for more information about these fListen = GetBoolArg("-listen", DEFAULT_LISTEN); fDiscover = GetBoolArg("-discover", true); - fNameLookup = GetBoolArg("-dns", true); bool fBound = false; if (fListen) { if (mapArgs.count("-bind") || mapArgs.count("-whitebind")) { - BOOST_FOREACH (std::string strBind, mapMultiArgs["-bind"]) { + for (std::string strBind : mapMultiArgs["-bind"]) { CService addrBind; if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) return InitError(strprintf(_("Cannot resolve -bind address: '%s'"), strBind)); fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR)); } - BOOST_FOREACH (std::string strBind, mapMultiArgs["-whitebind"]) { + for (std::string strBind : mapMultiArgs["-whitebind"]) { CService addrBind; if (!Lookup(strBind.c_str(), addrBind, 0, false)) return InitError(strprintf(_("Cannot resolve -whitebind address: '%s'"), strBind)); @@ -1159,7 +1236,7 @@ bool AppInit2(boost::thread_group& threadGroup) } else { struct in_addr inaddr_any; inaddr_any.s_addr = INADDR_ANY; - fBound |= Bind(CService(in6addr_any, GetListenPort()), BF_NONE); + fBound |= Bind(CService((in6_addr)IN6ADDR_ANY_INIT, GetListenPort()), BF_NONE); fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE); } if (!fBound) @@ -1167,7 +1244,7 @@ bool AppInit2(boost::thread_group& threadGroup) } if (mapArgs.count("-externalip")) { - BOOST_FOREACH (string strAddr, mapMultiArgs["-externalip"]) { + for (string strAddr : mapMultiArgs["-externalip"]) { CService addrLocal(strAddr, GetListenPort(), fNameLookup); if (!addrLocal.IsValid()) return InitError(strprintf(_("Cannot resolve -externalip address: '%s'"), strAddr)); @@ -1175,7 +1252,7 @@ bool AppInit2(boost::thread_group& threadGroup) } } - BOOST_FOREACH (string strDest, mapMultiArgs["-seednode"]) + for (string strDest : mapMultiArgs["-seednode"]) AddOneShot(strDest); #if ENABLE_ZMQ @@ -1244,7 +1321,9 @@ bool AppInit2(boost::thread_group& threadGroup) delete pcoinsdbview; delete pcoinscatcher; delete pblocktree; + delete pSporkDB; + pSporkDB = new CSporkDB(0, false, false); pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex); pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex); pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview); @@ -1253,8 +1332,15 @@ bool AppInit2(boost::thread_group& threadGroup) if (fReindex) pblocktree->WriteReindexing(true); - if (!LoadBlockIndex()) { + // MERGE: load previous sessions sporks if we have them. + uiInterface.InitMessage(_("Loading sporks...")); + LoadSporksFromDB(); + + uiInterface.InitMessage(_("Loading block index...")); + string strBlockIndexError = ""; + if (!LoadBlockIndex(strBlockIndexError)) { strLoadError = _("Error loading block database"); + strLoadError = strprintf("%s : %s", strLoadError, strBlockIndexError); break; } @@ -1275,18 +1361,33 @@ bool AppInit2(boost::thread_group& threadGroup) break; } - uiInterface.InitMessage(_("Verifying blocks...")); - if (!CVerifyDB().VerifyDB(pcoinsdbview, GetArg("-checklevel", 4), - GetArg("-checkblocks", 500))) { - strLoadError = _("Corrupted block database detected"); - break; + if (!fReindex) { + uiInterface.InitMessage(_("Verifying blocks...")); + + // Flag sent to validation code to let it know it can skip certain checks + fVerifyingBlocks = true; + + { + LOCK(cs_main); + CBlockIndex *tip = chainActive[chainActive.Height()]; + RPCNotifyBlockChange(tip->GetBlockHash()); + if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) { + strLoadError = _("The block database contains a block which appears to be from the future. " + "This may be due to your computer's date and time being set incorrectly. " + "Only rebuild the block database if you are sure that your computer's date and time are correct"); + break; + } + } + } } catch (std::exception& e) { if (fDebug) LogPrintf("%s\n", e.what()); strLoadError = _("Error opening block database"); + fVerifyingBlocks = false; break; } + fVerifyingBlocks = false; fLoaded = true; } while (false); @@ -1349,6 +1450,7 @@ bool AppInit2(boost::thread_group& threadGroup) } uiInterface.InitMessage(_("Loading wallet...")); + fVerifyingBlocks = true; nStart = GetTimeMillis(); bool fFirstRun = true; @@ -1426,7 +1528,7 @@ bool AppInit2(boost::thread_group& threadGroup) // Restore wallet transaction metadata after -zapwallettxes=1 if (GetBoolArg("-zapwallettxes", false) && GetArg("-zapwallettxes", "1") != "2") { - BOOST_FOREACH (const CWalletTx& wtxOld, vWtx) { + for (const CWalletTx& wtxOld : vWtx) { uint256 hash = wtxOld.GetHash(); std::map::iterator mi = pwalletMain->mapWallet.find(hash); if (mi != pwalletMain->mapWallet.end()) { @@ -1444,6 +1546,7 @@ bool AppInit2(boost::thread_group& threadGroup) } } } + fVerifyingBlocks = false; } // (!fDisableWallet) #else // ENABLE_WALLET LogPrintf("No wallet compiled in!\n"); @@ -1460,7 +1563,7 @@ bool AppInit2(boost::thread_group& threadGroup) std::vector vImportFiles; if (mapArgs.count("-loadblock")) { - BOOST_FOREACH (string strFile, mapMultiArgs["-loadblock"]) + for (string strFile : mapMultiArgs["-loadblock"]) vImportFiles.push_back(strFile); } threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles)); @@ -1494,7 +1597,7 @@ bool AppInit2(boost::thread_group& threadGroup) } if (fMasterNode) { - LogPrintf("IS OBFUSCATION MASTER NODE\n"); + LogPrintf("IS MASTER NODE\n"); strMasterNodeAddr = GetArg("-masternodeaddr", ""); LogPrintf(" addr %s\n", strMasterNodeAddr.c_str()); @@ -1528,7 +1631,7 @@ bool AppInit2(boost::thread_group& threadGroup) LOCK(pwalletMain->cs_wallet); LogPrintf("Locking Masternodes:\n"); uint256 mnTxHash; - BOOST_FOREACH (CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) { + for (CMasternodeConfig::CMasternodeEntry mne : masternodeConfig.getEntries()) { LogPrintf(" %s %s\n", mne.getTxHash(), mne.getOutputIndex()); mnTxHash.SetHex(mne.getTxHash()); COutPoint outpoint = COutPoint(mnTxHash, boost::lexical_cast(mne.getOutputIndex())); @@ -1611,6 +1714,9 @@ bool AppInit2(boost::thread_group& threadGroup) LogPrintf("mapAddressBook.size() = %u\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0); #endif + if (GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) + StartTorControl(threadGroup); + StartNode(threadGroup); #ifdef ENABLE_WALLET diff --git a/src/init.h b/src/init.h index 36300ea..1a519cb 100644 --- a/src/init.h +++ b/src/init.h @@ -19,9 +19,11 @@ extern CWallet* pwalletMain; void StartShutdown(); bool ShutdownRequested(); +/** Interrupt threads */ void Shutdown(); +void Interrupt(); void PrepareShutdown(); -bool AppInit2(boost::thread_group& threadGroup); +bool AppInit2(); /** The help message mode determines what help message to show */ enum HelpMessageMode { diff --git a/src/kernel.cpp b/src/kernel.cpp index 2a5fd66..3d15dfc 100644 --- a/src/kernel.cpp +++ b/src/kernel.cpp @@ -1,7 +1,7 @@ /* @flow */ // Copyright (c) 2012-2013 The PPCoin developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -87,7 +87,7 @@ static bool SelectBlockFromCandidates( bool fSelected = false; uint256 hashBest = 0; *pindexSelected = (const CBlockIndex*)0; - BOOST_FOREACH (const PAIRTYPE(int64_t, uint256) & item, vSortedByTimestamp) { + for (const PAIRTYPE(int64_t, uint256) & item : vSortedByTimestamp) { if (!mapBlockIndex.count(item.second)) return error("SelectBlockFromCandidates: failed to find block index for candidate block %s", item.second.ToString().c_str()); @@ -170,7 +170,7 @@ bool ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64_t& nStakeMod return error("ComputeNextStakeModifier: unable to get last modifier"); if (GetBoolArg("-printstakemodifier", false)) - LogPrintf("ComputeNextStakeModifier: prev modifier= %s time=%s\n", boost::lexical_cast(nStakeModifier).c_str(), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", nModifierTime).c_str()); + LogPrintf("ComputeNextStakeModifier: prev modifier= %s time=%s\n", std::to_string(nStakeModifier).c_str(), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", nModifierTime).c_str()); if (nModifierTime / getIntervalVersion(fTestNet) >= pindexPrev->GetBlockTime() / getIntervalVersion(fTestNet)) return true; @@ -225,7 +225,7 @@ bool ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64_t& nStakeMod strSelectionMap.replace(pindex->nHeight - nHeightFirstCandidate, 1, "="); pindex = pindex->pprev; } - BOOST_FOREACH (const PAIRTYPE(uint256, const CBlockIndex*) & item, mapSelectedBlocks) { + for (const std::pair &item : mapSelectedBlocks) { // 'S' indicates selected proof-of-stake blocks // 'W' indicates selected proof-of-work blocks strSelectionMap.replace(item.second->nHeight - nHeightFirstCandidate, 1, item.second->IsProofOfStake() ? "S" : "W"); @@ -233,7 +233,7 @@ bool ComputeNextStakeModifier(const CBlockIndex* pindexPrev, uint64_t& nStakeMod LogPrintf("ComputeNextStakeModifier: selection height [%d, %d] map %s\n", nHeightFirstCandidate, pindexPrev->nHeight, strSelectionMap.c_str()); } if (fDebug || GetBoolArg("-printstakemodifier", false)) { - LogPrintf("ComputeNextStakeModifier: new modifier=%s time=%s\n", boost::lexical_cast(nStakeModifierNew).c_str(), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexPrev->GetBlockTime()).c_str()); + LogPrintf("ComputeNextStakeModifier: new modifier=%s time=%s\n", std::to_string(nStakeModifierNew).c_str(), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexPrev->GetBlockTime()).c_str()); } nStakeModifier = nStakeModifierNew; diff --git a/src/leveldb/db/memtable.cc b/src/leveldb/db/memtable.cc index bfec0a7..31ad995 100644 --- a/src/leveldb/db/memtable.cc +++ b/src/leveldb/db/memtable.cc @@ -101,7 +101,7 @@ void MemTable::Add(SequenceNumber s, ValueType type, p += 8; p = EncodeVarint32(p, val_size); memcpy(p, value.data(), val_size); - assert((p + val_size) - buf == encoded_len); + assert((p + val_size) - buf == (long)encoded_len); table_.Insert(buf); } diff --git a/src/leveldb/util/bloom.cc b/src/leveldb/util/bloom.cc index a27a2ac..79276b8 100644 --- a/src/leveldb/util/bloom.cc +++ b/src/leveldb/util/bloom.cc @@ -47,7 +47,7 @@ class BloomFilterPolicy : public FilterPolicy { dst->resize(init_size + bytes, 0); dst->push_back(static_cast(k_)); // Remember # of probes in filter char* array = &(*dst)[init_size]; - for (size_t i = 0; i < n; i++) { + for (size_t i = 0; i < (size_t)n; i++) { // Use double-hashing to generate a sequence of hash values. // See analysis in [Kirsch,Mitzenmacher 2006]. uint32_t h = BloomHash(keys[i]); diff --git a/src/leveldb/util/logging.cc b/src/leveldb/util/logging.cc index ca6b324..2a1028a 100644 --- a/src/leveldb/util/logging.cc +++ b/src/leveldb/util/logging.cc @@ -55,7 +55,7 @@ bool ConsumeDecimalNumber(Slice* in, uint64_t* val) { const int delta = (c - '0'); static const uint64_t kMaxUint64 = ~static_cast(0); if (v > kMaxUint64/10 || - (v == kMaxUint64/10 && delta > kMaxUint64%10)) { + (v == kMaxUint64/10 && (uint64_t)delta > kMaxUint64%10)) { // Overflow return false; } diff --git a/src/leveldbwrapper.cpp b/src/leveldbwrapper.cpp index 70980fe..a5c56ef 100644 --- a/src/leveldbwrapper.cpp +++ b/src/leveldbwrapper.cpp @@ -13,7 +13,7 @@ #include #include -void HandleError(const leveldb::Status& status) throw(leveldb_error) +void HandleError(const leveldb::Status& status) { if (status.ok()) return; @@ -27,6 +27,31 @@ void HandleError(const leveldb::Status& status) throw(leveldb_error) throw leveldb_error("Unknown database error"); } +static void SetMaxOpenFiles(leveldb::Options *options) { + // On most platforms the default setting of max_open_files (which is 1000) + // is optimal. On Windows using a large file count is OK because the handles + // do not interfere with select() loops. On 64-bit Unix hosts this value is + // also OK, because up to that amount LevelDB will use an mmap + // implementation that does not use extra file descriptors (the fds are + // closed after being mmaped). + // + // Increasing the value beyond the default is dangerous because LevelDB will + // fall back to a non-mmap implementation when the file count is too large. + // On 32-bit Unix host we should decrease the value because the handles use + // up real fds, and we want to avoid fd exhaustion issues. + // + // See PR #12495 for further discussion. + + int default_open_files = options->max_open_files; +#ifndef WIN32 + if (sizeof(void*) < 8) { + options->max_open_files = 64; + } +#endif + LogPrintf("LevelDB using max_open_files=%d (default=%d)\n", + options->max_open_files, default_open_files); +} + static leveldb::Options GetOptions(size_t nCacheSize) { leveldb::Options options; @@ -34,12 +59,12 @@ static leveldb::Options GetOptions(size_t nCacheSize) options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously options.filter_policy = leveldb::NewBloomFilterPolicy(10); options.compression = leveldb::kNoCompression; - options.max_open_files = 64; if (leveldb::kMajorVersion > 1 || (leveldb::kMajorVersion == 1 && leveldb::kMinorVersion >= 16)) { // LevelDB versions before 1.16 consider short writes to be corruption. Only trigger error // on corruption in later versions. options.paranoid_checks = true; } + SetMaxOpenFiles(&options); return options; } @@ -80,7 +105,7 @@ CLevelDBWrapper::~CLevelDBWrapper() options.env = NULL; } -bool CLevelDBWrapper::WriteBatch(CLevelDBBatch& batch, bool fSync) throw(leveldb_error) +bool CLevelDBWrapper::WriteBatch(CLevelDBBatch& batch, bool fSync) { leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch); HandleError(status); diff --git a/src/leveldbwrapper.h b/src/leveldbwrapper.h index 4247920..6f8427a 100644 --- a/src/leveldbwrapper.h +++ b/src/leveldbwrapper.h @@ -22,7 +22,7 @@ class leveldb_error : public std::runtime_error leveldb_error(const std::string& msg) : std::runtime_error(msg) {} }; -void HandleError(const leveldb::Status& status) throw(leveldb_error); +void HandleError(const leveldb::Status& status); /** Batch of changes queued to be written to a CLevelDBWrapper */ class CLevelDBBatch @@ -90,7 +90,7 @@ class CLevelDBWrapper ~CLevelDBWrapper(); template - bool Read(const K& key, V& value) const throw(leveldb_error) + bool Read(const K& key, V& value) const { CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(ssKey.GetSerializeSize(key)); @@ -115,7 +115,7 @@ class CLevelDBWrapper } template - bool Write(const K& key, const V& value, bool fSync = false) throw(leveldb_error) + bool Write(const K& key, const V& value, bool fSync = false) { CLevelDBBatch batch; batch.Write(key, value); @@ -123,7 +123,7 @@ class CLevelDBWrapper } template - bool Exists(const K& key) const throw(leveldb_error) + bool Exists(const K& key) const { CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(ssKey.GetSerializeSize(key)); @@ -142,14 +142,14 @@ class CLevelDBWrapper } template - bool Erase(const K& key, bool fSync = false) throw(leveldb_error) + bool Erase(const K& key, bool fSync = false) { CLevelDBBatch batch; batch.Erase(key); return WriteBatch(batch, fSync); } - bool WriteBatch(CLevelDBBatch& batch, bool fSync = false) throw(leveldb_error); + bool WriteBatch(CLevelDBBatch& batch, bool fSync = false); // not available for LevelDB; provide for compatibility with BDB bool Flush() @@ -157,7 +157,7 @@ class CLevelDBWrapper return true; } - bool Sync() throw(leveldb_error) + bool Sync() { CLevelDBBatch batch; return WriteBatch(batch, true); diff --git a/src/main.cpp b/src/main.cpp index 9962972..a13733d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,7 +2,7 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -22,20 +22,24 @@ #include "obfuscation.h" #include "pow.h" #include "spork.h" +#include "sporkdb.h" #include "swifttx.h" #include "txdb.h" #include "txmempool.h" #include "ui_interface.h" #include "util.h" #include "utilmoneystr.h" +#include "validationinterface.h" #include #include #include #include -#include #include +#include +#include +#include using namespace boost; using namespace std; @@ -44,15 +48,21 @@ using namespace std; #error "XDNA cannot be compiled without assertions." #endif +// 6 comes from OPCODE (1) + vch.size() (1) + BIGNUM size (4) +#define SCRIPT_OFFSET 6 +// For Script size (BIGNUM/Uint256 size) +#define BIGNUM_SIZE 4 /** * Global state */ CCriticalSection cs_main; +CCriticalSection cs_mapstake; BlockMap mapBlockIndex; map mapProofOfStake; set > setStakeSeen; +map mapStakeSpent; map mapHashedBlocks; CChain chainActive; CBlockIndex* pindexBestHeader = NULL; @@ -65,10 +75,15 @@ bool fReindex = false; bool fTxIndex = true; bool fIsBareMultisigStd = true; bool fCheckBlockIndex = false; +bool fVerifyingBlocks = false; unsigned int nCoinCacheSize = 5000; bool fAlerts = DEFAULT_ALERTS; +bool fClearSpendCache = false; -unsigned int nStakeMinAge = 60 * 60; +/* If the tip is older than this (in seconds), the node is considered to be in initial block download. */ +int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; + +unsigned int nStakeMinAge = 1 * 60 * 60; int64_t nReserveBalance = 0; /** Fees smaller than this (in uxdna) are considered zero fee (for relaying and mining) @@ -87,7 +102,6 @@ map mapOrphanTransactions; map > mapOrphanTransactionsByPrev; map mapRejectedBlocks; - void EraseOrphansFor(NodeId peer); static void CheckBlockIndex(); @@ -176,84 +190,95 @@ set setDirtyFileInfo; ////////////////////////////////////////////////////////////////////////////// // -// dispatching functions +// Registration of network node signals. // -// These functions dispatch to one or all registered wallets - namespace { -struct CMainSignals { - /** Notifies listeners of updated transaction data (transaction, and optionally the block it is found in. */ - boost::signals2::signal SyncTransaction; - /** Notifies listeners of an erased transaction (currently disabled, requires transaction replacement). */ - // XX42 boost::signals2::signal EraseTransaction; - /** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */ - boost::signals2::signal UpdatedTransaction; - /** Notifies listeners of a new active block chain. */ - boost::signals2::signal SetBestChain; - /** Notifies listeners about an inventory item being seen on the network. */ - boost::signals2::signal Inventory; - /** Tells listeners to broadcast their data. */ - boost::signals2::signal Broadcast; - /** Notifies listeners of a block validation result */ - boost::signals2::signal BlockChecked; -} g_signals; +struct CBlockReject { + unsigned char chRejectCode; + string strRejectReason; + uint256 hashBlock; +}; -} // anon namespace -void RegisterValidationInterface(CValidationInterface* pwalletIn) +class CNodeBlocks { - g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2)); - // XX42 g_signals.EraseTransaction.connect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1)); - g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); - g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); - g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); - g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn)); - g_signals.BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); -} +public: + CNodeBlocks(): + maxSize(0), + maxAvg(0) + { + maxSize = GetArg("-blockspamfiltermaxsize", DEFAULT_BLOCK_SPAM_FILTER_MAX_SIZE); + maxAvg = GetArg("-blockspamfiltermaxavg", DEFAULT_BLOCK_SPAM_FILTER_MAX_AVG); + } -void UnregisterValidationInterface(CValidationInterface* pwalletIn) -{ - g_signals.BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); - g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn)); - g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); - g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); - g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); - // XX42 g_signals.EraseTransaction.disconnect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1)); - g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2)); -} + bool onBlockReceived(int nHeight) { + if(nHeight > 0 && maxSize && maxAvg) { + addPoint(nHeight); + return true; + } + return false; + } -void UnregisterAllValidationInterfaces() -{ - g_signals.BlockChecked.disconnect_all_slots(); - g_signals.Broadcast.disconnect_all_slots(); - g_signals.Inventory.disconnect_all_slots(); - g_signals.SetBestChain.disconnect_all_slots(); - g_signals.UpdatedTransaction.disconnect_all_slots(); - // XX42 g_signals.EraseTransaction.disconnect_all_slots(); - g_signals.SyncTransaction.disconnect_all_slots(); -} + bool updateState(CValidationState& state, bool ret) + { + // No Blocks + size_t size = points.size(); + if(size == 0) + return ret; + + // Compute the number of the received blocks + size_t nBlocks = 0; + for(auto point : points) + { + nBlocks += point.second; + } -void SyncWithWallets(const CTransaction& tx, const CBlock* pblock) -{ - g_signals.SyncTransaction(tx, pblock); -} + // Compute the average value per height + double nAvgValue = (double)nBlocks / size; -////////////////////////////////////////////////////////////////////////////// -// -// Registration of network node signals. -// + // Ban the node if try to spam + bool banNode = (nAvgValue >= 1.5 * maxAvg && size >= maxAvg) || + (nAvgValue >= maxAvg && nBlocks >= maxSize) || + (nBlocks >= maxSize * 3); + if(banNode) + { + // Clear the points and ban the node + points.clear(); + return state.DoS(100, error("block-spam ban node for sending spam")); + } -namespace -{ -struct CBlockReject { - unsigned char chRejectCode; - string strRejectReason; - uint256 hashBlock; + return ret; + } + +private: + void addPoint(int height) + { + // Remove the last element in the list + if(points.size() == maxSize) + { + points.erase(points.begin()); + } + + // Add the point to the list + int occurrence = 0; + auto mi = points.find(height); + if (mi != points.end()) + occurrence = (*mi).second; + occurrence++; + points[height] = occurrence; + } + +private: + std::map points; + size_t maxSize; + size_t maxAvg; }; -/** + + + /** * Maintain validation-specific state about nodes, protected by cs_main, instead * by CNode's own locks. This simplifies asynchronous operation, where * processing of incoming data is done after the ProcessMessage call returns, @@ -287,6 +312,8 @@ struct CNodeState { //! Whether we consider this a preferred download peer. bool fPreferredDownload; + CNodeBlocks nodeBlocks; + CNodeState() { fCurrentlyConnected = false; @@ -356,7 +383,7 @@ void FinalizeNode(NodeId nodeid) AddressCurrentlyConnected(state->address); } - BOOST_FOREACH (const QueuedBlock& entry, state->vBlocksInFlight) + for (const QueuedBlock& entry : state->vBlocksInFlight) mapBlocksInFlight.erase(entry.hash); EraseOrphansFor(nodeid); nPreferredDownload -= state->fPreferredDownload; @@ -503,7 +530,7 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vectorIsValid(BLOCK_VALID_TREE)) { // We consider the chain that this peer is on invalid. return; @@ -544,7 +571,7 @@ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) stats.nMisbehavior = state->nMisbehavior; stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1; stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1; - BOOST_FOREACH (const QueuedBlock& queue, state->vBlocksInFlight) { + for (const QueuedBlock& queue : state->vBlocksInFlight) { if (queue.pindex) stats.vHeightInFlight.push_back(queue.pindex->nHeight); } @@ -572,7 +599,7 @@ void UnregisterNodeSignals(CNodeSignals& nodeSignals) CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) { // Find the first block the caller has in the main chain - BOOST_FOREACH (const uint256& hash, locator.vHave) { + for (const uint256& hash : locator.vHave) { BlockMap::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end()) { CBlockIndex* pindex = (*mi).second; @@ -585,6 +612,7 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc CCoinsViewCache* pcoinsTip = NULL; CBlockTreeDB* pblocktree = NULL; +CSporkDB* pSporkDB = NULL; ////////////////////////////////////////////////////////////////////////////// // @@ -612,7 +640,7 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer) mapOrphanTransactions[hash].tx = tx; mapOrphanTransactions[hash].fromPeer = peer; - BOOST_FOREACH (const CTxIn& txin, tx.vin) + for (const CTxIn& txin : tx.vin) mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash); LogPrint("mempool", "stored orphan tx %s (mapsz %u prevsz %u)\n", hash.ToString(), @@ -625,7 +653,7 @@ void static EraseOrphanTx(uint256 hash) map::iterator it = mapOrphanTransactions.find(hash); if (it == mapOrphanTransactions.end()) return; - BOOST_FOREACH (const CTxIn& txin, it->second.tx.vin) { + for (const CTxIn& txin : it->second.tx.vin) { map >::iterator itPrev = mapOrphanTransactionsByPrev.find(txin.prevout.hash); if (itPrev == mapOrphanTransactionsByPrev.end()) continue; @@ -701,12 +729,13 @@ bool IsStandardTx(const CTransaction& tx, string& reason) // computing signature hashes is O(ninputs*txsize). Limiting transactions // to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks. unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); - if (sz >= MAX_STANDARD_TX_SIZE) { + unsigned int nMaxSize = MAX_STANDARD_TX_SIZE; + if (sz >= nMaxSize) { reason = "tx-size"; return false; } - BOOST_FOREACH (const CTxIn& txin, tx.vin) { + for (const CTxIn& txin : tx.vin) { // Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed // keys. (remember the 520 byte limit on redeemScript size) That works // out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)+3=1627 @@ -726,7 +755,7 @@ bool IsStandardTx(const CTransaction& tx, string& reason) unsigned int nDataOut = 0; txnouttype whichType; - BOOST_FOREACH (const CTxOut& txout, tx.vout) { + for (const CTxOut& txout : tx.vout) { if (!::IsStandard(txout.scriptPubKey, whichType)) { reason = "scriptpubkey"; return false; @@ -836,10 +865,10 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) unsigned int GetLegacySigOpCount(const CTransaction& tx) { unsigned int nSigOps = 0; - BOOST_FOREACH (const CTxIn& txin, tx.vin) { + for (const CTxIn& txin : tx.vin) { nSigOps += txin.scriptSig.GetSigOpCount(false); } - BOOST_FOREACH (const CTxOut& txout, tx.vout) { + for (const CTxOut& txout : tx.vout) { nSigOps += txout.scriptPubKey.GetSigOpCount(false); } return nSigOps; @@ -925,7 +954,7 @@ bool GetCoinAge(const CTransaction& tx, const unsigned int nTxTime, uint64_t& nC nCoinAge = 0; CBlockIndex* pindex = NULL; - BOOST_FOREACH (const CTxIn& txin, tx.vin) { + for (const CTxIn& txin : tx.vin) { // First try finding the previous transaction in database CTransaction txPrev; uint256 hashBlockPrev; @@ -977,7 +1006,6 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state) if (tx.vout.empty()) return state.DoS(10, error("CheckTransaction() : vout empty"), REJECT_INVALID, "bad-txns-vout-empty"); - // Size limits if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) return state.DoS(100, error("CheckTransaction() : size limits failed"), @@ -985,13 +1013,13 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state) // Check for negative or overflow output values CAmount nValueOut = 0; - BOOST_FOREACH (const CTxOut& txout, tx.vout) { + for (const CTxOut& txout : tx.vout) { if (txout.IsEmpty() && !tx.IsCoinBase() && !tx.IsCoinStake()) return state.DoS(100, error("CheckTransaction(): txout empty for user transaction")); - if (txout.nValue < 0){LogPrintf("txout.nValue= %d",txout.nValue); + if (txout.nValue < 0) return state.DoS(100, error("CheckTransaction() : txout.nValue negative"), - REJECT_INVALID, "bad-txns-vout-negative");} + REJECT_INVALID, "bad-txns-vout-negative"); if (txout.nValue > Params().MaxMoneyOut()) return state.DoS(100, error("CheckTransaction() : txout.nValue too high"), REJECT_INVALID, "bad-txns-vout-toolarge"); @@ -1001,9 +1029,9 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state) REJECT_INVALID, "bad-txns-txouttotal-toolarge"); } - // Check for duplicate inputs set vInOutPoints; - BOOST_FOREACH (const CTxIn& txin, tx.vin) { + for (const CTxIn& txin : tx.vin) { + // Check for duplicate inputs if (vInOutPoints.count(txin.prevout)) return state.DoS(100, error("CheckTransaction() : duplicate inputs"), REJECT_INVALID, "bad-txns-inputs-duplicate"); @@ -1011,12 +1039,12 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state) } if (tx.IsCoinBase()) { - if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 150) + if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 150) + return state.DoS(100, error("CheckTransaction() : coinbase script size=%d", tx.vin[0].scriptSig.size()), + REJECT_INVALID, "bad-cb-length"); - return state.DoS(100, error("CheckTransaction() : coinbase script size=%d", tx.vin[0].scriptSig.size()), - REJECT_INVALID, "bad-cb-length"); } else { - BOOST_FOREACH (const CTxIn& txin, tx.vin) + for (const CTxIn& txin : tx.vin) if (txin.prevout.IsNull()) return state.DoS(10, error("CheckTransaction() : prevout is null"), REJECT_INVALID, "bad-txns-prevout-null"); @@ -1083,7 +1111,6 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF return nMinFee; } - bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState& state, const CTransaction& tx, bool fLimitFree, bool* pfMissingInputs, bool fRejectInsaneFee, bool ignoreFees) { AssertLockHeld(cs_main); @@ -1100,9 +1127,15 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState& state, const CTransa //Coinstake is also only valid in a block, not as a loose transaction if (tx.IsCoinStake()) - return state.DoS(100, error("AcceptToMemoryPool: coinstake as individual tx"), + return state.DoS(100, error("AcceptToMemoryPool: coinstake as individual tx. txid=%s", tx.GetHash().GetHex()), REJECT_INVALID, "coinstake"); + // Only accept nLockTime-using transactions that can be mined in the next + // block; we don't want our mempool filled up with transactions that can't + // be mined yet. + if (!CheckFinalTx(tx, STANDARD_LOCKTIME_VERIFY_FLAGS)) + return state.DoS(0, false, REJECT_NONSTANDARD, "non-final"); + // Rather not work on nonstandard transactions (unless -testnet/-regtest) string reason; if (Params().RequireStandard() && !IsStandardTx(tx, reason)) @@ -1116,31 +1149,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState& state, const CTransa return false; } - // ----------- swiftTX transaction scanning ----------- - - BOOST_FOREACH (const CTxIn& in, tx.vin) { - if (mapLockedInputs.count(in.prevout)) { - if (mapLockedInputs[in.prevout] != tx.GetHash()) { - return state.DoS(0, - error("AcceptToMemoryPool : conflicts with existing transaction lock: %s", reason), - REJECT_INVALID, "tx-lock-conflict"); - } - } - } - - // Check for conflicts with in-memory transactions - { - LOCK(pool.cs); // protect pool.mapNextTx - for (unsigned int i = 0; i < tx.vin.size(); i++) { - COutPoint outpoint = tx.vin[i].prevout; - if (pool.mapNextTx.count(outpoint)) { - // Disable replacement feature for now - return false; - } - } - } - - { CCoinsView dummy; CCoinsViewCache view(&dummy); @@ -1158,7 +1166,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState& state, const CTransa // do all inputs exist? // Note that this does not check for the presence of actual outputs (see the next check for that), // only helps filling in pfMissingInputs (to determine missing vs spent). - for (const CTxIn txin : tx.vin) { + for (const CTxIn& txin : tx.vin) { if (!view.HaveCoins(txin.prevout.hash)) { if (pfMissingInputs) *pfMissingInputs = true; @@ -1169,7 +1177,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState& state, const CTransa // are the actual inputs available? if (!view.HaveInputs(tx)) return state.Invalid(error("AcceptToMemoryPool : inputs already spent"), - REJECT_DUPLICATE, "bad-txns-inputs-spent"); + REJECT_DUPLICATE, "bad-txns-inputs-spent"); // Bring the best block into scope view.GetBestBlock(); @@ -1184,6 +1192,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState& state, const CTransa if (Params().RequireStandard() && !AreInputsStandard(tx, view)) return error("AcceptToMemoryPool: : nonstandard transaction input"); + static const unsigned int MAX_BLOCK_SIGOPS = 1000000 / 50; + static const unsigned int MAX_TX_SIGOPS = MAX_BLOCK_SIGOPS / 5; + // Check that the transaction doesn't have an excessive number of // sigops, making it impossible to mine. Since the coinbase transaction // itself can contain sigops MAX_TX_SIGOPS is less than @@ -1192,10 +1203,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState& state, const CTransa unsigned int nSigOps = GetLegacySigOpCount(tx); nSigOps += GetP2SHSigOpCount(tx, view); if (nSigOps > MAX_TX_SIGOPS) - return state.DoS(0, - error("AcceptToMemoryPool : too many sigops %s, %d > %d", + return state.DoS(0, + error("AcceptToMemoryPool : too many sigops %s, %d > %d", hash.ToString(), nSigOps, MAX_TX_SIGOPS), - REJECT_NONSTANDARD, "bad-txns-too-many-sigops"); + REJECT_NONSTANDARD, "bad-txns-too-many-sigops"); CAmount nValueOut = tx.GetValueOut(); CAmount nFees = nValueIn - nValueOut; @@ -1204,46 +1215,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState& state, const CTransa CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height()); unsigned int nSize = entry.GetTxSize(); - // Don't accept it if it can't get into a block - // but prioritise dstx and don't check fees for it - if (mapObfuscationBroadcastTxes.count(hash)) { - mempool.PrioritiseTransaction(hash, hash.ToString(), 1000, 0.1 * COIN); - } else if (!ignoreFees) { - CAmount txMinFee = GetMinRelayFee(tx, nSize, true); - if (fLimitFree && nFees < txMinFee) - return state.DoS(0, error("AcceptToMemoryPool : not enough fees %s, %d < %d", - hash.ToString(), nFees, txMinFee), - REJECT_INSUFFICIENTFEE, "insufficient fee"); - - // Require that free transactions have sufficient priority to be mined in the next block. - if (GetBoolArg("-relaypriority", true) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) { - return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority"); - } - - // Continuously rate-limit free (really, very-low-fee) transactions - // This mitigates 'penny-flooding' -- sending thousands of free transactions just to - // be annoying or make others' transactions take longer to confirm. - if (fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize)) { - static CCriticalSection csFreeLimiter; - static double dFreeCount; - static int64_t nLastTime; - int64_t nNow = GetTime(); - - LOCK(csFreeLimiter); - - // Use an exponentially decaying ~10-minute window: - dFreeCount *= pow(1.0 - 1.0 / 600.0, (double)(nNow - nLastTime)); - nLastTime = nNow; - // -limitfreerelay unit is thousand-bytes-per-minute - // At default rate it would take over a month to fill 1GB - if (dFreeCount >= GetArg("-limitfreerelay", 30) * 10 * 1000) - return state.DoS(0, error("AcceptToMemoryPool : free transaction rejected by rate limiter"), - REJECT_INSUFFICIENTFEE, "rate limited free transaction"); - LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount + nSize); - dFreeCount += nSize; - } - } - if (fRejectInsaneFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000) return error("AcceptToMemoryPool: : insane fees %s, %d > %d", hash.ToString(), @@ -1283,6 +1254,7 @@ bool AcceptableInputs(CTxMemPool& pool, CValidationState& state, const CTransact if (pfMissingInputs) *pfMissingInputs = false; + if (!CheckTransaction(tx, state)) return error("AcceptableInputs: : CheckTransaction failed"); @@ -1304,31 +1276,6 @@ bool AcceptableInputs(CTxMemPool& pool, CValidationState& state, const CTransact if (pool.exists(hash)) return false; - // ----------- swiftTX transaction scanning ----------- - - BOOST_FOREACH (const CTxIn& in, tx.vin) { - if (mapLockedInputs.count(in.prevout)) { - if (mapLockedInputs[in.prevout] != tx.GetHash()) { - return state.DoS(0, - error("AcceptableInputs : conflicts with existing transaction lock: %s", reason), - REJECT_INVALID, "tx-lock-conflict"); - } - } - } - - // Check for conflicts with in-memory transactions - { - LOCK(pool.cs); // protect pool.mapNextTx - for (unsigned int i = 0; i < tx.vin.size(); i++) { - COutPoint outpoint = tx.vin[i].prevout; - if (pool.mapNextTx.count(outpoint)) { - // Disable replacement feature for now - return false; - } - } - } - - { CCoinsView dummy; CCoinsViewCache view(&dummy); @@ -1468,16 +1415,30 @@ bool AcceptableInputs(CTxMemPool& pool, CValidationState& state, const CTransact return true; } +bool GetOutput(const uint256& hash, unsigned int index, CValidationState& state, CTxOut& out) +{ + CTransaction txPrev; + uint256 hashBlock; + if (!GetTransaction(hash, txPrev, hashBlock, true)) { + return state.DoS(100, error("Output not found")); + } + if (index > txPrev.vout.size()) { + return state.DoS(100, error("Output not found, invalid index %d for %s",index, hash.GetHex())); + } + out = txPrev.vout[index]; + return true; +} + /** Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock */ -bool GetTransaction(const uint256& hash, CTransaction& txOut, uint256& hashBlock, bool fAllowSlow) +bool GetTransaction(const uint256& hash, CTransaction& txOut, uint256& hashBlock, bool fAllowSlow, CBlockIndex* blockIndex) { - CBlockIndex* pindexSlow = NULL; - { - LOCK(cs_main); - { - if (mempool.lookup(hash, txOut)) { - return true; - } + CBlockIndex* pindexSlow = blockIndex; + + LOCK(cs_main); + + if (!blockIndex) { + if (mempool.lookup(hash, txOut)) { + return true; } if (fTxIndex) { @@ -1520,7 +1481,7 @@ bool GetTransaction(const uint256& hash, CTransaction& txOut, uint256& hashBlock if (pindexSlow) { CBlock block; if (ReadBlockFromDisk(block, pindexSlow)) { - BOOST_FOREACH (const CTransaction& tx, block.vtx) { + for (const CTransaction& tx : block.vtx) { if (tx.GetHash() == hash) { txOut = tx; hashBlock = pindexSlow->GetBlockHash(); @@ -1623,51 +1584,30 @@ CAmount GetBlockValue(int nHeight, uint32_t nTime) } else if (nHeight <= Params().ANTI_INSTAMINE_TIME()) { return 1 * COIN; - // POS Year 1 - } else if (nHeight <= 1965600 && nHeight > Params().LAST_POW_BLOCK()) { - return 57 * COIN; - // POS Year 2 - } else if (nHeight <= 2491200 && nHeight >= 1965601) { - return 53 * COIN; - // POS Year 3 - } else if (nHeight <= 3016800 && nHeight >= 2491201) { - return 49 * COIN; - // POS Year 4 - } else if (nHeight <= 3542400 && nHeight >= 3016801) { - return 45 * COIN; - // POS Year 5 - } else if (nHeight <= 4068000 && nHeight >= 3542401) { - return 41 * COIN; - // POS Year 6 - } else if (nHeight <= 4593600 && nHeight >= 4068001) { - return 37 * COIN; - // POS Year 7 - } else if (nHeight <= 5119200 && nHeight >= 4593601) { - return 33 * COIN; - // POS Year 8 - } else if (nHeight <= 5644800 && nHeight >= 5119201) { - return 29 * COIN; - // POS Year 9 - } else if (nHeight <= 6170400 && nHeight >= 5644801) { - return 25 * COIN; - // POS Year 10 - } else if (nHeight <= 6696000 && nHeight >= 6170401) { - return 21 * COIN; - // POS Year 11 - } else if (nHeight <= 7221600 && nHeight >= 6696001) { - return 17 * COIN; - // POS Year 12 - } else if (nHeight <= 7747200 && nHeight >= 7221601) { - return 13 * COIN; - // POS Year 13 - } else if (nHeight <= 8272800 && nHeight >= 7747201) { - return 9 * COIN; - // POS Year 14 - } else if (nHeight <= 8798400 && nHeight >= 8272801) { - return 5 * COIN; - // POS Year 15 - } else if (nHeight >= 8798401) { + // POS Year 1 & 2 + } else if (nHeight <= 1733700 && nHeight > Params().LAST_POW_BLOCK()) { + return 8 * COIN; + // POS Year 3 & 4 + } else if (nHeight <= 2784900 && nHeight >= 1733701) { + return 4 * COIN; + // POS Year 5 & 6 + } else if (nHeight <= 3836100 && nHeight >= 2784901) { + return 2 * COIN; + // POS Year 7 & 8 + } else if (nHeight <= 4887300 && nHeight >= 3836101) { return 1 * COIN; + // POS Year 9 & 10 + } else if (nHeight <= 5938500 && nHeight >= 4887301) { + return 0.5 * COIN; + // POS Year 11 & 12 + } else if (nHeight <= 6989700 && nHeight >= 5938501) { + return 0.25 * COIN; + // POS Year 12 & 13 + } else if (nHeight <= 8040900 && nHeight >= 6989701) { + return 0.125 * COIN; + // POS Year 14 + } else if (nHeight >= 8040901) { + return 0.0625 * COIN; } int64_t netHashRate = chainActive.GetNetworkHashPS(24, nHeight); @@ -1684,8 +1624,10 @@ int64_t GetMasternodePayment(int nHeight, uint32_t nTime, unsigned mnlevel, int6 if(nTime <= Params().F2ActivationTime()) coeff = { 3, 9, 15 }; - else + else if (nTime > Params().F2ActivationTime() && (nHeight <= 682500)) coeff = { 5, 15, 25 }; + else + coeff = { 10, 30, 50 }; if(mnlevel - 1 < coeff.size()) return blockValue / 100 * coeff[mnlevel - 1]; @@ -1695,17 +1637,28 @@ int64_t GetMasternodePayment(int nHeight, uint32_t nTime, unsigned mnlevel, int6 bool IsInitialBlockDownload() { + const CChainParams& chainParams = Params(); + + // Once this function has returned false, it must remain false. + static std::atomic latchToFalse{false}; + // Optimization: pre-test latch before taking the lock. + if (latchToFalse.load(std::memory_order_relaxed)) + return false; + LOCK(cs_main); - if (fImporting || fReindex || chainActive.Height() < Checkpoints::GetTotalBlocksEstimate()) - return true; - static bool lockIBDState = false; - if (lockIBDState) + if (latchToFalse.load(std::memory_order_relaxed)) return false; - bool state = (chainActive.Height() < pindexBestHeader->nHeight - 24 * 6 || - pindexBestHeader->GetBlockTime() < GetTime() - 6 * 60 * 60); // ~144 blocks behind -> 2 x fork detection time - if (!state) - lockIBDState = true; - return state; + if (fImporting || fReindex) + return true; + if (chainActive.Tip() == NULL) + return true; + if (chainActive.Height() < Checkpoints::GetTotalBlocksEstimate()) + return true; + if (chainActive.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge)) + return true; + latchToFalse.store(true, std::memory_order_relaxed); + return false; + } bool fLargeWorkForkFound = false; @@ -1839,7 +1792,7 @@ void UpdateCoins(const CTransaction& tx, CValidationState& state, CCoinsViewCach // mark inputs spent if (!tx.IsCoinBase()) { txundo.vprevout.reserve(tx.vin.size()); - BOOST_FOREACH (const CTxIn& txin, tx.vin) { + for (const CTxIn& txin : tx.vin) { txundo.vprevout.push_back(CTxInUndo()); bool ret = inputs.ModifyCoins(txin.prevout.hash)->Spend(txin.prevout, txundo.vprevout.back()); assert(ret); @@ -1982,6 +1935,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex // undo transactions in reverse order for (int i = block.vtx.size() - 1; i >= 0; i--) { const CTransaction& tx = block.vtx[i]; + uint256 hash = tx.GetHash(); // Check that all outputs are available and match the outputs in the block itself @@ -2007,7 +1961,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex } // restore inputs - if (!tx.IsCoinBase()) { // not coinbases + if (!tx.IsCoinBase()) { // not coinbases because they dont have traditional inputs const CTxUndo& txundo = blockUndo.vtxundo[i - 1]; if (txundo.vprevout.size() != tx.vin.size()) return error("DisconnectBlock() : transaction and undo data inconsistent - txundo.vprevout.siz=%d tx.vin.siz=%d", txundo.vprevout.size(), tx.vin.size()); @@ -2032,6 +1986,10 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex if (coins->vout.size() < out.n + 1) coins->vout.resize(out.n + 1); coins->vout[out.n] = undo.txout; + + LOCK(cs_mapstake); + // erase the spent input + mapStakeSpent.erase(out); } } } @@ -2086,11 +2044,11 @@ static int64_t nTimeIndex = 0; static int64_t nTimeCallbacks = 0; static int64_t nTimeTotal = 0; -bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck) +bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck, bool fAlreadyChecked) { AssertLockHeld(cs_main); // Check it again in case a previous version let a bad block in - if (!CheckBlock(block, state, !fJustCheck, !fJustCheck)) + if (!fAlreadyChecked && !CheckBlock(block, state, !fJustCheck, !fJustCheck)) return false; // verify that the view's current state corresponds to the previous block @@ -2110,9 +2068,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, error("ConnectBlock() : PoS period not active"), REJECT_INVALID, "PoS-early"); - if (pindex->nHeight > Params().LAST_POW_BLOCK() && block.IsProofOfWork()) - return state.DoS(100, error("ConnectBlock() : PoW period ended"), - REJECT_INVALID, "PoW-ended"); + if (pindex->nHeight > Params().LAST_POW_BLOCK() && block.IsProofOfWork()) { + return state.DoS(100, error("ConnectBlock() : PoW period ended"), REJECT_INVALID, "PoW-ended"); + } bool fScriptChecks = pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(); @@ -2141,8 +2099,6 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // DERSIG (BIP66) rules flags |= SCRIPT_VERIFY_DERSIG; - CBlockUndo blockundo; - CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); int64_t nTimeStart = GetTimeMicros(); @@ -2152,6 +2108,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); std::vector > vPos; vPos.reserve(block.vtx.size()); + CBlockUndo blockundo; blockundo.vtxundo.reserve(block.vtx.size() - 1); CAmount nValueOut = 0; CAmount nValueIn = 0; @@ -2202,7 +2159,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // ppcoin: track money supply and mint amount info CAmount nMoneySupplyPrev = pindex->pprev ? pindex->pprev->nMoneySupply : 0; pindex->nMoneySupply = nMoneySupplyPrev + nValueOut - nValueIn; - pindex->nMint = pindex->nMoneySupply - nMoneySupplyPrev; + pindex->nMint = pindex->nMoneySupply - nMoneySupplyPrev + nFees; if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex))) return error("Connect() : WriteBlockIndex for pindex failed"); @@ -2216,6 +2173,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (block.IsProofOfWork()) nExpectedMint += nFees; + //Check that the block does not overmint if (!IsBlockValueValid(block, nExpectedMint, pindex->nMint)) { return state.DoS(100, error("ConnectBlock() : reward pays too much (actual=%s vs limit=%s)", @@ -2224,7 +2182,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } if (!control.Wait()) - return state.DoS(100, false); + return state.DoS(100, error("%s: CheckQueue failed", __func__), REJECT_INVALID, "block-validation-failed"); int64_t nTime2 = GetTimeMicros(); nTimeVerify += nTime2 - nTimeStart; LogPrint("bench", " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs]\n", nInputs - 1, 0.001 * (nTime2 - nTimeStart), nInputs <= 1 ? 0 : 0.001 * (nTime2 - nTimeStart) / (nInputs - 1), nTimeVerify * 0.000001); @@ -2254,6 +2212,29 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (!pblocktree->WriteTxIndex(vPos)) return state.Abort("Failed to write transaction index"); + { + LOCK(cs_mapstake); + // add new entries + for (const CTransaction& tx : block.vtx) { + if (tx.IsCoinBase()) + continue; + for (const CTxIn& in: tx.vin) { + if (fDebug) LogPrintf("mapStakeSpent: Insert %s | %u\n", in.prevout.ToString(), pindex->nHeight); + mapStakeSpent.insert(std::make_pair(in.prevout, pindex->nHeight)); + } + } + + // delete old entries + for (auto it = mapStakeSpent.begin(); it != mapStakeSpent.end();) { + if (it->second < pindex->nHeight - Params().MaxReorganizationDepth()) { + if (fDebug) LogPrintf("mapStakeSpent: Erase %s | %u\n", it->first.ToString(), it->second); + it = mapStakeSpent.erase(it); + } + else { + it++; + } + } + } // add this block to the view's block chain view.SetBestBlock(pindex->GetBlockHash()); @@ -2263,7 +2244,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // Watch for changes to the previous coinbase transaction. static uint256 hashPrevBestCoinBase; - g_signals.UpdatedTransaction(hashPrevBestCoinBase); + GetMainSignals().UpdatedTransaction(hashPrevBestCoinBase); hashPrevBestCoinBase = block.vtx[0].GetHash(); int64_t nTime4 = GetTimeMicros(); @@ -2325,7 +2306,7 @@ bool static FlushStateToDisk(CValidationState& state, FlushStateMode mode) return state.Abort("Failed to write to coin database"); // Update best block in wallet (so we can detect restored wallets). if (mode != FLUSH_STATE_IF_NEEDED) { - g_signals.SetBestChain(chainActive.GetLocator()); + GetMainSignals().SetBestChain(chainActive.GetLocator()); } nLastWrite = GetTimeMicros(); } @@ -2350,8 +2331,8 @@ void static UpdateTip(CBlockIndex* pindexNew) nTimeBestReceived = GetTime(); mempool.AddTransactionsUpdated(1); - LogPrintf("UpdateTip: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%u\n", - chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble()) / log(2.0), (unsigned long)chainActive.Tip()->nChainTx, + LogPrintf("UpdateTip: new best=%s height=%d version=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%u\n", + chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), chainActive.Tip()->nVersion, log(chainActive.Tip()->nChainWork.getdouble()) / log(2.0), (unsigned long)chainActive.Tip()->nChainTx, DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), Checkpoints::GuessVerificationProgress(chainActive.Tip()), (unsigned int)pcoinsTip->GetCacheSize()); @@ -2401,7 +2382,7 @@ bool static DisconnectTip(CValidationState& state) if (!FlushStateToDisk(state, FLUSH_STATE_ALWAYS)) return false; // Resurrect mempool transactions from the disconnected block. - BOOST_FOREACH (const CTransaction& tx, block.vtx) { + for (const CTransaction& tx : block.vtx) { // ignore validation errors in resurrected transactions list removed; CValidationState stateDummy; @@ -2414,7 +2395,7 @@ bool static DisconnectTip(CValidationState& state) UpdateTip(pindexDelete->pprev); // Let wallets know transactions went from 1-confirmed to // 0-confirmed or conflicted: - BOOST_FOREACH (const CTransaction& tx, block.vtx) { + for (const CTransaction& tx : block.vtx) { SyncWithWallets(tx, NULL); } return true; @@ -2430,12 +2411,15 @@ static int64_t nTimePostConnect = 0; * Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock * corresponding to pindexNew, to bypass loading it again from disk. */ -bool static ConnectTip(CValidationState& state, CBlockIndex* pindexNew, CBlock* pblock) +bool static ConnectTip(CValidationState& state, CBlockIndex* pindexNew, CBlock* pblock, bool fAlreadyChecked) { assert(pindexNew->pprev == chainActive.Tip()); mempool.check(pcoinsTip); CCoinsViewCache view(pcoinsTip); + if (pblock == NULL) + fAlreadyChecked = false; + // Read block from disk. int64_t nTime1 = GetTimeMicros(); CBlock block; @@ -2451,8 +2435,8 @@ bool static ConnectTip(CValidationState& state, CBlockIndex* pindexNew, CBlock* LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001); { CInv inv(MSG_BLOCK, pindexNew->GetBlockHash()); - bool rv = ConnectBlock(*pblock, state, pindexNew, view); - g_signals.BlockChecked(*pblock, state); + bool rv = ConnectBlock(*pblock, state, pindexNew, view, false, fAlreadyChecked); + GetMainSignals().BlockChecked(*pblock, state); if (!rv) { if (state.IsInvalid()) InvalidBlockFound(pindexNew, state); @@ -2486,11 +2470,11 @@ bool static ConnectTip(CValidationState& state, CBlockIndex* pindexNew, CBlock* UpdateTip(pindexNew); // Tell wallet about transactions that went from mempool // to conflicted: - BOOST_FOREACH (const CTransaction& tx, txConflicted) { + for (const CTransaction& tx : txConflicted) { SyncWithWallets(tx, NULL); } // ... and about transactions that got confirmed: - BOOST_FOREACH (const CTransaction& tx, pblock->vtx) { + for (const CTransaction& tx : pblock->vtx) { SyncWithWallets(tx, pblock); } @@ -2552,10 +2536,10 @@ bool DisconnectBlockAndInputs(CValidationState& state, CTransaction txLock) // Queue memory transactions to resurrect. // We only do this for blocks after the last checkpoint (reorganisation before that // point should only happen with -reindex/-loadblock, or a misbehaving peer. - BOOST_FOREACH (const CTransaction& tx, block.vtx) { + for (const CTransaction& tx : block.vtx) { if (!tx.IsCoinBase()) { - BOOST_FOREACH (const CTxIn& in1, txLock.vin) { - BOOST_FOREACH (const CTxIn& in2, tx.vin) { + for (const CTxIn& in1 : txLock.vin) { + for (const CTxIn& in2 : tx.vin) { if (in1.prevout == in2.prevout) foundConflictingTx = true; } } @@ -2576,7 +2560,7 @@ bool DisconnectBlockAndInputs(CValidationState& state, CTransaction txLock) if (vDisconnect.size() > 0) { LogPrintf("REORGANIZE: Disconnect Conflicting Blocks %lli blocks; %s..\n", vDisconnect.size(), pindexNew->GetBlockHash().ToString()); - BOOST_FOREACH (CBlockIndex* pindex, vDisconnect) { + for (CBlockIndex* pindex : vDisconnect) { LogPrintf(" -- disconnect %s\n", pindex->GetBlockHash().ToString()); DisconnectTip(state); } @@ -2662,9 +2646,11 @@ static void PruneBlockIndexCandidates() * Try to make some progress towards making pindexMostWork the active block. * pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork. */ -static bool ActivateBestChainStep(CValidationState& state, CBlockIndex* pindexMostWork, CBlock* pblock) +static bool ActivateBestChainStep(CValidationState& state, CBlockIndex* pindexMostWork, CBlock* pblock, bool fAlreadyChecked) { AssertLockHeld(cs_main); + if (pblock == NULL) + fAlreadyChecked = false; bool fInvalidFound = false; const CBlockIndex* pindexOldTip = chainActive.Tip(); const CBlockIndex* pindexFork = chainActive.FindFork(pindexMostWork); @@ -2694,7 +2680,7 @@ static bool ActivateBestChainStep(CValidationState& state, CBlockIndex* pindexMo // Connect new blocks. BOOST_REVERSE_FOREACH (CBlockIndex* pindexConnect, vpindexToConnect) { - if (!ConnectTip(state, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL)) { + if (!ConnectTip(state, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL, fAlreadyChecked)) { if (state.IsInvalid()) { // The block violates a consensus rule. if (!state.CorruptionPossible()) @@ -2732,7 +2718,7 @@ static bool ActivateBestChainStep(CValidationState& state, CBlockIndex* pindexMo * or an activated best chain. pblock is either NULL or a pointer to a block * that is already loaded (to avoid loading it again from disk). */ -bool ActivateBestChain(CValidationState& state, CBlock* pblock) +bool ActivateBestChain(CValidationState& state, CBlock* pblock, bool fAlreadyChecked) { CBlockIndex* pindexNewTip = NULL; CBlockIndex* pindexMostWork = NULL; @@ -2753,7 +2739,7 @@ bool ActivateBestChain(CValidationState& state, CBlock* pblock) if (pindexMostWork == NULL || pindexMostWork == chainActive.Tip()) return true; - if (!ActivateBestChainStep(state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL)) + if (!ActivateBestChainStep(state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL, fAlreadyChecked)) return false; pindexNewTip = chainActive.Tip(); @@ -2769,12 +2755,22 @@ bool ActivateBestChain(CValidationState& state, CBlock* pblock) int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); { LOCK(cs_vNodes); - BOOST_FOREACH (CNode* pnode, vNodes) + for (CNode* pnode : vNodes) if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip)); } // Notify external listeners about the new tip. + // Note: uiInterface, should switch main signals. uiInterface.NotifyBlockTip(hashNewTip); + GetMainSignals().UpdatedBlockTip(pindexNewTip); + + unsigned size = 0; + if (pblock) + size = GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION); + // If the size is over 1 MB notify external listeners, and it is within the last 5 minutes + if (size > MAX_BLOCK_SIZE && pblock->GetBlockTime() > GetAdjustedTime() - 300) { + uiInterface.NotifyBlockSize(static_cast(size), hashNewTip); + } } } while (pindexMostWork != chainActive.Tip()); CheckBlockIndex(); @@ -2909,7 +2905,7 @@ CBlockIndex* AddToBlockIndex(const CBlock& block) pindexNew->SetStakeModifier(nStakeModifier, fGeneratedStakeModifier); pindexNew->nStakeModifierChecksum = GetStakeModifierChecksum(pindexNew); if (!CheckStakeModifierCheckpoints(pindexNew->nHeight, pindexNew->nStakeModifierChecksum)) - LogPrintf("AddToBlockIndex() : Rejected by stake modifier checkpoint height=%d, modifier=%s \n", pindexNew->nHeight, boost::lexical_cast(nStakeModifier)); + LogPrintf("AddToBlockIndex() : Rejected by stake modifier checkpoint height=%d, modifier=%s \n", pindexNew->nHeight, std::to_string(nStakeModifier)); } pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + GetBlockProof(*pindexNew); pindexNew->RaiseValidity(BLOCK_VALID_TREE); @@ -3097,7 +3093,8 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo // because we receive the wrong transactions for it. // Size limits - if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) + unsigned int nMaxBlockSize = MAX_BLOCK_SIZE; + if (block.vtx.empty() || block.vtx.size() > nMaxBlockSize || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > nMaxBlockSize) return state.DoS(100, error("CheckBlock() : size limits failed"), REJECT_INVALID, "bad-blk-length"); @@ -3169,31 +3166,10 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo } } - // ----------- swiftTX transaction scanning ----------- - if (IsSporkActive(SPORK_2_SWIFTTX_BLOCK_FILTERING)) { - BOOST_FOREACH (const CTransaction& tx, block.vtx) { - if (!tx.IsCoinBase()) { - //only reject blocks when it's based on complete consensus - BOOST_FOREACH (const CTxIn& in, tx.vin) { - if (mapLockedInputs.count(in.prevout)) { - if (mapLockedInputs[in.prevout] != tx.GetHash()) { - mapRejectedBlocks.insert(make_pair(block.GetHash(), GetTime())); - LogPrintf("CheckBlock() : found conflicting transaction with transaction lock %s %s\n", mapLockedInputs[in.prevout].ToString(), tx.GetHash().ToString()); - return state.DoS(0, error("CheckBlock() : found conflicting transaction with transaction lock"), - REJECT_INVALID, "conflicting-tx-ix"); - } - } - } - } - } - } else { - LogPrintf("CheckBlock() : skipping transaction locking checks\n"); - } - // masternode payments CBlockIndex* pindexPrev = chainActive.Tip(); + int nHeight = 0; if (pindexPrev != NULL) { - int nHeight = 0; if (pindexPrev->GetBlockHash() == block.hashPrevBlock) { nHeight = pindexPrev->nHeight + 1; } else { //out of order @@ -3221,14 +3197,16 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo } // Check transactions - BOOST_FOREACH (const CTransaction& tx, block.vtx) + for (const CTransaction& tx : block.vtx) if (!CheckTransaction(tx, state)) return error("CheckBlock() : CheckTransaction failed"); unsigned int nSigOps = 0; - BOOST_FOREACH (const CTransaction& tx, block.vtx) { + for (const CTransaction& tx : block.vtx) { nSigOps += GetLegacySigOpCount(tx); } + + unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE / 50; if (nSigOps > MAX_BLOCK_SIGOPS) return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"), REJECT_INVALID, "bad-blk-sigops", true); @@ -3250,11 +3228,11 @@ bool CheckWork(const CBlock block, CBlockIndex* const pindexPrev) uint256 hashProofOfStake; uint256 hash = block.GetHash(); - if (!CheckProofOfStake(block, hashProofOfStake)) { + if(!CheckProofOfStake(block, hashProofOfStake)) { LogPrintf("WARNING: ProcessBlock(): check proof-of-stake failed for block %s\n", hash.ToString().c_str()); return false; } - if (!mapProofOfStake.count(hash)) // add to mapProofOfStake + if(!mapProofOfStake.count(hash)) // add to mapProofOfStake mapProofOfStake.insert(make_pair(hash, hashProofOfStake)); } @@ -3273,8 +3251,9 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta int nHeight = pindexPrev->nHeight + 1; //If this is a reorg, check that it is not too deep - if (chainActive.Height() - nHeight >= Params().MaxReorganizationDepth()) - return state.DoS(1, error("%s: forked chain older than max reorganization depth (height %d)", __func__, nHeight)); + int nMaxReorgDepth = GetArg("-maxreorg", Params().MaxReorganizationDepth()); + if (chainActive.Height() - nHeight >= nMaxReorgDepth) + return state.DoS(1, error("%s: forked chain older than max reorganization depth (height %d)", __func__, chainActive.Height() - nHeight)); // Check timestamp against prev if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) { @@ -3315,11 +3294,11 @@ bool IsBlockHashInChain(const uint256& hashBlock) return chainActive.Contains(mapBlockIndex[hashBlock]); } -bool IsTransactionInChain(uint256 txId, int& nHeightTx) +bool IsTransactionInChain(const uint256& txId, int& nHeightTx, CTransaction& tx) { uint256 hashBlock; - CTransaction tx; - GetTransaction(txId, tx, hashBlock, true); + if (!GetTransaction(txId, tx, hashBlock, true)) + return false; if (!IsBlockHashInChain(hashBlock)) return false; @@ -3327,6 +3306,12 @@ bool IsTransactionInChain(uint256 txId, int& nHeightTx) return true; } +bool IsTransactionInChain(const uint256& txId, int& nHeightTx) +{ + CTransaction tx; + return IsTransactionInChain(txId, nHeightTx, tx); +} + bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex* const pindexPrev) { const int nHeight = !pindexPrev ? 0 : pindexPrev->nHeight + 1; @@ -3417,7 +3402,7 @@ bool AcceptBlockHeader(const CBlock& block, CValidationState& state, CBlockIndex return true; } -bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, CDiskBlockPos* dbp) +bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, CDiskBlockPos* dbp, bool fAlreadyCheckedBlock) { AssertLockHeld(cs_main); @@ -3449,16 +3434,26 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, if (block.GetHash() != Params().HashGenesisBlock() && !CheckWork(block, pindexPrev)) return false; + bool isPoS = false; + if (block.IsProofOfStake()) { + isPoS = true; + uint256 hashProofOfStake = 0; + uint256 hash = block.GetHash(); + if(!mapProofOfStake.count(hash)) // add to mapProofOfStake + mapProofOfStake.insert(make_pair(hash, hashProofOfStake)); + } + if (!AcceptBlockHeader(block, state, &pindex)) return false; if (pindex->nStatus & BLOCK_HAVE_DATA) { // TODO: deal better with duplicate blocks. // return state.DoS(20, error("AcceptBlock() : already have block %d %s", pindex->nHeight, pindex->GetBlockHash().ToString()), REJECT_DUPLICATE, "duplicate"); + LogPrintf("AcceptBlock() : already have block %d %s", pindex->nHeight, pindex->GetBlockHash().ToString()); return true; } - if ((!CheckBlock(block, state)) || !ContextualCheckBlock(block, state, pindex->pprev)) { + if ((!fAlreadyCheckedBlock && !CheckBlock(block, state)) || !ContextualCheckBlock(block, state, pindex->pprev)) { if (state.IsInvalid() && !state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); @@ -3467,6 +3462,111 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, } int nHeight = pindex->nHeight; + int splitHeight = -1; + + if (isPoS) { + LOCK(cs_main); + + CCoinsViewCache coins(pcoinsTip); + + if (!coins.HaveInputs(block.vtx[1])) { + + LOCK(cs_mapstake); + + // the inputs are spent at the chain tip so we should look at the recently spent outputs + for (CTxIn in : block.vtx[1].vin) { + auto it = mapStakeSpent.find(in.prevout); + if (it == mapStakeSpent.end()) { + return false; + } + if (it->second < pindexPrev->nHeight) { + return false; + } + } + } + + // Blocks arrives in order, so if prev block is not the tip then we are on a fork. + // Extra info: duplicated blocks are skipping this checks, so we don't have to worry about those here. + bool isBlockFromFork = pindexPrev != nullptr && chainActive.Tip() != pindexPrev; + + // Coin stake + CTransaction &stakeTxIn = block.vtx[1]; + + // Inputs + std::vector lnoInputs; + + for (const CTxIn& stakeIn : stakeTxIn.vin) { + lnoInputs.push_back(stakeIn); + } + const bool hasLNOInputs = !lnoInputs.empty(); + // Check for serial double spent on the same block, TODO: Move this to the proper method.. + + for (const CTransaction& tx : block.vtx) { + for (const CTxIn& in: tx.vin) { + + if(tx.IsCoinStake()) continue; + if(hasLNOInputs) + // Check if coinstake input is double spent inside the same block + for (const CTxIn& pivIn : lnoInputs){ + if(pivIn.prevout == in.prevout){ + // double spent coinstake input inside block + return error("%s: double spent coinstake input inside block", __func__); + } + } + } + } + + // Check whether is a fork or not + if (isBlockFromFork) { + + // Start at the block we're adding on to + CBlockIndex *prev = pindexPrev; + + CBlock bl; + if (!ReadBlockFromDisk(bl, prev)) + return error("%s: previous block %s not on disk", __func__, prev->GetBlockHash().GetHex()); + + int readBlock = 0; + // Go backwards on the forked chain up to the split + while (!chainActive.Contains(prev)) { + + // Increase amount of read blocks + readBlock++; + // Check if the forked chain is longer than the max reorg limit + if (readBlock == Params().MaxReorganizationDepth()) { + // TODO: Remove this chain from disk. + return error("%s: forked chain longer than maximum reorg limit", __func__); + } + + // Loop through every input from said block + for (const CTransaction &t : bl.vtx) { + for (const CTxIn &in: t.vin) { + // Loop through every input of the staking tx + for (const CTxIn &stakeIn : lnoInputs) { + // if it's already spent + + // First regular staking check + if (hasLNOInputs) { + if (stakeIn.prevout == in.prevout) { + return state.DoS(100, error("%s: input already spent on a previous block", + __func__)); + } + + } + } + } + } + + // Prev block + prev = prev->pprev; + if (!ReadBlockFromDisk(bl, prev)) + // Previous block not on disk + return error("%s: previous block %s not on disk", __func__, prev->GetBlockHash().GetHex()); + + } + } + + } // Write block to history file try { @@ -3555,13 +3655,6 @@ bool ProcessNewBlock(CValidationState& state, CNode* pfrom, CBlock* pblock, CDis int64_t nStartTime = GetTimeMillis(); bool checked = CheckBlock(*pblock, state); - // ppcoin: check proof-of-stake - // Limited duplicity on stake: prevents block flood attack - // Duplicate stake allowed only when there is orphan child block - //if (pblock->IsProofOfStake() && setStakeSeen.count(pblock->GetProofOfStake())/* && !mapOrphanBlocksByPrev.count(hash)*/) - // return error("ProcessNewBlock() : duplicate proof-of-stake (%s, %d) for block %s", pblock->GetProofOfStake().first.ToString().c_str(), pblock->GetProofOfStake().second, pblock->GetHash().ToString().c_str()); - - // NovaCoin: check proof-of-stake block signature if (!pblock->CheckBlockSignature()) return error("ProcessNewBlock() : bad proof-of-stake block signature"); @@ -3575,25 +3668,44 @@ bool ProcessNewBlock(CValidationState& state, CNode* pfrom, CBlock* pblock, CDis } { - LOCK(cs_main); // Replaces the former TRY_LOCK loop because busy waiting wastes too much resources + LOCK(cs_main); // Replaces the former TRY_LOCK loop because busy waiting wastes too much resources - MarkBlockAsReceived(pblock->GetHash()); + MarkBlockAsReceived (pblock->GetHash ()); if (!checked) { - return error("%s : CheckBlock FAILED for block %s", __func__, pblock->GetHash().GetHex()); + return error ("%s : CheckBlock FAILED for block %s", __func__, pblock->GetHash().GetHex()); } // Store to disk - CBlockIndex* pindex = NULL; - bool ret = AcceptBlock(*pblock, state, &pindex, dbp); + CBlockIndex* pindex = nullptr; + bool ret = AcceptBlock (*pblock, state, &pindex, dbp, checked); if (pindex && pfrom) { - mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId(); - } - CheckBlockIndex(); - if (!ret) + mapBlockSource[pindex->GetBlockHash ()] = pfrom->GetId (); + } + CheckBlockIndex (); + if (!ret) { + // Check spamming + if(pindex && pfrom && GetBoolArg("-blockspamfilter", DEFAULT_BLOCK_SPAM_FILTER)) { + CNodeState *nodestate = State(pfrom->GetId()); + if(nodestate != nullptr) { + nodestate->nodeBlocks.onBlockReceived(pindex->nHeight); + bool nodeStatus = true; + // UpdateState will return false if the node is attacking us or update the score and return true. + nodeStatus = nodestate->nodeBlocks.updateState(state, nodeStatus); + int nDoS = 0; + if (state.IsInvalid(nDoS)) { + if (nDoS > 0) + Misbehaving(pfrom->GetId(), nDoS); + nodeStatus = false; + } + if (!nodeStatus) + return error("%s : AcceptBlock FAILED - block spam protection", __func__); + } + } return error("%s : AcceptBlock FAILED", __func__); + } } - if (!ActivateBestChain(state, pblock)) + if (!ActivateBestChain(state, pblock, checked)) return error("%s : ActivateBestChain failed", __func__); if (!fLiteMode) { @@ -3613,8 +3725,8 @@ bool ProcessNewBlock(CValidationState& state, CNode* pfrom, CBlock* pblock, CDis pwalletMain->AutoCombineDust(); } - LogPrintf("%s : ACCEPTED in %ld milliseconds with size=%d\n", __func__, GetTimeMillis() - nStartTime, - pblock->GetSerializeSize(SER_DISK, CLIENT_VERSION)); + LogPrintf("%s : ACCEPTED Block %ld in %ld milliseconds with size=%d\n", __func__, GetHeight(), GetTimeMillis() - nStartTime, + pblock->GetSerializeSize(SER_DISK, CLIENT_VERSION)); return true; } @@ -3622,7 +3734,11 @@ bool ProcessNewBlock(CValidationState& state, CNode* pfrom, CBlock* pblock, CDis bool TestBlockValidity(CValidationState& state, const CBlock& block, CBlockIndex* const pindexPrev, bool fCheckPOW, bool fCheckMerkleRoot) { AssertLockHeld(cs_main); - assert(pindexPrev == chainActive.Tip()); + assert(pindexPrev); + if (pindexPrev != chainActive.Tip()) { + LogPrintf("%s : No longer working on chain tip\n", __func__); + return false; + } CCoinsViewCache viewNew(pcoinsTip); CBlockIndex indexDummy(block); @@ -3733,7 +3849,7 @@ CBlockIndex* InsertBlockIndex(uint256 hash) return pindexNew; } -bool static LoadBlockIndexDB() +bool static LoadBlockIndexDB(string& strError) { if (!pblocktree->LoadBlockIndexGuts()) return false; @@ -3743,12 +3859,12 @@ bool static LoadBlockIndexDB() // Calculate nChainWork vector > vSortedByHeight; vSortedByHeight.reserve(mapBlockIndex.size()); - for (const PAIRTYPE(uint256, CBlockIndex*) & item : mapBlockIndex) { + for (const std::pair& item : mapBlockIndex) { CBlockIndex* pindex = item.second; vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex)); } sort(vSortedByHeight.begin(), vSortedByHeight.end()); - BOOST_FOREACH (const PAIRTYPE(int, CBlockIndex*) & item, vSortedByHeight) { + for (const PAIRTYPE(int, CBlockIndex*) & item : vSortedByHeight) { CBlockIndex* pindex = item.second; pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex); if (pindex->nStatus & BLOCK_HAVE_DATA) { @@ -3793,7 +3909,7 @@ bool static LoadBlockIndexDB() // Check presence of blk files LogPrintf("Checking all blk files are present...\n"); set setBlkDataFiles; - for (const PAIRTYPE(uint256, CBlockIndex*) & item : mapBlockIndex) { + for (const std::pair& item : mapBlockIndex) { CBlockIndex* pindex = item.second; if (pindex->nStatus & BLOCK_HAVE_DATA) { setBlkDataFiles.insert(pindex->nFile); @@ -3812,83 +3928,69 @@ bool static LoadBlockIndexDB() LogPrintf("%s: Last shutdown was prepared: %s\n", __func__, fLastShutdownWasPrepared); //Check for inconsistency with block file info and internal state - if (!fLastShutdownWasPrepared && !GetBoolArg("-forcestart", false) && !GetBoolArg("-reindex", false) && (vSortedByHeight.size() != vinfoBlockFile[nLastBlockFile].nHeightLast + 1) && (vinfoBlockFile[nLastBlockFile].nHeightLast != 0)) { - //The database is in a state where a block has been accepted and written to disk, but not - //all of the block has perculated through the code. The block and the index should both be - //intact (although assertions are added if they are not), and the block will be reprocessed - //to ensure all data will be accounted for. - LogPrintf("%s: Inconsistent State Detected mapBlockIndex.size()=%d blockFileBlocks=%d\n", __func__, vSortedByHeight.size(), vinfoBlockFile[nLastBlockFile].nHeightLast + 1); - LogPrintf("%s: lastIndexPos=%d blockFileSize=%d\n", __func__, vSortedByHeight[vSortedByHeight.size() - 1].second->GetBlockPos().nPos, - vinfoBlockFile[nLastBlockFile].nSize); - - //try reading the block from the last index we have - bool isFixed = true; - string strError = ""; - LogPrintf("%s: Attempting to re-add last block that was recorded to disk\n", __func__); - - //get the last block that was properly recorded to the block info file - CBlockIndex* pindexLastMeta = vSortedByHeight[vinfoBlockFile[nLastBlockFile].nHeightLast + 1].second; - - //fix Assertion `hashPrevBlock == view.GetBestBlock()' failed. By adjusting height to the last recorded by coinsview - CBlockIndex* pindexCoinsView = mapBlockIndex[pcoinsTip->GetBestBlock()]; - for(unsigned int i = vinfoBlockFile[nLastBlockFile].nHeightLast + 1; i < vSortedByHeight.size(); i++) - { - pindexLastMeta = vSortedByHeight[i].second; - if(pindexLastMeta->nHeight > pindexCoinsView->nHeight) - break; - } - - LogPrintf("%s: Last block properly recorded: #%d %s\n", __func__, pindexLastMeta->nHeight, pindexLastMeta->GetBlockHash().ToString().c_str()); - - CBlock lastMetaBlock; - if (!ReadBlockFromDisk(lastMetaBlock, pindexLastMeta)) { - isFixed = false; - strError = strprintf("failed to read block %d from disk", pindexLastMeta->nHeight); - } + if (!fLastShutdownWasPrepared && !GetBoolArg("-forcestart", false) && !GetBoolArg("-reindex", false)) { + unsigned int nHeightLastBlockFile = vinfoBlockFile[nLastBlockFile].nHeightLast + 1; + if (vSortedByHeight.size() > nHeightLastBlockFile && pcoinsTip->GetBestBlock() != vSortedByHeight[nHeightLastBlockFile].second->GetBlockHash()) { + //The database is in a state where a block has been accepted and written to disk, but the + //transaction database (pcoinsTip) was not flushed to disk, and is therefore not in sync with + //the block index database. + + if (!mapBlockIndex.count(pcoinsTip->GetBestBlock())) { + strError = "The wallet has been not been closed gracefully, causing the transaction database to be out of sync with the block database"; + return false; + } + LogPrintf("%s : pcoinstip synced to block height %d, block index height %d\n", __func__, + mapBlockIndex[pcoinsTip->GetBestBlock()]->nHeight, vSortedByHeight.size()); + + //get the index associated with the point in the chain that pcoinsTip is synced to + CBlockIndex* pindexLastMeta = vSortedByHeight[vinfoBlockFile[nLastBlockFile].nHeightLast + 1].second; + CBlockIndex* pindex = vSortedByHeight[0].second; + unsigned int nSortedPos = 0; + for (unsigned int i = 0; i < vSortedByHeight.size(); i++) { + nSortedPos = i; + if (vSortedByHeight[i].first == mapBlockIndex[pcoinsTip->GetBestBlock()]->nHeight + 1) { + pindex = vSortedByHeight[i].second; + break; + } + } - //set the chain to the block before lastMeta so that the meta block will be seen as new - chainActive.SetTip(pindexLastMeta->pprev); + // Start at the last block that was successfully added to the txdb (pcoinsTip) and manually add all transactions that occurred for each block up until + // the best known block from the block index db. + CCoinsViewCache view(pcoinsTip); + while (nSortedPos < vSortedByHeight.size()) { + CBlock block; + if (!ReadBlockFromDisk(block, pindex)) { + strError = "The wallet has been not been closed gracefully and has caused corruption of blocks stored to disk. Data directory is in an unusable state"; + return false; + } - //Process the lastMetaBlock again, using the known location on disk - CDiskBlockPos blockPos = pindexLastMeta->GetBlockPos(); - CValidationState state; - ProcessNewBlock(state, NULL, &lastMetaBlock, &blockPos); + vector vtxundo; + vtxundo.reserve(block.vtx.size() - 1); + uint256 hashBlock = block.GetHash(); + for (unsigned int i = 0; i < block.vtx.size(); i++) { + CValidationState state; + CTxUndo undoDummy; + if (i > 0) + vtxundo.push_back(CTxUndo()); + UpdateCoins(block.vtx[i], state, view, i == 0 ? undoDummy : vtxundo.back(), pindex->nHeight); + view.SetBestBlock(hashBlock); + } - //ensure that everything is as it should be - if (pcoinsTip->GetBestBlock() != vSortedByHeight[vSortedByHeight.size() - 1].second->GetBlockHash()) { - isFixed = false; - strError = "pcoinsTip best block is not correct"; - } + if (pindex->nHeight >= pindexLastMeta->nHeight) + break; - //properly account for all of the blocks that were not in the meta data. If this is not done the file - //positioning will be wrong and blocks will be overwritten and later cause serialization errors - CBlockIndex *pindexLast = vSortedByHeight[vSortedByHeight.size() - 1].second; - CBlock lastBlock; - if (!ReadBlockFromDisk(lastBlock, pindexLast)) { - isFixed = false; - strError = strprintf("failed to read block %d from disk", pindexLast->nHeight); - } - vinfoBlockFile[nLastBlockFile].nHeightLast = pindexLast->nHeight; - vinfoBlockFile[nLastBlockFile].nSize = pindexLast->GetBlockPos().nPos + ::GetSerializeSize(lastBlock, SER_DISK, CLIENT_VERSION);; - setDirtyFileInfo.insert(nLastBlockFile); - FlushStateToDisk(state, FLUSH_STATE_ALWAYS); + pindex = vSortedByHeight[++nSortedPos].second; + } - //Print out file info again - pblocktree->ReadLastBlockFile(nLastBlockFile); - vinfoBlockFile.resize(nLastBlockFile + 1); - LogPrintf("%s: last block file = %i\n", __func__, nLastBlockFile); - for (int nFile = 0; nFile <= nLastBlockFile; nFile++) { - pblocktree->ReadBlockFileInfo(nFile, vinfoBlockFile[nFile]); - } - LogPrintf("%s: last block file info: %s\n", __func__, vinfoBlockFile[nLastBlockFile].ToString()); + // Save the updates to disk + if (!view.Flush() || !pcoinsTip->Flush()) + LogPrintf("%s : failed to flush view\n", __func__); - if (!isFixed) { - strError = "Failed reading from database. " + strError + ". The block database is in an inconsistent state and may cause issues in the future." - "To force start use -forcestart"; - uiInterface.ThreadSafeMessageBox(strError, "", CClientUIInterface::MSG_ERROR); - abort(); + LogPrintf("%s: Last block properly recorded: #%d %s\n", __func__, pindexLastMeta->nHeight, + pindexLastMeta->GetBlockHash().ToString().c_str()); + LogPrintf("%s : pcoinstip=%d %s\n", __func__, mapBlockIndex[pcoinsTip->GetBestBlock()]->nHeight, + pcoinsTip->GetBestBlock().GetHex()); } - LogPrintf("Passed corruption fix\n"); } // Check whether we need to continue reindexing @@ -3996,7 +4098,7 @@ bool CVerifyDB::VerifyDB(CCoinsView* coinsview, int nCheckLevel, int nCheckDepth CBlock block; if (!ReadBlockFromDisk(block, pindex)) return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); - if (!ConnectBlock(block, state, pindex, coins)) + if (!ConnectBlock(block, state, pindex, coins, false)) return error("VerifyDB() : *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); } } @@ -4008,16 +4110,37 @@ bool CVerifyDB::VerifyDB(CCoinsView* coinsview, int nCheckLevel, int nCheckDepth void UnloadBlockIndex() { - mapBlockIndex.clear(); + LOCK(cs_main); setBlockIndexCandidates.clear(); chainActive.SetTip(NULL); pindexBestInvalid = NULL; + pindexBestHeader = NULL; + mempool.clear(); + mapOrphanTransactions.clear(); + mapOrphanTransactionsByPrev.clear(); + nSyncStarted = 0; + mapBlocksUnlinked.clear(); + vinfoBlockFile.clear(); + nLastBlockFile = 0; + nBlockSequenceId = 1; + mapBlockSource.clear(); + mapBlocksInFlight.clear(); + nQueuedValidatedHeaders = 0; + nPreferredDownload = 0; + setDirtyBlockIndex.clear(); + setDirtyFileInfo.clear(); + mapNodeState.clear(); + + for (BlockMap::value_type& entry : mapBlockIndex) { + delete entry.second; + } + mapBlockIndex.clear(); } -bool LoadBlockIndex() +bool LoadBlockIndex(string& strError) { // Load block index from databases - if (!fReindex && !LoadBlockIndexDB()) + if (!fReindex && !LoadBlockIndexDB(strError)) return false; return true; } @@ -4072,7 +4195,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos* dbp) int nLoaded = 0; try { // This takes over fileIn and calls fclose() on it in the CBufferedFile destructor - CBufferedFile blkdat(fileIn, 2 * MAX_BLOCK_SIZE, MAX_BLOCK_SIZE + 8, SER_DISK, CLIENT_VERSION); + CBufferedFile blkdat(fileIn, 2 * MAX_BLOCKFILE_SIZE, MAX_BLOCKFILE_SIZE + 8, SER_DISK, CLIENT_VERSION); uint64_t nRewind = blkdat.GetPos(); while (!blkdat.eof()) { boost::this_thread::interruption_point(); @@ -4091,7 +4214,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos* dbp) continue; // read size blkdat >> nSize; - if (nSize < 80 || nSize > MAX_BLOCK_SIZE) + if (nSize < 80 || nSize > MAX_BLOCKFILE_SIZE) continue; } catch (const std::exception&) { // no valid block header found; don't complain @@ -4318,7 +4441,7 @@ string GetWarnings(string strFor) string strRPC; if (!CLIENT_VERSION_IS_RELEASE) - strStatusBar = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"); + strStatusBar = _("This is a pre-release test build - use at your own risk - do not use for mining, staking or merchant applications"); if (GetBoolArg("-testsafemode", false)) strStatusBar = strRPC = "testsafemode enabled"; @@ -4340,7 +4463,7 @@ string GetWarnings(string strFor) // Alerts { LOCK(cs_mapAlerts); - BOOST_FOREACH (PAIRTYPE(const uint256, CAlert) & item, mapAlerts) { + for (PAIRTYPE(const uint256, CAlert) & item : mapAlerts) { const CAlert& alert = item.second; if (alert.AppliesToMe() && alert.nPriority > nPriority) { nPriority = alert.nPriority; @@ -4410,26 +4533,21 @@ void static ProcessGetData(CNode* pfrom) vector vNotFound; + LOCK(cs_main); + while (it != pfrom->vRecvGetData.end()) { // Don't bother if send buffer is too full to respond anyway if (pfrom->nSendSize >= SendBufferSize()) break; const CInv& inv = *it; + { + boost::this_thread::interruption_point(); + it++; - boost::this_thread::interruption_point(); - it++; - - if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) { - - CBlock block; - bool send = false; - - { - LOCK(cs_main); - + if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) { + bool send = false; BlockMap::iterator mi = mapBlockIndex.find(inv.hash); - if (mi != mapBlockIndex.end()) { if (chainActive.Contains(mi->second)) { send = true; @@ -4437,166 +4555,158 @@ void static ProcessGetData(CNode* pfrom) // To prevent fingerprinting attacks, only send blocks outside of the active // chain if they are valid, and no more than a max reorg depth than the best header // chain we know about. - send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) - && pindexBestHeader - && (chainActive.Height() - mi->second->nHeight < Params().MaxReorganizationDepth()); - - if(!send) + send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != NULL) && + (chainActive.Height() - mi->second->nHeight < Params().MaxReorganizationDepth()); + if (!send) { LogPrintf("ProcessGetData(): ignoring request from peer=%i for old block that isn't in the main chain\n", pfrom->GetId()); + } + } + } + // Don't send not-validated blocks + if (send && (mi->second->nStatus & BLOCK_HAVE_DATA)) { + // Send block from disk + CBlock block; + if (!ReadBlockFromDisk(block, (*mi).second)) + assert(!"cannot load block from disk"); + if (inv.type == MSG_BLOCK) + pfrom->PushMessage("block", block); + else // MSG_FILTERED_BLOCK) + { + LOCK(pfrom->cs_filter); + if (pfrom->pfilter) { + CMerkleBlock merkleBlock(block, *pfrom->pfilter); + pfrom->PushMessage("merkleblock", merkleBlock); + // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see + // This avoids hurting performance by pointlessly requiring a round-trip + // Note that there is currently no way for a node to request any single transactions we didnt send here - + // they must either disconnect and retry or request the full block. + // Thus, the protocol spec specified allows for us to provide duplicate txn here, + // however we MUST always provide at least what the remote peer needs + typedef std::pair PairType; + for (PairType& pair : merkleBlock.vMatchedTxn) + if (!pfrom->setInventoryKnown.count(CInv(MSG_TX, pair.second))) + pfrom->PushMessage("tx", block.vtx[pair.first]); + } + // else + // no response } - // Don't send not-validated blocks - send = send && (mi->second->nStatus & BLOCK_HAVE_DATA); - - if(send) { - - // Send block from disk - if (!ReadBlockFromDisk(block, mi->second)) - assert(!"cannot load block from disk"); + // Trigger them to send a getblocks request for the next batch of inventory + if (inv.hash == pfrom->hashContinue) { + // Bypass PushInventory, this must send even if redundant, + // and we want it right after the last block so they don't + // wait for other stuff first. + vector vInv; + vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash())); + pfrom->PushMessage("inv", vInv); + pfrom->hashContinue = 0; } } - } - - if(send) { - if (inv.type == MSG_BLOCK) - pfrom->PushMessage("block", block); - else // MSG_FILTERED_BLOCK) + } else if (inv.IsKnownType()) { + // Send stream from relay memory + bool pushed = false; { - LOCK(pfrom->cs_filter); - if (pfrom->pfilter) { - CMerkleBlock merkleBlock(block, *pfrom->pfilter); - pfrom->PushMessage("merkleblock", merkleBlock); - // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see - // This avoids hurting performance by pointlessly requiring a round-trip - // Note that there is currently no way for a node to request any single transactions we didnt send here - - // they must either disconnect and retry or request the full block. - // Thus, the protocol spec specified allows for us to provide duplicate txn here, - // however we MUST always provide at least what the remote peer needs - typedef std::pair PairType; - BOOST_FOREACH (PairType& pair, merkleBlock.vMatchedTxn) - if (!pfrom->setInventoryKnown.count(CInv(MSG_TX, pair.second))) - pfrom->PushMessage("tx", block.vtx[pair.first]); + LOCK(cs_mapRelay); + map::iterator mi = mapRelay.find(inv); + if (mi != mapRelay.end()) { + pfrom->PushMessage(inv.GetCommand(), (*mi).second); + pushed = true; } - // else - // no response - } - - // Trigger them to send a getblocks request for the next batch of inventory - if (inv.hash == pfrom->hashContinue) { - // Bypass PushInventory, this must send even if redundant, - // and we want it right after the last block so they don't - // wait for other stuff first. - vector vInv; - vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash())); - pfrom->PushMessage("inv", vInv); - pfrom->hashContinue = 0; - } - } - } else if (inv.IsKnownType()) { - // Send stream from relay memory - bool pushed = false; - { - LOCK(cs_mapRelay); - map::iterator mi = mapRelay.find(inv); - if (mi != mapRelay.end()) { - pfrom->PushMessage(inv.GetCommand(), (*mi).second); - pushed = true; } - } - if (!pushed && inv.type == MSG_TX) { - CTransaction tx; - if (mempool.lookup(inv.hash, tx)) { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(1000); - ss << tx; - pfrom->PushMessage("tx", ss); - pushed = true; + if (!pushed && inv.type == MSG_TX) { + CTransaction tx; + if (mempool.lookup(inv.hash, tx)) { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << tx; + pfrom->PushMessage("tx", ss); + pushed = true; + } } - } - if (!pushed && inv.type == MSG_TXLOCK_VOTE) { - if (mapTxLockVote.count(inv.hash)) { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(1000); - ss << mapTxLockVote[inv.hash]; - pfrom->PushMessage("txlvote", ss); - pushed = true; + if (!pushed && inv.type == MSG_TXLOCK_VOTE) { + if (mapTxLockVote.count(inv.hash)) { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << mapTxLockVote[inv.hash]; + pfrom->PushMessage("txlvote", ss); + pushed = true; + } } - } - if (!pushed && inv.type == MSG_TXLOCK_REQUEST) { - if (mapTxLockReq.count(inv.hash)) { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(1000); - ss << mapTxLockReq[inv.hash]; - pfrom->PushMessage("ix", ss); - pushed = true; + if (!pushed && inv.type == MSG_TXLOCK_REQUEST) { + if (mapTxLockReq.count(inv.hash)) { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << mapTxLockReq[inv.hash]; + pfrom->PushMessage("ix", ss); + pushed = true; + } } - } - if (!pushed && inv.type == MSG_SPORK) { - if (mapSporks.count(inv.hash)) { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(1000); - ss << mapSporks[inv.hash]; - pfrom->PushMessage("spork", ss); - pushed = true; + if (!pushed && inv.type == MSG_SPORK) { + if (mapSporks.count(inv.hash)) { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << mapSporks[inv.hash]; + pfrom->PushMessage("spork", ss); + pushed = true; + } } - } - - if (!pushed && inv.type == MSG_MASTERNODE_WINNER) { + if (!pushed && inv.type == MSG_MASTERNODE_WINNER) { auto mnw = masternodePayments.mapMasternodePayeeVotes.find(inv.hash); if(mnw != masternodePayments.mapMasternodePayeeVotes.cend()) { CDataStream ss{SER_NETWORK, PROTOCOL_VERSION}; - ss.reserve(1000); + ss.reserve(1000); ss << mnw->second; pfrom->PushMessage("mnw", ss); - pushed = true; + pushed = true; + } } - } - if (!pushed && inv.type == MSG_MASTERNODE_ANNOUNCE) { - if (mnodeman.mapSeenMasternodeBroadcast.count(inv.hash)) { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(1000); - ss << mnodeman.mapSeenMasternodeBroadcast[inv.hash]; - pfrom->PushMessage("mnb", ss); - pushed = true; + if (!pushed && inv.type == MSG_MASTERNODE_ANNOUNCE) { + if (mnodeman.mapSeenMasternodeBroadcast.count(inv.hash)) { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << mnodeman.mapSeenMasternodeBroadcast[inv.hash]; + pfrom->PushMessage("mnb", ss); + pushed = true; + } } - } - if (!pushed && inv.type == MSG_MASTERNODE_PING) { - if (mnodeman.mapSeenMasternodePing.count(inv.hash)) { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(1000); - ss << mnodeman.mapSeenMasternodePing[inv.hash]; - pfrom->PushMessage("mnp", ss); - pushed = true; + if (!pushed && inv.type == MSG_MASTERNODE_PING) { + if (mnodeman.mapSeenMasternodePing.count(inv.hash)) { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << mnodeman.mapSeenMasternodePing[inv.hash]; + pfrom->PushMessage("mnp", ss); + pushed = true; + } } - } - if (!pushed && inv.type == MSG_DSTX) { - if (mapObfuscationBroadcastTxes.count(inv.hash)) { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(1000); - ss << mapObfuscationBroadcastTxes[inv.hash].tx << mapObfuscationBroadcastTxes[inv.hash].vin << mapObfuscationBroadcastTxes[inv.hash].vchSig << mapObfuscationBroadcastTxes[inv.hash].sigTime; + if (!pushed && inv.type == MSG_DSTX) { + if (mapObfuscationBroadcastTxes.count(inv.hash)) { + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + ss.reserve(1000); + ss << mapObfuscationBroadcastTxes[inv.hash].tx << mapObfuscationBroadcastTxes[inv.hash].vin << mapObfuscationBroadcastTxes[inv.hash].vchSig << mapObfuscationBroadcastTxes[inv.hash].sigTime; - pfrom->PushMessage("dstx", ss); - pushed = true; + pfrom->PushMessage("dstx", ss); + pushed = true; + } } - } - if (!pushed) { - vNotFound.push_back(inv); + if (!pushed) { + vNotFound.push_back(inv); + } } - } - // Track requests for our stuff. - g_signals.Inventory(inv.hash); + // Track requests for our stuff. + GetMainSignals().Inventory(inv.hash); - if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) - break; + if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) + break; + } } pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it); @@ -4613,6 +4723,7 @@ void static ProcessGetData(CNode* pfrom) } } +bool fRequestedSporksIDB = false; bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived) { RandAddSeedPerfmon(); @@ -4627,10 +4738,17 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Each connection can only send one version message if (pfrom->nVersion != 0) { pfrom->PushMessage("reject", strCommand, REJECT_DUPLICATE, string("Duplicate version message")); + LOCK(cs_main); Misbehaving(pfrom->GetId(), 1); return false; } + if (!fRequestedSporksIDB) { + LogPrintf("asking peer for sporks\n"); + pfrom->PushMessage("getsporks"); + fRequestedSporksIDB = true; + } + int64_t nTime; CAddress addrMe; CAddress addrFrom; @@ -4684,9 +4802,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (fListen && !IsInitialBlockDownload()) { CAddress addr = GetLocalAddress(&pfrom->addr); if (addr.IsRoutable()) { + LogPrintf("ProcessMessages: advertizing address %s\n", addr.ToString()); pfrom->PushAddress(addr); } else if (IsPeerAddrLocalGood(pfrom)) { addr.SetIP(pfrom->addrLocal); + LogPrintf("ProcessMessages: advertizing address %s\n", addr.ToString()); pfrom->PushAddress(addr); } } @@ -4707,7 +4827,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Relay alerts { LOCK(cs_mapAlerts); - BOOST_FOREACH (PAIRTYPE(const uint256, CAlert) & item, mapAlerts) + for (PAIRTYPE(const uint256, CAlert) & item : mapAlerts) item.second.RelayTo(pfrom); } @@ -4722,17 +4842,25 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->nStartingHeight, addrMe.ToString(), pfrom->id, remoteAddr); - AddTimeData(pfrom->addr, nTime); + int64_t nTimeOffset = nTime - GetTime(); + pfrom->nTimeOffset = nTimeOffset; + AddTimeData(pfrom->addr, nTimeOffset); } - else if (pfrom->nVersion == 0) { // Must have a version message before anything else + LOCK(cs_main); Misbehaving(pfrom->GetId(), 1); return false; } + else if (pfrom->DisconnectOldProtocol(ActiveProtocol(), strCommand)) { + // Instantly disconnect old protocol + return false; + } + + else if (strCommand == "verack") { pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); @@ -4743,7 +4871,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } - else if (strCommand == "addr") { vector vAddr; vRecv >> vAddr; @@ -4752,6 +4879,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pfrom->nVersion < CADDR_TIME_VERSION && addrman.size() > 1000) return true; if (vAddr.size() > 1000) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 20); return error("message addr size() = %u", vAddr.size()); } @@ -4760,7 +4888,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vector vAddrOk; int64_t nNow = GetAdjustedTime(); int64_t nSince = nNow - 10 * 60; - BOOST_FOREACH (CAddress& addr, vAddr) { + for (CAddress& addr : vAddr) { boost::this_thread::interruption_point(); if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) @@ -4780,7 +4908,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, uint256 hashRand = hashSalt ^ (hashAddr << 32) ^ ((GetTime() + hashAddr) / (24 * 60 * 60)); hashRand = Hash(BEGIN(hashRand), END(hashRand)); multimap mapMix; - BOOST_FOREACH (CNode* pnode, vNodes) { + for (CNode* pnode : vNodes) { if (pnode->nVersion < CADDR_TIME_VERSION) continue; unsigned int nPointer; @@ -4807,22 +4935,23 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if (strCommand == "inv") { + vector vInv; vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 20); return error("message inv size() = %u", vInv.size()); } - std::vector vToFetch; - - for(const auto& inv : vInv) { + LOCK(cs_main); - LOCK(cs_main); + std::vector vToFetch; + for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { + const CInv& inv = vInv[nInv]; boost::this_thread::interruption_point(); pfrom->AddInventoryKnown(inv); - bool fAlreadyHave = AlreadyHave(inv); LogPrint("net", "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id); @@ -4839,7 +4968,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } // Track requests for our stuff - g_signals.Inventory(inv.hash); + GetMainSignals().Inventory(inv.hash); if (pfrom->nSendSize > (SendBufferSize() * 2)) { Misbehaving(pfrom->GetId(), 50); @@ -4856,6 +4985,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vector vInv; vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 20); return error("message getdata size() = %u", vInv.size()); } @@ -4885,7 +5015,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pindex) pindex = chainActive.Next(pindex); int nLimit = 500; - LogPrintf("getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop == uint256(0) ? "end" : hashStop.ToString(), nLimit, pfrom->id); + LogPrint("net", "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop == uint256(0) ? "end" : hashStop.ToString(), nLimit, pfrom->id); for (; pindex; pindex = chainActive.Next(pindex)) { if (pindex->GetBlockHash() == hashStop) { LogPrint("net", " getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); @@ -4966,7 +5096,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } - std::string strMessage = tx.GetHash().ToString() + boost::lexical_cast(sigTime); + std::string strMessage = tx.GetHash().ToString() + std::to_string(sigTime); std::string errorMessage = ""; if (!obfuScationSigner.VerifyMessage(pmn->pubKeyMasternode, vchSig, strMessage, errorMessage)) { @@ -5008,21 +5138,21 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vWorkQueue.push_back(inv.hash); LogPrint("mempool", "AcceptToMemoryPool: peer=%d %s : accepted %s (poolsz %u)\n", - pfrom->id, pfrom->cleanSubVer, - tx.GetHash().ToString(), - mempool.mapTx.size()); + pfrom->id, pfrom->cleanSubVer, + tx.GetHash().ToString(), + mempool.mapTx.size()); // Recursively process any orphan transactions that depended on this one set setMisbehaving; - for (unsigned int i = 0; i < vWorkQueue.size(); i++) { + for(unsigned int i = 0; i < vWorkQueue.size(); i++) { map >::iterator itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue[i]); - if (itByPrev == mapOrphanTransactionsByPrev.end()) + if(itByPrev == mapOrphanTransactionsByPrev.end()) continue; - for (set::iterator mi = itByPrev->second.begin(); - mi != itByPrev->second.end(); - ++mi) { - const uint256& orphanHash = *mi; - const CTransaction& orphanTx = mapOrphanTransactions[orphanHash].tx; + for(set::iterator mi = itByPrev->second.begin(); + mi != itByPrev->second.end(); + ++mi) { + const uint256 &orphanHash = *mi; + const CTransaction &orphanTx = mapOrphanTransactions[orphanHash].tx; NodeId fromPeer = mapOrphanTransactions[orphanHash].fromPeer; bool fMissingInputs2 = false; // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan @@ -5031,16 +5161,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, CValidationState stateDummy; - if (setMisbehaving.count(fromPeer)) + if(setMisbehaving.count(fromPeer)) continue; - if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) { + if(AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) { LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString()); RelayTransaction(orphanTx); vWorkQueue.push_back(orphanHash); vEraseQueue.push_back(orphanHash); - } else if (!fMissingInputs2) { + } else if(!fMissingInputs2) { int nDos = 0; - if (stateDummy.IsInvalid(nDos) && nDos > 0) { + if(stateDummy.IsInvalid(nDos) && nDos > 0) { // Punish peer that gave us an invalid orphan tx Misbehaving(fromPeer, nDos); setMisbehaving.insert(fromPeer); @@ -5055,7 +5185,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } - BOOST_FOREACH (uint256 hash, vEraseQueue) + for (uint256 hash : vEraseQueue) EraseOrphanTx(hash); } else if (fMissingInputs) { AddOrphanTx(tx, pfrom->GetId()); @@ -5083,7 +5213,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrint("mempool", "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(), pfrom->id, pfrom->cleanSubVer, state.GetRejectReason()); - pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), + pfrom->PushMessage("reject", strCommand, (unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); if (nDoS > 0) Misbehaving(pfrom->GetId(), nDoS); @@ -5098,6 +5228,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks. unsigned int nCount = ReadCompactSize(vRecv); if (nCount > MAX_HEADERS_RESULTS) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 20); return error("headers message size = %u", nCount); } @@ -5114,7 +5245,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } CBlockIndex* pindexLast = NULL; - BOOST_FOREACH (const CBlockHeader& header, headers) { + for (const CBlockHeader& header : headers) { CValidationState state; if (pindexLast != NULL && header.hashPrevBlock != pindexLast->GetBlockHash()) { Misbehaving(pfrom->GetId(), 20); @@ -5175,12 +5306,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (!mapBlockIndex.count(block.GetHash())) { ProcessNewBlock(state, pfrom, &block); int nDoS; - if (state.IsInvalid(nDoS)) { - pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), - state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); - if (nDoS > 0) { + if(state.IsInvalid(nDoS)) { + pfrom->PushMessage("reject", strCommand, (unsigned char)state.GetRejectCode(), + state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); + if(nDoS > 0) { TRY_LOCK(cs_main, lockMain); - if (lockMain) Misbehaving(pfrom->GetId(), nDoS); + if(lockMain) Misbehaving(pfrom->GetId(), nDoS); } } //disconnect this node if its old protocol version @@ -5200,7 +5331,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if ((strCommand == "getaddr") && (pfrom->fInbound)) { pfrom->vAddrToSend.clear(); vector vAddr = addrman.GetAddr(); - BOOST_FOREACH (const CAddress& addr, vAddr) + for (const CAddress& addr : vAddr) pfrom->PushAddress(addr); } @@ -5211,7 +5342,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, std::vector vtxid; mempool.queryHashes(vtxid); vector vInv; - BOOST_FOREACH (uint256& hash, vtxid) { + for (uint256& hash : vtxid) { CInv inv(MSG_TX, hash); CTransaction tx; bool fInMemPool = mempool.lookup(hash, tx); @@ -5284,10 +5415,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } else { sProblem = "Unsolicited pong without ping"; } - } else { - // This is most likely a bug in another implementation somewhere, cancel this ping - bPingFinished = true; - sProblem = "Short payload"; } if (!(sProblem.empty())) { @@ -5316,7 +5443,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->setKnown.insert(alertHash); { LOCK(cs_vNodes); - BOOST_FOREACH (CNode* pnode, vNodes) + for (CNode* pnode : vNodes) alert.RelayTo(pnode); } } else { @@ -5326,6 +5453,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // This isn't a Misbehaving(100) (immediate ban) because the // peer might be an older or different implementation with // a different signature key, etc. + LOCK(cs_main); Misbehaving(pfrom->GetId(), 10); } } @@ -5336,6 +5464,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, strCommand == "filteradd" || strCommand == "filterclear")) { LogPrintf("bloom message=%s\n", strCommand); + LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); } @@ -5343,10 +5472,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, CBloomFilter filter; vRecv >> filter; - if (!filter.IsWithinSizeConstraints()) + if (!filter.IsWithinSizeConstraints()) { // There is no excuse for sending a too-large filter + LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); - else { + } else { LOCK(pfrom->cs_filter); delete pfrom->pfilter; pfrom->pfilter = new CBloomFilter(filter); @@ -5363,13 +5493,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Nodes must NEVER send a data item > 520 bytes (the max size for a script data object, // and thus, the maximum size any matched object can have) in a filteradd message if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); } else { LOCK(pfrom->cs_filter); if (pfrom->pfilter) pfrom->pfilter->insert(vData); - else + else { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); + } } } @@ -5409,26 +5542,23 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, obfuScationPool.ProcessMessageObfuscation(pfrom, strCommand, vRecv); mnodeman.ProcessMessage(pfrom, strCommand, vRecv); masternodePayments.ProcessMessageMasternodePayments(pfrom, strCommand, vRecv); - ProcessMessageSwiftTX(pfrom, strCommand, vRecv); ProcessSpork(pfrom, strCommand, vRecv); masternodeSync.ProcessMessage(pfrom, strCommand, vRecv); } + return true; } // Note: whenever a protocol update is needed toggle between both implementations (comment out the formerly active one) // so we can leave the existing clients untouched (old SPORK will stay on so they don't see even older clients). // Those old clients won't react to the changes of the other (new) SPORK because at the time of their implementation -// - +// it was the one which was commented out int ActiveProtocol() { - if(Params().F2ActivationTime() < GetAdjustedTime()) + if (IsSporkActive(SPORK_8_NEW_PROTOCOL_ENFORCEMENT)) return MIN_PEER_PROTO_VERSION_AFTER_ENFORCEMENT; -// if (IsSporkActive(SPORK_X_NEW_PROTOCOL_ENFORCEMENT_X)) -// return MIN_PEER_PROTO_VERSION_AFTER_ENFORCEMENT; return MIN_PEER_PROTO_VERSION_BEFORE_ENFORCEMENT; } @@ -5464,10 +5594,16 @@ bool ProcessMessages(CNode* pfrom) // get next message CNetMessage& msg = *it; - //if (fDebug) - // LogPrintf("ProcessMessages(message %u msgsz, %u bytes, complete:%s)\n", - // msg.hdr.nMessageSize, msg.vRecv.size(), - // msg.complete() ? "Y" : "N"); + if (fDebug) + LogPrintf("ProcessMessages(message %u msgsz, %u bytes, complete:%s)\n", + msg.hdr.nMessageSize, msg.vRecv.size(), + msg.complete() ? "Y" : "N"); + + ///////////////////////////////////////////////// + CMessageHeader& hdr = msg.hdr; + string strCommand = hdr.GetCommand(); + if (fDebug) LogPrintf("\e[32m%s\e[0m\n", strCommand.c_str()); + ///////////////////////////////////////////////// // end, if an incomplete message is found if (!msg.complete()) @@ -5484,19 +5620,18 @@ bool ProcessMessages(CNode* pfrom) } // Read header - CMessageHeader& hdr = msg.hdr; if (!hdr.IsValid()) { LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->id); continue; } - string strCommand = hdr.GetCommand(); + strCommand = hdr.GetCommand(); // Message size unsigned int nMessageSize = hdr.nMessageSize; // Checksum CDataStream& vRecv = msg.vRecv; - uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); + const uint256& hash = msg.GetMessageHash(); unsigned int nChecksum = 0; memcpy(&nChecksum, &hash, sizeof(nChecksum)); if (nChecksum != hdr.nChecksum) { @@ -5507,6 +5642,16 @@ bool ProcessMessages(CNode* pfrom) // Process message bool fRet = false; + ///////////////////////////////////////////////// + if (fDebug) { + char msgbuf[65536] = {0}; + unsigned int msglen = vRecv.size(); + if (msglen>65536) msglen = 65535; + memcpy(msgbuf, vRecv.str().c_str(), msglen); + for (unsigned int i = 0; i < msglen; i++) printf("%02hhx", msgbuf[i]); + printf("\n"); + } + ///////////////////////////////////////////////// try { fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime); boost::this_thread::interruption_point(); @@ -5545,211 +5690,227 @@ bool ProcessMessages(CNode* pfrom) bool SendMessages(CNode* pto, bool fSendTrickle) { - // Don't send anything until we get their version message - if (pto->nVersion == 0) - return true; + // Don't send anything until we get their version message + if (pto->nVersion == 0) + return true; - // - // Message: ping - // - bool pingSend = false; - if (pto->fPingQueued) { - // RPC ping request by user - pingSend = true; - } - if (pto->nPingNonceSent == 0 && pto->nPingUsecStart + PING_INTERVAL * 1000000 < GetTimeMicros()) { - // Ping automatically sent as a latency probe & keepalive. - pingSend = true; - } - if (pingSend) { - uint64_t nonce = 0; - while (nonce == 0) { - GetRandBytes((unsigned char*)&nonce, sizeof(nonce)); - } - pto->fPingQueued = false; - pto->nPingUsecStart = GetTimeMicros(); - if (pto->nVersion > BIP0031_VERSION) { - pto->nPingNonceSent = nonce; - pto->PushMessage("ping", nonce); - } else { - // Peer is too old to support ping command with nonce, pong will never arrive. - pto->nPingNonceSent = 0; - pto->PushMessage("ping"); + // + // Message: ping + // + bool pingSend = false; + if (pto->fPingQueued) { + // RPC ping request by user + pingSend = true; + } + if (pto->nPingNonceSent == 0 && pto->nPingUsecStart + PING_INTERVAL * 1000000 < GetTimeMicros()) { + // Ping automatically sent as a latency probe & keepalive. + pingSend = true; + } + if (pingSend) { + uint64_t nonce = 0; + while (nonce == 0) { + GetRandBytes((unsigned char*)&nonce, sizeof(nonce)); + } + pto->fPingQueued = false; + pto->nPingUsecStart = GetTimeMicros(); + if (pto->nVersion > BIP0031_VERSION) { + pto->nPingNonceSent = nonce; + pto->PushMessage("ping", nonce); + } else { + // Peer is too old to support ping command with nonce, pong will never arrive. + pto->nPingNonceSent = 0; + pto->PushMessage("ping"); + } } - } - TRY_LOCK(cs_main, lockMain); // Acquire cs_main for IsInitialBlockDownload() and CNodeState() - if (!lockMain) - return true; + TRY_LOCK(cs_main, lockMain); // Acquire cs_main for IsInitialBlockDownload() and CNodeState() + if (!lockMain) + return true; - // - // Message: addr - // - if (fSendTrickle) { - vector vAddr; - vAddr.reserve(pto->vAddrToSend.size()); - BOOST_FOREACH (const CAddress& addr, pto->vAddrToSend) { - // returns true if wasn't already contained in the set - if (pto->setAddrKnown.insert(addr).second) { - vAddr.push_back(addr); - // receiver rejects addr messages larger than 1000 - if (vAddr.size() >= 1000) { - pto->PushMessage("addr", vAddr); - vAddr.clear(); + // Address refresh broadcast + static int64_t nLastRebroadcast; + if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60)) { + LOCK(cs_vNodes); + for (CNode* pnode : vNodes) { + // Periodically clear setAddrKnown to allow refresh broadcasts + if (nLastRebroadcast) + pnode->setAddrKnown.clear(); + + // Rebroadcast our address + AdvertiseLocal(pnode); + } + if (!vNodes.empty()) + nLastRebroadcast = GetTime(); + } + + // + // Message: addr + // + if (fSendTrickle) { + vector vAddr; + vAddr.reserve(pto->vAddrToSend.size()); + for (const CAddress& addr : pto->vAddrToSend) { + // returns true if wasn't already contained in the set + if (pto->setAddrKnown.insert(addr).second) { + vAddr.push_back(addr); + // receiver rejects addr messages larger than 1000 + if (vAddr.size() >= 1000) { + pto->PushMessage("addr", vAddr); + vAddr.clear(); + } } } + pto->vAddrToSend.clear(); + if (!vAddr.empty()) + pto->PushMessage("addr", vAddr); } - pto->vAddrToSend.clear(); - if (!vAddr.empty()) - pto->PushMessage("addr", vAddr); - } - CNodeState& state = *State(pto->GetId()); - if (state.fShouldBan) { - if (pto->fWhitelisted) - LogPrintf("Warning: not punishing whitelisted peer %s!\n", pto->addr.ToString()); - else { - pto->fDisconnect = true; - if (pto->addr.IsLocal()) - LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString()); + CNodeState& state = *State(pto->GetId()); + if (state.fShouldBan) { + if (pto->fWhitelisted) + LogPrintf("Warning: not punishing whitelisted peer %s!\n", pto->addr.ToString()); else { - CNode::Ban(pto->addr); + pto->fDisconnect = true; + if (pto->addr.IsLocal()) + LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString()); + else { + CNode::Ban(pto->addr, BanReasonNodeMisbehaving); + } + } + state.fShouldBan = false; + } + + for (const CBlockReject& reject : state.rejects) + pto->PushMessage("reject", (string) "block", reject.chRejectCode, reject.strRejectReason, reject.hashBlock); + state.rejects.clear(); + + // Start block sync + if (pindexBestHeader == NULL) + pindexBestHeader = chainActive.Tip(); + bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); // Download if this is a nice peer, or we have no nice peers and this one might do. + if (!state.fSyncStarted && !pto->fClient && fFetch /*&& !fImporting*/ && !fReindex) { + // Only actively request headers from a single peer, unless we're close to end of initial download. + if (nSyncStarted == 0 || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 6 * 60 * 60) { // NOTE: was "close to today" and 24h in Bitcoin + state.fSyncStarted = true; + nSyncStarted++; + //CBlockIndex *pindexStart = pindexBestHeader->pprev ? pindexBestHeader->pprev : pindexBestHeader; + //LogPrint("net", "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->id, pto->nStartingHeight); + //pto->PushMessage("getheaders", chainActive.GetLocator(pindexStart), uint256(0)); + pto->PushMessage("getblocks", chainActive.GetLocator(chainActive.Tip()), uint256(0)); } } - state.fShouldBan = false; - } - - BOOST_FOREACH (const CBlockReject& reject, state.rejects) - pto->PushMessage("reject", (string) "block", reject.chRejectCode, reject.strRejectReason, reject.hashBlock); - state.rejects.clear(); - // Start block sync - if (pindexBestHeader == NULL) - pindexBestHeader = chainActive.Tip(); - bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); // Download if this is a nice peer, or we have no nice peers and this one might do. - if (!state.fSyncStarted && !pto->fClient && fFetch /*&& !fImporting*/ && !fReindex) { - // Only actively request headers from a single peer, unless we're close to end of initial download. - if (nSyncStarted == 0 || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 6 * 60 * 60) { // NOTE: was "close to today" and 24h in Bitcoin - state.fSyncStarted = true; - nSyncStarted++; - //CBlockIndex *pindexStart = pindexBestHeader->pprev ? pindexBestHeader->pprev : pindexBestHeader; - //LogPrint("net", "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->id, pto->nStartingHeight); - //pto->PushMessage("getheaders", chainActive.GetLocator(pindexStart), uint256(0)); - pto->PushMessage("getblocks", chainActive.GetLocator(chainActive.Tip()), uint256(0)); + // Resend wallet transactions that haven't gotten in a block yet + // Except during reindex, importing and IBD, when old wallet + // transactions become unconfirmed and spams other nodes. + if (!fReindex /*&& !fImporting && !IsInitialBlockDownload()*/) { + GetMainSignals().Broadcast(); } - } - // Resend wallet transactions that haven't gotten in a block yet - // Except during reindex, importing and IBD, when old wallet - // transactions become unconfirmed and spams other nodes. - if (!fReindex /*&& !fImporting && !IsInitialBlockDownload()*/) { - g_signals.Broadcast(); - } + // + // Message: inventory + // + vector vInv; + vector vInvWait; + { + LOCK(pto->cs_inventory); + vInv.reserve(pto->vInventoryToSend.size()); + vInvWait.reserve(pto->vInventoryToSend.size()); + for (const CInv& inv : pto->vInventoryToSend) { + if (pto->setInventoryKnown.count(inv)) + continue; - // - // Message: inventory - // - vector vInv; - vector vInvWait; - { - LOCK(pto->cs_inventory); - vInv.reserve(pto->vInventoryToSend.size()); - vInvWait.reserve(pto->vInventoryToSend.size()); - BOOST_FOREACH (const CInv& inv, pto->vInventoryToSend) { - if (pto->setInventoryKnown.count(inv)) - continue; + // trickle out tx inv to protect privacy + if (inv.type == MSG_TX && !fSendTrickle) { + // 1/4 of tx invs blast to all immediately + static uint256 hashSalt; + if (hashSalt == 0) + hashSalt = GetRandHash(); + uint256 hashRand = inv.hash ^ hashSalt; + hashRand = Hash(BEGIN(hashRand), END(hashRand)); + bool fTrickleWait = ((hashRand & 3) != 0); - // trickle out tx inv to protect privacy - if (inv.type == MSG_TX && !fSendTrickle) { - // 1/4 of tx invs blast to all immediately - static uint256 hashSalt; - if (hashSalt == 0) - hashSalt = GetRandHash(); - uint256 hashRand = inv.hash ^ hashSalt; - hashRand = Hash(BEGIN(hashRand), END(hashRand)); - bool fTrickleWait = ((hashRand & 3) != 0); - - if (fTrickleWait) { - vInvWait.push_back(inv); - continue; + if (fTrickleWait) { + vInvWait.push_back(inv); + continue; + } } - } - // returns true if wasn't already contained in the set - if (pto->setInventoryKnown.insert(inv).second) { - vInv.push_back(inv); - if (vInv.size() >= 1000) { - pto->PushMessage("inv", vInv); - vInv.clear(); + // returns true if wasn't already contained in the set + if (pto->setInventoryKnown.insert(inv).second) { + vInv.push_back(inv); + if (vInv.size() >= 1000) { + pto->PushMessage("inv", vInv); + vInv.clear(); + } } } + pto->vInventoryToSend = vInvWait; + } + if (!vInv.empty()) + pto->PushMessage("inv", vInv); + + // Detect whether we're stalling + int64_t nNow = GetTimeMicros(); + if (!pto->fDisconnect && state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) { + // Stalling only triggers when the block download window cannot move. During normal steady state, + // the download window should be much larger than the to-be-downloaded set of blocks, so disconnection + // should only happen during initial block download. + LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->id); + pto->fDisconnect = true; + } + // In case there is a block that has been in flight from this peer for (2 + 0.5 * N) times the block interval + // (with N the number of validated blocks that were in flight at the time it was requested), disconnect due to + // timeout. We compensate for in-flight blocks to prevent killing off peers due to our own downstream link + // being saturated. We only count validated in-flight blocks so peers can't advertize nonexisting block hashes + // to unreasonably increase our timeout. + if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0 && state.vBlocksInFlight.front().nTime < nNow - 500000 * Params().TargetSpacing() * (4 + state.vBlocksInFlight.front().nValidatedQueuedBefore)) { + LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", state.vBlocksInFlight.front().hash.ToString(), pto->id); + pto->fDisconnect = true; } - pto->vInventoryToSend = vInvWait; - } - if (!vInv.empty()) - pto->PushMessage("inv", vInv); - - // Detect whether we're stalling - int64_t nNow = GetTimeMicros(); - if (!pto->fDisconnect && state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) { - // Stalling only triggers when the block download window cannot move. During normal steady state, - // the download window should be much larger than the to-be-downloaded set of blocks, so disconnection - // should only happen during initial block download. - LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->id); - pto->fDisconnect = true; - } - // In case there is a block that has been in flight from this peer for (2 + 0.5 * N) times the block interval - // (with N the number of validated blocks that were in flight at the time it was requested), disconnect due to - // timeout. We compensate for in-flight blocks to prevent killing off peers due to our own downstream link - // being saturated. We only count validated in-flight blocks so peers can't advertize nonexisting block hashes - // to unreasonably increase our timeout. - if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0 && state.vBlocksInFlight.front().nTime < nNow - 500000 * Params().TargetSpacing() * (4 + state.vBlocksInFlight.front().nValidatedQueuedBefore)) { - LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", state.vBlocksInFlight.front().hash.ToString(), pto->id); - pto->fDisconnect = true; - } - // - // Message: getdata (blocks) - // - vector vGetData; - if (!pto->fDisconnect && !pto->fClient && fFetch && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { - vector vToDownload; - NodeId staller = -1; - FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller); - BOOST_FOREACH (CBlockIndex* pindex, vToDownload) { - vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); - MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), pindex); - LogPrintf("Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), - pindex->nHeight, pto->id); - } - if (state.nBlocksInFlight == 0 && staller != -1) { - if (State(staller)->nStallingSince == 0) { - State(staller)->nStallingSince = nNow; - LogPrint("net", "Stall started peer=%d\n", staller); + // + // Message: getdata (blocks) + // + vector vGetData; + if (!pto->fDisconnect && !pto->fClient && fFetch && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { + vector vToDownload; + NodeId staller = -1; + FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller); + for (CBlockIndex* pindex : vToDownload) { + vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); + MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), pindex); + LogPrintf("Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), + pindex->nHeight, pto->id); + } + if (state.nBlocksInFlight == 0 && staller != -1) { + if (State(staller)->nStallingSince == 0) { + State(staller)->nStallingSince = nNow; + LogPrint("net", "Stall started peer=%d\n", staller); + } } } - } - // - // Message: getdata (non-blocks) - // - while (!pto->fDisconnect && !pto->mapAskFor.empty() && pto->mapAskFor.begin()->first <= nNow) { - const CInv& inv = pto->mapAskFor.begin()->second; - if (!AlreadyHave(inv)) { - if (fDebug) - LogPrint("net", "Requesting %s peer=%d\n", inv.ToString(), pto->id); - vGetData.push_back(inv); - if (vGetData.size() >= 1000) { - pto->PushMessage("getdata", vGetData); - vGetData.clear(); + // + // Message: getdata (non-blocks) + // + while (!pto->fDisconnect && !pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) { + const CInv& inv = (*pto->mapAskFor.begin()).second; + if (!AlreadyHave(inv)) { + if (fDebug) + LogPrint("net", "Requesting %s peer=%d\n", inv.ToString(), pto->id); + vGetData.push_back(inv); + if (vGetData.size() >= 1000) { + pto->PushMessage("getdata", vGetData); + vGetData.clear(); + } } + pto->mapAskFor.erase(pto->mapAskFor.begin()); } - pto->mapAskFor.erase(pto->mapAskFor.begin()); - } - if (!vGetData.empty()) - pto->PushMessage("getdata", vGetData); + if (!vGetData.empty()) + pto->PushMessage("getdata", vGetData); - return true; + return true; } diff --git a/src/main.h b/src/main.h index 50e88f6..9c3ddc1 100644 --- a/src/main.h +++ b/src/main.h @@ -2,7 +2,7 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -43,6 +43,7 @@ class CBlockIndex; class CBlockTreeDB; +class CSporkDB; class CBloomFilter; class CInv; class CScriptCheck; @@ -102,6 +103,17 @@ static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111; /** Enable bloom filter */ static const bool DEFAULT_PEERBLOOMFILTERS = true; +static const bool DEFAULT_PEERBLOOMFILTERS_ZC = false; + +/** If the tip is older than this (in seconds), the node is considered to be in initial block download. */ +static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60; + +/** Default for -blockspamfilter, use header spam filter */ +static const bool DEFAULT_BLOCK_SPAM_FILTER = true; +/** Default for -blockspamfiltermaxsize, maximum size of the list of indexes in the block spam filter */ +static const unsigned int DEFAULT_BLOCK_SPAM_FILTER_MAX_SIZE = COINBASE_MATURITY; +/** Default for -blockspamfiltermaxavg, maximum average size of an index occurrence in the block spam filter */ +static const unsigned int DEFAULT_BLOCK_SPAM_FILTER_MAX_AVG = 10; /** "reject" message codes */ static const unsigned char REJECT_MALFORMED = 0x01; @@ -137,6 +149,8 @@ extern bool fCheckBlockIndex; extern unsigned int nCoinCacheSize; extern CFeeRate minRelayTxFee; extern bool fAlerts; +extern bool fVerifyingBlocks; +extern bool fClearSpendCache; extern bool fLargeWorkForkFound; extern bool fLargeWorkInvalidChainFound; @@ -195,7 +209,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos* dbp = NULL); /** Initialize a new block tree database + block data on disk */ bool InitBlockIndex(); /** Load the block tree and coins database from disk */ -bool LoadBlockIndex(); +bool LoadBlockIndex(std::string& strError); /** Unload database information */ void UnloadBlockIndex(); /** See whether the protocol update is enforced for connected nodes */ @@ -221,7 +235,9 @@ bool IsInitialBlockDownload(); /** Format a string that describes several potential problems detected by the core */ std::string GetWarnings(std::string strFor); /** Retrieve a transaction (from memory pool, or from disk, if possible) */ -bool GetTransaction(const uint256& hash, CTransaction& tx, uint256& hashBlock, bool fAllowSlow = false); +bool GetTransaction(const uint256& hash, CTransaction& tx, uint256& hashBlock, bool fAllowSlow = false, CBlockIndex* blockIndex = nullptr); +/** Retrieve an output (from memory pool, or from disk, if possible) */ +bool GetOutput(const uint256& hash, unsigned int index, CValidationState& state, CTxOut& out); /** Find the best known block, and make it the tip of the block chain */ bool DisconnectBlocksAndReprocess(int blocks); @@ -230,7 +246,7 @@ bool DisconnectBlocksAndReprocess(int blocks); double ConvertBitsToDouble(unsigned int nBits); int64_t GetMasternodePayment(int nHeight, uint32_t nTime, unsigned mnlevel, int64_t blockValue); -bool ActivateBestChain(CValidationState& state, CBlock* pblock = NULL); +bool ActivateBestChain(CValidationState& state, CBlock* pblock = NULL, bool fAlreadyChecked = false); CAmount GetBlockValue(int nHeight, uint32_t nTime); /** Create a new block index entry for a given block hash */ @@ -342,6 +358,8 @@ void UpdateCoins(const CTransaction& tx, CValidationState& state, CCoinsViewCach /** Context-independent validity checks */ bool CheckTransaction(const CTransaction& tx, CValidationState& state); +bool IsTransactionInChain(const uint256& txId, int& nHeightTx, CTransaction& tx); +bool IsTransactionInChain(const uint256& txId, int& nHeightTx); /** * Check if transaction will be final in the next block to be created. @@ -431,7 +449,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex bool DisconnectBlocksAndReprocess(int blocks); /** Apply the effects of this block (with given index) on the UTXO set represented by coins */ -bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck = false); +bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck, bool fAlreadyChecked = false); /** Context-independent validity checks */ bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true); @@ -446,7 +464,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn bool TestBlockValidity(CValidationState& state, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true); /** Store block on disk. If dbp is provided, the file is known to already reside on disk */ -bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** pindex, CDiskBlockPos* dbp = NULL); +bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** pindex, CDiskBlockPos* dbp = NULL, bool fAlreadyCheckedBlock = false); bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex** ppindex = NULL); @@ -608,26 +626,13 @@ extern CCoinsViewCache* pcoinsTip; /** Global variable that points to the active block tree (protected by cs_main) */ extern CBlockTreeDB* pblocktree; +/** Global variable that points to the spork database (protected by cs_main) */ +extern CSporkDB* pSporkDB; + struct CBlockTemplate { CBlock block; std::vector vTxFees; std::vector vTxSigOps; }; -/* -class CValidationInterface -{ -protected: - virtual void SyncTransaction(const CTransaction& tx, const CBlock* pblock){}; - virtual void EraseFromWallet(const uint256& hash){}; - virtual void SetBestChain(const CBlockLocator& locator){}; - virtual bool UpdatedTransaction(const uint256& hash) { return false; }; - virtual void Inventory(const uint256& hash){}; - virtual void ResendWalletTransactions(){}; - virtual void BlockChecked(const CBlock&, const CValidationState&){}; - friend void ::RegisterValidationInterface(CValidationInterface*); - friend void ::UnregisterValidationInterface(CValidationInterface*); - friend void ::UnregisterAllValidationInterfaces(); -}; -*/ #endif // BITCOIN_MAIN_H diff --git a/src/masternode-payments.cpp b/src/masternode-payments.cpp index 8fa8e13..04c7613 100644 --- a/src/masternode-payments.cpp +++ b/src/masternode-payments.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers // Copyright (c) 2017-2018 The Bulwark developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -28,19 +28,19 @@ CCriticalSection cs_mapMasternodePayeeVotes; bool IsBlockValueValid(const CBlock& block, CAmount nExpectedValue, CAmount nMinted) { CBlockIndex* pindexPrev = chainActive.Tip(); - if (!pindexPrev) return true; + if (pindexPrev == NULL) return true; int nHeight = 0; if (pindexPrev->GetBlockHash() == block.hashPrevBlock) { nHeight = pindexPrev->nHeight + 1; } else { //out of order BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); - if (mi != mapBlockIndex.end() && mi->second) - nHeight = mi->second->nHeight + 1; + if (mi != mapBlockIndex.end() && (*mi).second) + nHeight = (*mi).second->nHeight + 1; } - if (!nHeight) { - LogPrintf("IsBlockValueValid() : WARNING: Couldn't find previous block\n"); + if (nHeight == 0) { + LogPrintf("masternode", "IsBlockValueValid() : WARNING: Couldn't find previous block\n"); } if (nMinted > nExpectedValue) { @@ -58,7 +58,7 @@ bool IsBlockPayeeValid(const CBlock& block, int nBlockHeight) } // disable mn payments checks till fork activation - if(Params().F2ActivationTime() <= block.nTime) + if(Params().F3Activation() >= nBlockHeight) return true; const CTransaction& txNew = (nBlockHeight > Params().LAST_POW_BLOCK() ? block.vtx[1] : block.vtx[0]); @@ -106,7 +106,7 @@ CAmount CMasternodePayments::FillBlockPayee(CMutableTransaction& txNew, uint32_t payee = GetScriptForDestination(winningNode->pubKeyCollateralAddress.GetID()); } - CAmount masternodePayment = GetMasternodePayment(pindexPrev->nHeight, nTime, mnlevel, block_value); + CAmount masternodePayment = GetMasternodePayment(pindexPrev->nHeight + 1, nTime, mnlevel, block_value); if(!masternodePayment) continue; @@ -123,6 +123,7 @@ CAmount CMasternodePayments::FillBlockPayee(CMutableTransaction& txNew, uint32_t } return mn_payments_total; + } int CMasternodePayments::GetMinMasternodePaymentsProto() @@ -136,6 +137,25 @@ void CMasternodePayments::ProcessMessageMasternodePayments(CNode* pfrom, std::st if (fLiteMode) return; //disable all Obfuscation/Masternode related functionality + if (strCommand == "mnget") { //Masternode Payments Request Sync + + int nCountNeeded; + vRecv >> nCountNeeded; + + if (Params().NetworkID() == CBaseChainParams::MAIN) { + + if (pfrom->HasFulfilledRequest("mnget")) { + LogPrintf("mnget - peer already asked me for the list\n"); + Misbehaving(pfrom->GetId(), 20); + return; + } + } + + pfrom->FulfilledRequest("mnget"); + masternodePayments.Sync(pfrom, nCountNeeded); + LogPrint("mnpayments", "mnget - Sent Masternode winners to peer %i\n", pfrom->GetId()); + } else + if (strCommand == "mnw") { //Masternode Payments Declare Winner //this is required in litemodef CMasternodePaymentWinner winner; @@ -169,15 +189,15 @@ void CMasternodePayments::ProcessMessageMasternodePayments(CNode* pfrom, std::st return; } - std::string strError = ""; - if (!winner.IsValid(pfrom, strError)) { - // if(strError != "") LogPrintf("mnw - invalid message - %s\n", strError); + int nFirstBlock = nHeight - mnodeman.CountEnabled(winner.payeeLevel) / 100 * 125; + if (winner.nBlockHeight < nFirstBlock || winner.nBlockHeight > nHeight + 20) { + LogPrint("mnpayments", "mnw - winner out of range - FirstBlock %d Height %d bestHeight %d\n", nFirstBlock, winner.nBlockHeight, nHeight); return; } - int nFirstBlock = nHeight - mnodeman.CountEnabled(winner.payeeLevel) * 125 / 100; - if (winner.nBlockHeight < nFirstBlock || winner.nBlockHeight > nHeight + 20) { - LogPrint("mnpayments", "mnw - winner out of range - FirstBlock %d Height %d bestHeight %d\n", nFirstBlock, winner.nBlockHeight, nHeight); + std::string strError = ""; + if (!winner.IsValid(pfrom, strError)) { + // if(strError != "") LogPrintf("mnw - invalid message - %s\n", strError); return; } @@ -208,17 +228,15 @@ bool CMasternodePaymentWinner::Sign(CKey& keyMasternode, CPubKey& pubKeyMasterno std::string errorMessage; std::string strMasterNodeSignMessage; - std::string strMessage = vinMasternode.prevout.ToStringShort() + - boost::lexical_cast(nBlockHeight) + - payee.ToString(); + std::string strMessage = vinMasternode.prevout.ToStringShort() + std::to_string(nBlockHeight) + payee.ToString(); if (!obfuScationSigner.SignMessage(strMessage, errorMessage, vchSig, keyMasternode)) { - LogPrintf("CMasternodePing::Sign() - Error: %s\n", errorMessage.c_str()); + LogPrint("masternode","CMasternodePing::Sign() - Error: %s\n", errorMessage.c_str()); return false; } if (!obfuScationSigner.VerifyMessage(pubKeyMasternode, vchSig, strMessage, errorMessage)) { - LogPrintf("CMasternodePing::Sign() - Error: %s\n", errorMessage.c_str()); + LogPrint("masternode","CMasternodePing::Sign() - Error: %s\n", errorMessage.c_str()); return false; } @@ -236,7 +254,8 @@ bool CMasternodePayments::GetBlockPayee(int nBlockHeight, unsigned mnlevel, CScr } // Is this masternode scheduled to get paid soon? -bool CMasternodePayments::IsScheduled(CMasternode& mn, int nSameLevelMNCount, int nNotBlockHeight) const +// -- Only look ahead up to 8 blocks to allow for propagation of the latest 2 winners +bool CMasternodePayments::IsScheduled(CMasternode& mn, int nNotBlockHeight) { LOCK(cs_mapMasternodeBlocks); @@ -244,7 +263,7 @@ bool CMasternodePayments::IsScheduled(CMasternode& mn, int nSameLevelMNCount, in { TRY_LOCK(cs_main, locked); - if(!locked) + if (!locked) return false; auto chain_tip = chainActive.Tip(); @@ -257,7 +276,7 @@ bool CMasternodePayments::IsScheduled(CMasternode& mn, int nSameLevelMNCount, in CScript mnpayee = GetScriptForDestination(mn.pubKeyCollateralAddress.GetID()); - for(int64_t h_upper_bound = nHeight + 10, h = h_upper_bound - std::min(10, nSameLevelMNCount - 1); h < h_upper_bound; ++h) { + for(int64_t h = nHeight; h <= nHeight + 8; ++h) { if(h == nNotBlockHeight) continue; @@ -283,7 +302,7 @@ bool CMasternodePayments::CanVote(const COutPoint& outMasternode, int nBlockHeig { LOCK(cs_mapMasternodePayeeVotes); - uint256 key = ((outMasternode.hash + outMasternode.n) << 4) + mnlevel; + uint256 key = outMasternode.hash + outMasternode.n + mnlevel; auto ins_res = mapMasternodesLastVote.emplace(key, nBlockHeight); @@ -291,7 +310,7 @@ bool CMasternodePayments::CanVote(const COutPoint& outMasternode, int nBlockHeig auto& last_vote = ins_res.first->second; - if(last_vote >= nBlockHeight) + if(last_vote <= nBlockHeight) return false; last_vote = nBlockHeight; @@ -391,7 +410,7 @@ bool CMasternodeBlockPayees::IsTransactionValid(const CTransaction& txNew, uint3 strPayeesPossible += "," + address2; } - LogPrintf("CMasternodePayments::IsTransactionValid - Missing required payment to %s\n", strPayeesPossible.c_str()); + LogPrint("masternode","CMasternodePayments::IsTransactionValid - Missing required payment to %s\n", strPayeesPossible.c_str()); return false; } @@ -450,22 +469,22 @@ void CMasternodePayments::CleanPaymentList() int nHeight; { TRY_LOCK(cs_main, locked); - if (!locked || !chainActive.Tip()) return; + if (!locked || chainActive.Tip() == NULL) return; nHeight = chainActive.Tip()->nHeight; } //keep up to five cycles for historical sake - int nLimit = std::max(mnodeman.size() * 125 / 100, 1000); + int nLimit = std::max(int(mnodeman.size() * 1.25), 1000); std::map::iterator it = mapMasternodePayeeVotes.begin(); while (it != mapMasternodePayeeVotes.end()) { - CMasternodePaymentWinner winner = it->second; + CMasternodePaymentWinner winner = (*it).second; if (nHeight - winner.nBlockHeight > nLimit) { LogPrint("mnpayments", "CMasternodePayments::CleanPaymentList - Removing old Masternode payment - block %d\n", winner.nBlockHeight); - masternodeSync.mapSeenSyncMNW.erase(it->first); + masternodeSync.mapSeenSyncMNW.erase((*it).first); + mapMasternodePayeeVotes.erase(it++); mapMasternodeBlocks.erase(winner.nBlockHeight); - it = mapMasternodePayeeVotes.erase(it); } else { ++it; } @@ -478,14 +497,14 @@ bool CMasternodePaymentWinner::IsValid(CNode* pnode, std::string& strError) if (!pmn) { strError = strprintf("Unknown Masternode %s", vinMasternode.prevout.hash.ToString()); - LogPrintf("CMasternodePaymentWinner::IsValid - %s\n", strError); + LogPrint("masternode","CMasternodePaymentWinner::IsValid - %s\n", strError); mnodeman.AskForMN(pnode, vinMasternode); return false; } if (pmn->protocolVersion < ActiveProtocol()) { strError = strprintf("Masternode protocol too old %d - req %d", pmn->protocolVersion, ActiveProtocol()); - LogPrintf("CMasternodePaymentWinner::IsValid - %s\n", strError); + LogPrint("masternode","CMasternodePaymentWinner::IsValid - %s\n", strError); return false; } @@ -493,7 +512,7 @@ bool CMasternodePaymentWinner::IsValid(CNode* pnode, std::string& strError) if(n == -1) { strError = strprintf("Unknown Masternode (rank==-1) %s", vinMasternode.prevout.hash.ToString()); - LogPrintf("CMasternodePaymentWinner::IsValid - %s\n", strError); + LogPrint("masternode","CMasternodePaymentWinner::IsValid - %s\n", strError); return false; } @@ -502,7 +521,7 @@ bool CMasternodePaymentWinner::IsValid(CNode* pnode, std::string& strError) // We don't want to print all of these messages, or punish them unless they're way off if (n > MNPAYMENTS_SIGNATURES_TOTAL * 2) { strError = strprintf("Masternode not in the top %d (%d)", MNPAYMENTS_SIGNATURES_TOTAL * 2, n); - LogPrintf("CMasternodePaymentWinner::IsValid - %s\n", strError); + LogPrint("masternode","CMasternodePaymentWinner::IsValid - %s\n", strError); if (masternodeSync.IsSynced()) Misbehaving(pnode->GetId(), 20); } return false; @@ -516,14 +535,12 @@ bool CMasternodePayments::ProcessBlock(int nBlockHeight) if(!fMasterNode) return false; - auto nWinnerBlockHeight = nBlockHeight + 10; - //reference node - hybrid mode - if(nWinnerBlockHeight <= nLastBlockHeight) + if(nBlockHeight <= nLastBlockHeight) return false; - int n = mnodeman.GetMasternodeRank(activeMasternode.vin, nWinnerBlockHeight - 100, ActiveProtocol()); + int n = mnodeman.GetMasternodeRank(activeMasternode.vin, nBlockHeight - 100, ActiveProtocol()); if(n == -1) { LogPrint("mnpayments", "CMasternodePayments::ProcessBlock - Unknown Masternode\n"); @@ -535,7 +552,7 @@ bool CMasternodePayments::ProcessBlock(int nBlockHeight) return false; } - LogPrintf("CMasternodePayments::ProcessBlock() Start nHeight %d - vin %s. \n", nWinnerBlockHeight, activeMasternode.vin.prevout.hash.ToString()); + LogPrint("masternode","CMasternodePayments::ProcessBlock() Start nHeight %d - vin %s. \n", nBlockHeight, activeMasternode.vin.prevout.hash.ToString()); // pay to the oldest MN that still had no payment but its input is old enough and it was active long enough std::string errorMessage; @@ -543,7 +560,7 @@ bool CMasternodePayments::ProcessBlock(int nBlockHeight) CKey keyMasternode; if (!obfuScationSigner.SetKey(strMasterNodePrivKey, errorMessage, keyMasternode, pubKeyMasternode)) { - LogPrintf("CMasternodePayments::ProcessBlock() - Error upon calling SetKey: %s\n", errorMessage.c_str()); + LogPrint("masternode","CMasternodePayments::ProcessBlock() - Error upon calling SetKey: %s\n", errorMessage.c_str()); return false; } @@ -553,32 +570,32 @@ bool CMasternodePayments::ProcessBlock(int nBlockHeight) unsigned nCount = 0; - auto pmn = mnodeman.GetNextMasternodeInQueueForPayment(nWinnerBlockHeight, mnlevel, true, nCount); + auto pmn = mnodeman.GetNextMasternodeInQueueForPayment(nBlockHeight, mnlevel, true, nCount); if(!pmn) { - LogPrintf("CMasternodePayments::ProcessBlock() Failed to find masternode level %d to pay \n", mnlevel); + LogPrint("masternode","CMasternodePayments::ProcessBlock() Failed to find masternode level %d to pay \n", mnlevel); continue; } auto payee = GetScriptForDestination(pmn->pubKeyCollateralAddress.GetID()); - CMasternodePaymentWinner newWinner{activeMasternode.vin}; - newWinner.nBlockHeight = nWinnerBlockHeight; + CMasternodePaymentWinner newWinner{activeMasternode.vin}; + newWinner.nBlockHeight = nBlockHeight; newWinner.AddPayee(payee, mnlevel); CTxDestination address1; ExtractDestination(payee, address1); CBitcoinAddress address2{address1}; - LogPrintf("CMasternodePayments::ProcessBlock() Winner payee %s nHeight %d level %d. \n", address2.ToString().c_str(), newWinner.nBlockHeight, mnlevel); + LogPrint("masternode","CMasternodePayments::ProcessBlock() Winner payee %s nHeight %d level %d. \n", address2.ToString().c_str(), newWinner.nBlockHeight, mnlevel); - LogPrintf("CMasternodePayments::ProcessBlock() - Signing Winner level %d\n", mnlevel); + LogPrint("masternode","CMasternodePayments::ProcessBlock() - Signing Winner level %d\n", mnlevel); if(!newWinner.Sign(keyMasternode, pubKeyMasternode)) continue; - LogPrintf("CMasternodePayments::ProcessBlock() - AddWinningMasternode level %d\n", mnlevel); + LogPrint("masternode","CMasternodePayments::ProcessBlock() - AddWinningMasternode level %d\n", mnlevel); if(!AddWinningMasternode(newWinner)) continue; @@ -593,7 +610,7 @@ bool CMasternodePayments::ProcessBlock(int nBlockHeight) winner.Relay(); } - nLastBlockHeight = nWinnerBlockHeight; + nLastBlockHeight = nBlockHeight; return true; } @@ -608,20 +625,18 @@ bool CMasternodePaymentWinner::SignatureValid() { CMasternode* pmn = mnodeman.Find(vinMasternode); - if (!pmn) - return false; - - std::string strMessage = vinMasternode.prevout.ToStringShort() + - boost::lexical_cast(nBlockHeight) + - payee.ToString(); + if (pmn != NULL) { + std::string strMessage = vinMasternode.prevout.ToStringShort() + std::to_string(nBlockHeight) + payee.ToString(); - std::string errorMessage; + std::string errorMessage = ""; + if (!obfuScationSigner.VerifyMessage(pmn->pubKeyMasternode, vchSig, strMessage, errorMessage)) { + return error("CMasternodePaymentWinner::SignatureValid() - Got bad Masternode address signature %s\n", vinMasternode.prevout.hash.ToString()); + } - if (!obfuScationSigner.VerifyMessage(pmn->pubKeyMasternode, vchSig, strMessage, errorMessage)) { - return error("CMasternodePaymentWinner::SignatureValid() - Got bad Masternode address signature %s\n", vinMasternode.prevout.hash.ToString()); - } + return true; +} - return true; + return false; } void CMasternodePayments::Sync(CNode* node, int nCountNeeded) @@ -631,39 +646,30 @@ void CMasternodePayments::Sync(CNode* node, int nCountNeeded) int nHeight; { TRY_LOCK(cs_main, locked); - if(!locked || !chainActive.Tip()) - return; - + if (!locked || chainActive.Tip() == NULL) return; nHeight = chainActive.Tip()->nHeight; } auto mn_counts = mnodeman.CountEnabledByLevels(); - unsigned max_mn_count = 0u; - for(auto& count : mn_counts) - max_mn_count = std::max(max_mn_count, count.second * 125 / 100); - - if(max_mn_count > nCountNeeded) - max_mn_count = nCountNeeded; + for(auto& count : mn_counts) { + max_mn_count = std::max(max_mn_count, unsigned(count.second * 1.25)); + count.second = unsigned(count.second * 1.25) + 1; + } + if(max_mn_count > nCountNeeded) max_mn_count = nCountNeeded; int nInvCount = 0; for(const auto& vote : mapMasternodePayeeVotes) { - - const auto& winner = vote.second; - - bool push = winner.nBlockHeight >= nHeight - max_mn_count - && winner.nBlockHeight <= nHeight + 20; - - if(!push) - continue; - - node->PushInventory(CInv(MSG_MASTERNODE_WINNER, winner.GetHash())); - ++nInvCount; - } - - node->PushMessage("ssc", MASTERNODE_SYNC_MNW, nInvCount); + const auto& winner = vote.second; + bool push = winner.nBlockHeight >= nHeight - max_mn_count && winner.nBlockHeight <= nHeight + 20; + if(!push) + continue; + node->PushInventory(CInv(MSG_MASTERNODE_WINNER, winner.GetHash())); + ++nInvCount; + } + node->PushMessage("ssc", MASTERNODE_SYNC_MNW, nInvCount); } std::string CMasternodePayments::ToString() const diff --git a/src/masternode-payments.h b/src/masternode-payments.h index 66c4487..dd806e8 100644 --- a/src/masternode-payments.h +++ b/src/masternode-payments.h @@ -242,7 +242,7 @@ class CMasternodePayments bool GetBlockPayee(int nBlockHeight, unsigned mnlevel, CScript& payee); bool IsTransactionValid(const CTransaction& txNew, int nBlockHeight, uint32_t nTime); - bool IsScheduled(CMasternode& mn, int nSameLevelMNCount, int nNotBlockHeight) const; + bool IsScheduled(CMasternode& mn, int nNotBlockHeight); bool CanVote(const COutPoint& outMasternode, int nBlockHeight, unsigned mnlevel); int GetMinMasternodePaymentsProto(); diff --git a/src/masternode-sync.cpp b/src/masternode-sync.cpp index 357c6e2..1369139 100644 --- a/src/masternode-sync.cpp +++ b/src/masternode-sync.cpp @@ -49,8 +49,8 @@ bool CMasternodeSync::IsBlockchainSynced() if (!lockMain) return false; CBlockIndex* pindex = chainActive.Tip(); + if (pindex == NULL) return false; - if (!pindex) return false; if (pindex->nTime + 60 * 60 < GetTime()) return false; @@ -126,7 +126,7 @@ void CMasternodeSync::GetNextAsset() RequestedMasternodeAssets = MASTERNODE_SYNC_MNW; break; case (MASTERNODE_SYNC_MNW): - LogPrintf("CMasternodeSync::GetNextAsset - Sync has finished\n"); + LogPrint("masternode","CMasternodeSync::GetNextAsset - Sync has finished\n"); RequestedMasternodeAssets = MASTERNODE_SYNC_FINISHED; break; } @@ -217,19 +217,11 @@ void CMasternodeSync::Process() LogPrint("masternode", "CMasternodeSync::Process() - tick %d RequestedMasternodeAssets %d\n", tick, RequestedMasternodeAssets); - if (RequestedMasternodeAssets == MASTERNODE_SYNC_INITIAL) - GetNextAsset(); + if (RequestedMasternodeAssets == MASTERNODE_SYNC_INITIAL) GetNextAsset(); // sporks synced but blockchain is not, wait until we're almost at a recent block to continue - bool wait_blockchain_sync = Params().NetworkID() != CBaseChainParams::REGTEST - && !IsBlockchainSynced() - && RequestedMasternodeAssets > MASTERNODE_SYNC_SPORKS; - - if(wait_blockchain_sync) - { - nAssetSyncStarted = GetTime(); - return; - } + if (Params().NetworkID() != CBaseChainParams::REGTEST && + !IsBlockchainSynced() && RequestedMasternodeAssets > MASTERNODE_SYNC_SPORKS) return; TRY_LOCK(cs_vNodes, lockRecv); if (!lockRecv) return; @@ -252,18 +244,13 @@ void CMasternodeSync::Process() //set to synced if (RequestedMasternodeAssets == MASTERNODE_SYNC_SPORKS) { - if (pnode->HasFulfilledRequest("getspork")) - continue; - - if (RequestedMasternodeAttempt >= MASTERNODE_SYNC_THRESHOLD) { - GetNextAsset(); - return; - } - + if (pnode->HasFulfilledRequest("getspork")) continue; pnode->FulfilledRequest("getspork"); pnode->PushMessage("getsporks"); //get current network sporks + if (RequestedMasternodeAttempt >= 2) GetNextAsset(); RequestedMasternodeAttempt++; + return; } @@ -275,9 +262,7 @@ void CMasternodeSync::Process() return; } - if (pnode->HasFulfilledRequest("mnsync")) - continue; - + if (pnode->HasFulfilledRequest("mnsync")) continue; pnode->FulfilledRequest("mnsync"); // timeout @@ -295,29 +280,20 @@ void CMasternodeSync::Process() return; } - if (RequestedMasternodeAttempt >= MASTERNODE_SYNC_THRESHOLD * 3) - return; - - if(!mnodeman.DsegUpdate(pnode)) - continue; - - ++RequestedMasternodeAttempt; + if (RequestedMasternodeAttempt >= MASTERNODE_SYNC_THRESHOLD * 3) return; + mnodeman.DsegUpdate(pnode); + RequestedMasternodeAttempt++; return; } if (RequestedMasternodeAssets == MASTERNODE_SYNC_MNW) { - if (lastMasternodeWinner > 0 && lastMasternodeWinner < GetTime() - MASTERNODE_SYNC_TIMEOUT * 2 && RequestedMasternodeAttempt >= MASTERNODE_SYNC_THRESHOLD) { //hasn't received a new item in the last five seconds, so we'll move to the GetNextAsset(); - // Try to activate our masternode if possible - activeMasternode.ManageStatus(); return; } - if (pnode->HasFulfilledRequest("mnwsync")) - continue; - + if (pnode->HasFulfilledRequest("mnwsync")) continue; pnode->FulfilledRequest("mnwsync"); // timeout @@ -331,25 +307,36 @@ void CMasternodeSync::Process() nCountFailures++; } else { GetNextAsset(); - // Try to activate our masternode if possible - activeMasternode.ManageStatus(); } return; } - if (RequestedMasternodeAttempt >= MASTERNODE_SYNC_THRESHOLD * 3) - return; + if (RequestedMasternodeAttempt >= MASTERNODE_SYNC_THRESHOLD * 3) return; - if (!chainActive.Tip()) - return; + CBlockIndex* pindexPrev = chainActive.Tip(); + if (pindexPrev == NULL) return; - if(!mnodeman.WinnersUpdate(pnode)) - continue; + int nMnCount = mnodeman.CountEnabled(); + pnode->PushMessage("mnget", nMnCount); //sync payees + RequestedMasternodeAttempt++; - ++RequestedMasternodeAttempt; + return; + } + } + if (pnode->nVersion >= ActiveProtocol()) { + if ((RequestedMasternodeAttempt >= MASTERNODE_SYNC_THRESHOLD * 3 || GetTime() - nAssetSyncStarted > MASTERNODE_SYNC_TIMEOUT * 5)) { + GetNextAsset(); + activeMasternode.ManageStatus(); return; } + if (RequestedMasternodeAttempt >= MASTERNODE_SYNC_THRESHOLD * 3) return; + + if(!mnodeman.WinnersUpdate(pnode)) continue; + + RequestedMasternodeAttempt++; + + return; } } } diff --git a/src/masternode.cpp b/src/masternode.cpp index 4285286..b143e12 100644 --- a/src/masternode.cpp +++ b/src/masternode.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -10,6 +10,7 @@ #include "obfuscation.h" #include "sync.h" #include "util.h" +#include "spork.h" #include @@ -254,9 +255,7 @@ int64_t CMasternode::SecondsSincePayment() int64_t CMasternode::GetLastPaid() { CBlockIndex* pindexPrev = chainActive.Tip(); - - if (!pindexPrev) - return 0; + if (pindexPrev == NULL) return false; CScript mnpayee; mnpayee = GetScriptForDestination(pubKeyCollateralAddress.GetID()); @@ -266,12 +265,14 @@ int64_t CMasternode::GetLastPaid() ss << sigTime; uint256 hash = ss.GetHash(); - // use a deterministic offset to break a tie -- 1.5 minutes - int64_t nOffset = hash.GetCompact(false) % 90; + // use a deterministic offset to break a tie -- 2.5 minutes + int64_t nOffset = hash.GetCompact(false) % 150; - const CBlockIndex* BlockReading = pindexPrev; + if (chainActive.Tip() == NULL) return false; - int nMnCount = mnodeman.CountEnabled(Level()) * 125 / 100; + const CBlockIndex* BlockReading = chainActive.Tip(); + + int nMnCount = mnodeman.CountEnabled(Level()) * 1.25; int n = 0; for (unsigned int i = 1; BlockReading && BlockReading->nHeight > 0; i++) { if (n >= nMnCount) { @@ -285,16 +286,40 @@ int64_t CMasternode::GetLastPaid() to converge on the same payees quickly, then keep the same schedule. */ if (masternodePayments.mapMasternodeBlocks[BlockReading->nHeight].HasPayeeWithVotes(mnpayee, 2)) { - return BlockReading->nTime - nOffset; + return BlockReading->nTime + nOffset; } } + if (BlockReading->pprev == NULL) { + assert(BlockReading); + break; + } BlockReading = BlockReading->pprev; } return 0; } +std::string CMasternode::GetStatus() +{ + switch (nActiveState) { + case CMasternode::MASTERNODE_PRE_ENABLED: + return "PRE_ENABLED"; + case CMasternode::MASTERNODE_ENABLED: + return "ENABLED"; + case CMasternode::MASTERNODE_EXPIRED: + return "EXPIRED"; + case CMasternode::MASTERNODE_OUTPOINT_SPENT: + return "OUTPOINT_SPENT"; + case CMasternode::MASTERNODE_REMOVE: + return "REMOVE"; + case CMasternode::MASTERNODE_POSE_BAN: + return "POSE_BAN"; + default: + return "UNKNOWN"; + } +} + bool CMasternode::IsValidNetAddr() { // TODO: regtest is fine with any addresses for now, @@ -303,30 +328,37 @@ bool CMasternode::IsValidNetAddr() (IsReachable(addr) && addr.IsRoutable()); } -unsigned CMasternode::Level(CAmount vin_val) +unsigned CMasternode::Level(CAmount vin_val, int blockHeight) { - switch(vin_val) { - case 1000 * COIN: return 1; - case 3000 * COIN: return 2; - case 5000 * COIN: return 3; + if (blockHeight >= 0 && blockHeight < 682500) { + switch(vin_val) { + case 1000 * COIN: return 1; + case 3000 * COIN: return 2; + case 5000 * COIN: return 3; + } + } else if (blockHeight >= 682500) { + switch(vin_val) { + case 5000 * COIN: return 1; + case 10000 * COIN: return 2; + case 50000 * COIN: return 3; + } } - return 0; } -unsigned CMasternode::Level(const CTxIn& vin) +unsigned CMasternode::Level(const CTxIn& vin, int blockHeight) { CAmount vin_val; if(!IsDepositCoins(vin, vin_val)) return LevelValue::UNSPECIFIED; - return Level(vin_val); + return Level(vin_val, blockHeight); } bool CMasternode::IsDepositCoins(CAmount vin_val) { - return Level(vin_val); + return Level(vin_val, chainActive.Height()); } bool CMasternode::IsDepositCoins(const CTxIn& vin, CAmount& vin_val) @@ -413,19 +445,19 @@ bool CMasternodeBroadcast::Create(std::string strService, std::string strKeyMast //need correct blocks to send ping if (!fOffline && !masternodeSync.IsBlockchainSynced()) { strErrorRet = "Sync in progress. Must wait until sync is complete to start Masternode"; - LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet); + LogPrint("masternode","CMasternodeBroadcast::Create -- %s\n", strErrorRet); return false; } if (!obfuScationSigner.GetKeysFromSecret(strKeyMasternode, keyMasternodeNew, pubKeyMasternodeNew)) { strErrorRet = strprintf("Invalid masternode key %s", strKeyMasternode); - LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet); + LogPrint("masternode","CMasternodeBroadcast::Create -- %s\n", strErrorRet); return false; } if (!pwalletMain->GetMasternodeVinAndKeys(txin, pubKeyCollateralAddressNew, keyCollateralAddressNew, strTxHash, strOutputIndex)) { strErrorRet = strprintf("Could not allocate txin %s:%s for masternode %s", strTxHash, strOutputIndex, strService); - LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet); + LogPrint("masternode","CMasternodeBroadcast::Create -- %s\n", strErrorRet); return false; } @@ -447,7 +479,7 @@ bool CMasternodeBroadcast::Create(CTxIn txin, CService service, CKey keyCollater if(mnode && mnode->vin != txin) { strErrorRet = strprintf("Duplicate Masternode address: %s", service.ToString()); - LogPrintf("CMasternodeBroadcast::Create -- ActiveMasternode::Register() - %s\n", strErrorRet); + LogPrint("masternode","CMasternodeBroadcast::Create -- ActiveMasternode::Register() - %s\n", strErrorRet); mnbRet = CMasternodeBroadcast(); return false; } @@ -460,7 +492,7 @@ bool CMasternodeBroadcast::Create(CTxIn txin, CService service, CKey keyCollater CMasternodePing mnp(txin); if (!mnp.Sign(keyMasternodeNew, pubKeyMasternodeNew)) { strErrorRet = strprintf("Failed to sign ping, masternode=%s", txin.prevout.hash.ToString()); - LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet); + LogPrint("masternode","CMasternodeBroadcast::Create -- %s\n", strErrorRet); mnbRet = CMasternodeBroadcast(); return false; } @@ -469,7 +501,7 @@ bool CMasternodeBroadcast::Create(CTxIn txin, CService service, CKey keyCollater if (!mnbRet.IsValidNetAddr()) { strErrorRet = strprintf("Invalid IP address %s, masternode=%s", mnbRet.addr.ToStringIP (), txin.prevout.hash.ToString()); - LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet); + LogPrint("masternode","CMasternodeBroadcast::Create -- %s\n", strErrorRet); mnbRet = CMasternodeBroadcast(); return false; } @@ -477,7 +509,7 @@ bool CMasternodeBroadcast::Create(CTxIn txin, CService service, CKey keyCollater mnbRet.lastPing = mnp; if (!mnbRet.Sign(keyCollateralAddressNew)) { strErrorRet = strprintf("Failed to sign broadcast, masternode=%s", txin.prevout.hash.ToString()); - LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet); + LogPrint("masternode","CMasternodeBroadcast::Create -- %s\n", strErrorRet); mnbRet = CMasternodeBroadcast(); return false; } @@ -508,7 +540,7 @@ bool CMasternodeBroadcast::CheckAndUpdate(int& nDos) { // make sure signature isn't in the future (past is OK) if (sigTime > GetAdjustedTime() + 60 * 60) { - LogPrintf("mnb - Signature rejected, too far into the future %s\n", vin.prevout.hash.ToString()); + LogPrint("masternode","mnb - Signature rejected, too far into the future %s\n", vin.prevout.hash.ToString()); nDos = 1; return false; } @@ -518,7 +550,7 @@ bool CMasternodeBroadcast::CheckAndUpdate(int& nDos) std::string strMessage = addr.ToString() + boost::lexical_cast(sigTime) + vchPubKey + vchPubKey2 + boost::lexical_cast(protocolVersion); if (protocolVersion < masternodePayments.GetMinMasternodePaymentsProto()) { - LogPrintf("mnb - ignoring outdated Masternode %s protocol version %d\n", vin.prevout.hash.ToString(), protocolVersion); + LogPrint("masternode","mnb - ignoring outdated Masternode %s protocol version %d\n", vin.prevout.hash.ToString(), protocolVersion); return false; } @@ -526,7 +558,7 @@ bool CMasternodeBroadcast::CheckAndUpdate(int& nDos) pubkeyScript = GetScriptForDestination(pubKeyCollateralAddress.GetID()); if (pubkeyScript.size() != 25) { - LogPrintf("mnb - pubkey the wrong size\n"); + LogPrint("masternode","mnb - pubkey the wrong size\n"); nDos = 100; return false; } @@ -535,20 +567,20 @@ bool CMasternodeBroadcast::CheckAndUpdate(int& nDos) pubkeyScript2 = GetScriptForDestination(pubKeyMasternode.GetID()); if (pubkeyScript2.size() != 25) { - LogPrintf("mnb - pubkey2 the wrong size\n"); + LogPrint("masternode","mnb - pubkey2 the wrong size\n"); nDos = 100; return false; } if (!vin.scriptSig.empty()) { - LogPrintf("mnb - Ignore Not Empty ScriptSig %s\n", vin.prevout.hash.ToString()); + LogPrint("masternode","mnb - Ignore Not Empty ScriptSig %s\n", vin.prevout.hash.ToString()); return false; } std::string errorMessage = ""; if (!obfuScationSigner.VerifyMessage(pubKeyCollateralAddress, sig, strMessage, errorMessage)) { - LogPrintf("mnb - Got bad Masternode address signature\n"); + LogPrint("masternode","mnb - Got bad Masternode address signature\n"); nDos = 100; return false; } @@ -565,7 +597,7 @@ bool CMasternodeBroadcast::CheckAndUpdate(int& nDos) // this broadcast older than we have, it's bad. if (pmn->sigTime > sigTime) { - LogPrintf("mnb - Bad sigTime %d for Masternode %s (existing broadcast is at %d)\n", + LogPrint("masternode","mnb - Bad sigTime %d for Masternode %s (existing broadcast is at %d)\n", sigTime, vin.prevout.hash.ToString(), pmn->sigTime); return false; } @@ -642,7 +674,7 @@ bool CMasternodeBroadcast::CheckInputsAndAdd(int& nDoS) LogPrint("masternode", "mnb - Accepted Masternode entry\n"); if (GetInputAge(vin) < MASTERNODE_MIN_CONFIRMATIONS) { - LogPrintf("mnb - Input must have at least %d confirmations\n", MASTERNODE_MIN_CONFIRMATIONS); + LogPrint("masternode","mnb - Input must have at least %d confirmations\n", MASTERNODE_MIN_CONFIRMATIONS); // maybe we miss few blocks, let this mnb to be checked again later mnodeman.mapSeenMasternodeBroadcast.erase(GetHash()); masternodeSync.mapSeenSyncMNB.erase(GetHash()); @@ -659,7 +691,7 @@ bool CMasternodeBroadcast::CheckInputsAndAdd(int& nDoS) CBlockIndex* pMNIndex = mi->second; // block for 1000 XDNA tx -> 1 confirmation CBlockIndex* pConfIndex = chainActive[pMNIndex->nHeight + MASTERNODE_MIN_CONFIRMATIONS - 1]; // block where tx got MASTERNODE_MIN_CONFIRMATIONS if (pConfIndex->GetBlockTime() > sigTime) { - LogPrintf("mnb - Bad sigTime %d for Masternode %s (%i conf block is at %d)\n", + LogPrint("masternode","mnb - Bad sigTime %d for Masternode %s (%i conf block is at %d)\n", sigTime, vin.prevout.hash.ToString(), MASTERNODE_MIN_CONFIRMATIONS, pConfIndex->GetBlockTime() ); @@ -667,7 +699,7 @@ bool CMasternodeBroadcast::CheckInputsAndAdd(int& nDoS) } } - LogPrintf("mnb - Got NEW Masternode entry - %s - %lli \n", vin.prevout.hash.ToString(), sigTime); + LogPrint("masternode","mnb - Got NEW Masternode entry - %s - %lli \n", vin.prevout.hash.ToString(), sigTime); CMasternode mn(*this); mnodeman.Add(mn); @@ -705,18 +737,49 @@ bool CMasternodeBroadcast::Sign(CKey& keyCollateralAddress) std::string strMessage = addr.ToString() + boost::lexical_cast(sigTime) + vchPubKey + vchPubKey2 + boost::lexical_cast(protocolVersion); if (!obfuScationSigner.SignMessage(strMessage, errorMessage, sig, keyCollateralAddress)) { - LogPrintf("CMasternodeBroadcast::Sign() - Error: %s\n", errorMessage); + LogPrint("masternode","CMasternodeBroadcast::Sign() - Error: %s\n", errorMessage); return false; } if (!obfuScationSigner.VerifyMessage(pubKeyCollateralAddress, sig, strMessage, errorMessage)) { - LogPrintf("CMasternodeBroadcast::Sign() - Error: %s\n", errorMessage); + LogPrint("masternode","CMasternodeBroadcast::Sign() - Error: %s\n", errorMessage); return false; } return true; } +bool CMasternodeBroadcast::VerifySignature() +{ + std::string errorMessage; + + if(!obfuScationSigner.VerifyMessage(pubKeyCollateralAddress, sig, GetNewStrMessage(), errorMessage) + && !obfuScationSigner.VerifyMessage(pubKeyCollateralAddress, sig, GetOldStrMessage(), errorMessage)) + return error("CMasternodeBroadcast::VerifySignature() - Error: %s", errorMessage); + + return true; +} + +std::string CMasternodeBroadcast::GetOldStrMessage() +{ + std::string strMessage; + + std::string vchPubKey(pubKeyCollateralAddress.begin(), pubKeyCollateralAddress.end()); + std::string vchPubKey2(pubKeyMasternode.begin(), pubKeyMasternode.end()); + strMessage = addr.ToString() + std::to_string(sigTime) + vchPubKey + vchPubKey2 + std::to_string(protocolVersion); + + return strMessage; +} + +std:: string CMasternodeBroadcast::GetNewStrMessage() +{ + std::string strMessage; + + strMessage = addr.ToString() + std::to_string(sigTime) + pubKeyCollateralAddress.GetID().ToString() + pubKeyMasternode.GetID().ToString() + std::to_string(protocolVersion); + + return strMessage; +} + CMasternodePing::CMasternodePing() { vin = CTxIn(); @@ -743,12 +806,12 @@ bool CMasternodePing::Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode) std::string strMessage = vin.ToString() + blockHash.ToString() + boost::lexical_cast(sigTime); if (!obfuScationSigner.SignMessage(strMessage, errorMessage, vchSig, keyMasternode)) { - LogPrintf("CMasternodePing::Sign() - Error: %s\n", errorMessage); + LogPrint("masternode","CMasternodePing::Sign() - Error: %s\n", errorMessage); return false; } if (!obfuScationSigner.VerifyMessage(pubKeyMasternode, vchSig, strMessage, errorMessage)) { - LogPrintf("CMasternodePing::Sign() - Error: %s\n", errorMessage); + LogPrint("masternode","CMasternodePing::Sign() - Error: %s\n", errorMessage); return false; } @@ -758,13 +821,13 @@ bool CMasternodePing::Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode) bool CMasternodePing::CheckAndUpdate(int& nDos, bool fRequireEnabled) { if (sigTime > GetAdjustedTime() + 60 * 60) { - LogPrintf("CMasternodePing::CheckAndUpdate - Signature rejected, too far into the future %s\n", vin.prevout.hash.ToString()); + LogPrint("masternode","CMasternodePing::CheckAndUpdate - Signature rejected, too far into the future %s\n", vin.prevout.hash.ToString()); nDos = 1; return false; } if (sigTime <= GetAdjustedTime() - 60 * 60) { - LogPrintf("CMasternodePing::CheckAndUpdate - Signature rejected, too far into the past %s - %d %d \n", vin.prevout.hash.ToString(), sigTime, GetAdjustedTime()); + LogPrint("masternode","CMasternodePing::CheckAndUpdate - Signature rejected, too far into the past %s - %d %d \n", vin.prevout.hash.ToString(), sigTime, GetAdjustedTime()); nDos = 1; return false; } @@ -773,6 +836,7 @@ bool CMasternodePing::CheckAndUpdate(int& nDos, bool fRequireEnabled) // see if we have this Masternode CMasternode* pmn = mnodeman.Find(vin); + if (pmn != NULL && pmn->protocolVersion >= masternodePayments.GetMinMasternodePaymentsProto()) { if (fRequireEnabled && !pmn->IsEnabled()) return false; @@ -784,7 +848,7 @@ bool CMasternodePing::CheckAndUpdate(int& nDos, bool fRequireEnabled) std::string errorMessage = ""; if (!obfuScationSigner.VerifyMessage(pmn->pubKeyMasternode, vchSig, strMessage, errorMessage)) { - LogPrintf("CMasternodePing::CheckAndUpdate - Got bad Masternode address signature %s\n", vin.prevout.hash.ToString()); + LogPrint("masternode","CMasternodePing::CheckAndUpdate - Got bad Masternode address signature %s\n", vin.prevout.hash.ToString()); nDos = 33; return false; } @@ -792,14 +856,14 @@ bool CMasternodePing::CheckAndUpdate(int& nDos, bool fRequireEnabled) BlockMap::iterator mi = mapBlockIndex.find(blockHash); if (mi != mapBlockIndex.end() && (*mi).second) { if ((*mi).second->nHeight < chainActive.Height() - 24) { - LogPrintf("CMasternodePing::CheckAndUpdate - Masternode %s block hash %s is too old\n", vin.prevout.hash.ToString(), blockHash.ToString()); + LogPrint("masternode","CMasternodePing::CheckAndUpdate - Masternode %s block hash %s is too old\n", vin.prevout.hash.ToString(), blockHash.ToString()); // Do nothing here (no Masternode update, no mnping relay) // Let this node to be visible but fail to accept mnping return false; } } else { - if (fDebug) LogPrintf("CMasternodePing::CheckAndUpdate - Masternode %s block hash %s is unknown\n", vin.prevout.hash.ToString(), blockHash.ToString()); + if (fDebug) LogPrint("masternode","CMasternodePing::CheckAndUpdate - Masternode %s block hash %s is unknown\n", vin.prevout.hash.ToString(), blockHash.ToString()); // maybe we stuck so we shouldn't ban this node, just fail to accept it // TODO: or should we also request this block? @@ -815,6 +879,13 @@ bool CMasternodePing::CheckAndUpdate(int& nDos, bool fRequireEnabled) mnodeman.mapSeenMasternodeBroadcast[hash].lastPing = *this; } + if (IsSporkActive(SPORK_7_MN_REBROADCAST_ENFORCEMENT)) { + //dirty hack // + pmn->UpdateFromNewBroadcast(mnb); + mnb.Relay(); + ////////////// + } + pmn->Check(true); if (!pmn->IsEnabled()) return false; diff --git a/src/masternode.h b/src/masternode.h index 70f8d2b..c5007bf 100644 --- a/src/masternode.h +++ b/src/masternode.h @@ -1,6 +1,6 @@ // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -16,11 +16,11 @@ #include "util.h" #define MASTERNODE_MIN_CONFIRMATIONS 15 -#define MASTERNODE_MIN_MNP_SECONDS (4 * 60) +#define MASTERNODE_MIN_MNP_SECONDS (10 * 60) #define MASTERNODE_MIN_MNB_SECONDS (5 * 60) -#define MASTERNODE_PING_SECONDS (4 * 60) -#define MASTERNODE_EXPIRATION_SECONDS (9 * 60) -#define MASTERNODE_REMOVAL_SECONDS (10 * 60) +#define MASTERNODE_PING_SECONDS (5 * 60) +#define MASTERNODE_EXPIRATION_SECONDS (120 * 60) +#define MASTERNODE_REMOVAL_SECONDS (130 * 60) #define MASTERNODE_CHECK_SECONDS 5 using namespace std; @@ -62,6 +62,7 @@ class CMasternodePing bool CheckAndUpdate(int& nDos, bool fRequireEnabled = true); bool Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode); + bool VerifySignature(CPubKey& pubKeyMasternode, int &nDos); void Relay(); uint256 GetHash() @@ -80,7 +81,6 @@ class CMasternodePing // by swapping the members of two classes, // the two classes are effectively swapped swap(first.vin, second.vin); - swap(first.blockHash, second.blockHash); swap(first.sigTime, second.sigTime); swap(first.vchSig, second.vchSig); @@ -114,10 +114,14 @@ class CMasternode public: enum state { + MASTERNODE_PRE_ENABLED, MASTERNODE_ENABLED, MASTERNODE_EXPIRED, + MASTERNODE_OUTPOINT_SPENT, MASTERNODE_REMOVE, + MASTERNODE_POSE_BAN, MASTERNODE_VIN_SPENT, + MASTERNODE_POS_ERROR }; enum LevelValue : unsigned { @@ -139,11 +143,12 @@ class CMasternode bool unitTest; bool allowFreeTx; int protocolVersion; + int nActiveState; int64_t nLastDsq; //the dsq count from the last dsq broadcast of this node CMasternodePing lastPing; - static unsigned Level(CAmount vin_val); - static unsigned Level(const CTxIn& vin); + static unsigned Level(CAmount vin_val, int blockHeight); + static unsigned Level(const CTxIn& vin, int blockHeight); static bool IsDepositCoins(CAmount); static bool IsDepositCoins(const CTxIn& vin, CAmount& vin_val); @@ -220,6 +225,13 @@ class CMasternode bool UpdateFromNewBroadcast(CMasternodeBroadcast& mnb); + inline uint64_t SliceHash(uint256& hash, int slice) + { + uint64_t n = 0; + memcpy(&n, &hash + slice * 64, 64); + return n; + } + void Check(bool forceCheck = false); bool IsBroadcastedWithin(int seconds) @@ -240,26 +252,25 @@ class CMasternode lastPing = CMasternodePing(); } - bool IsEnabled() const + bool IsEnabled() { return activeState == MASTERNODE_ENABLED; } int GetMasternodeInputAge() { - auto chain_tip = chainActive.Tip(); - - if (!chain_tip) - return 0; + if (chainActive.Tip() == NULL) return 0; - if (!cacheInputAge) { + if (cacheInputAge == 0) { cacheInputAge = GetInputAge(vin); - cacheInputAgeBlock = chain_tip->nHeight; + cacheInputAgeBlock = chainActive.Tip()->nHeight; } - return cacheInputAge + (chain_tip->nHeight - cacheInputAgeBlock); + return cacheInputAge + (chainActive.Tip()->nHeight - cacheInputAgeBlock); } + std::string GetStatus(); + std::string Status() { std::string strStatus = "ACTIVE"; @@ -268,13 +279,14 @@ class CMasternode if (activeState == CMasternode::MASTERNODE_EXPIRED) strStatus = "EXPIRED"; if (activeState == CMasternode::MASTERNODE_VIN_SPENT) strStatus = "VIN_SPENT"; if (activeState == CMasternode::MASTERNODE_REMOVE) strStatus = "REMOVE"; + if (activeState == CMasternode::MASTERNODE_POS_ERROR) strStatus = "POS_ERROR"; return strStatus; } unsigned Level() { - return Level(deposit); + return Level(deposit, chainActive.Height()); } int64_t GetLastPaid(); @@ -296,7 +308,10 @@ class CMasternodeBroadcast : public CMasternode bool CheckAndUpdate(int& nDoS); bool CheckInputsAndAdd(int& nDos); bool Sign(CKey& keyCollateralAddress); + bool VerifySignature(); void Relay(); + std::string GetOldStrMessage(); + std::string GetNewStrMessage(); ADD_SERIALIZE_METHODS; diff --git a/src/masternodeconfig.h b/src/masternodeconfig.h index 59df088..ddd7d98 100644 --- a/src/masternodeconfig.h +++ b/src/masternodeconfig.h @@ -107,7 +107,7 @@ class CMasternodeConfig int getCount() { int c = -1; - BOOST_FOREACH (CMasternodeEntry e, entries) { + for (CMasternodeEntry e : entries) { if (e.getAlias() != "") c++; } return c; diff --git a/src/masternodeman.cpp b/src/masternodeman.cpp index cb93925..92c2a4b 100644 --- a/src/masternodeman.cpp +++ b/src/masternodeman.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -78,8 +78,8 @@ bool CMasternodeDB::Write(const CMasternodeMan& mnodemanToSave) // FileCommit(fileout); fileout.fclose(); - LogPrintf("Written info to mncache.dat %dms\n", GetTimeMillis() - nStart); - LogPrintf(" %s\n", mnodemanToSave.ToString()); + LogPrint("masternode","Written info to mncache.dat %dms\n", GetTimeMillis() - nStart); + LogPrint("masternode"," %s\n", mnodemanToSave.ToString()); return true; } @@ -153,13 +153,13 @@ CMasternodeDB::ReadResult CMasternodeDB::Read(CMasternodeMan& mnodemanToLoad, bo return IncorrectFormat; } - LogPrintf("Loaded info from mncache.dat %dms\n", GetTimeMillis() - nStart); - LogPrintf(" %s\n", mnodemanToLoad.ToString()); + LogPrint("masternode","Loaded info from mncache.dat %dms\n", GetTimeMillis() - nStart); + LogPrint("masternode"," %s\n", mnodemanToLoad.ToString()); if (!fDryRun) { - LogPrintf("Masternode manager - cleaning....\n"); + LogPrint("masternode","Masternode manager - cleaning....\n"); mnodemanToLoad.CheckAndRemove(true); - LogPrintf("Masternode manager - result:\n"); - LogPrintf(" %s\n", mnodemanToLoad.ToString()); + LogPrint("masternode","Masternode manager - result:\n"); + LogPrint("masternode"," %s\n", mnodemanToLoad.ToString()); } return Ok; @@ -172,24 +172,24 @@ void DumpMasternodes() CMasternodeDB mndb; CMasternodeMan tempMnodeman; - LogPrintf("Verifying mncache.dat format...\n"); + LogPrint("masternode","Verifying mncache.dat format...\n"); CMasternodeDB::ReadResult readResult = mndb.Read(tempMnodeman, true); // there was an error and it was not an error on file opening => do not proceed if (readResult == CMasternodeDB::FileError) - LogPrintf("Missing masternode cache file - mncache.dat, will try to recreate\n"); + LogPrint("masternode","Missing masternode cache file - mncache.dat, will try to recreate\n"); else if (readResult != CMasternodeDB::Ok) { - LogPrintf("Error reading mncache.dat: "); + LogPrint("masternode","Error reading mncache.dat: "); if (readResult == CMasternodeDB::IncorrectFormat) - LogPrintf("magic is ok but data has invalid format, will try to recreate\n"); + LogPrint("masternode","magic is ok but data has invalid format, will try to recreate\n"); else { - LogPrintf("file format is unknown or invalid, please fix it manually\n"); + LogPrint("masternode","file format is unknown or invalid, please fix it manually\n"); return; } } - LogPrintf("Writting info to mncache.dat...\n"); + LogPrint("masternode","Writting info to mncache.dat...\n"); mndb.Write(mnodeman); - LogPrintf("Masternode dump finished %dms\n", GetTimeMillis() - nStart); + LogPrint("masternode","Masternode dump finished %dms\n", GetTimeMillis() - nStart); } CMasternodeMan::CMasternodeMan() @@ -210,7 +210,6 @@ CValidationState CMasternodeMan::GetInputCheckingTx(const CTxIn& vin, CMutableTr CMutableTransaction chk_tx; chk_tx.vin.push_back(vin); -// chk_tx.vout.push_back(CTxOut(9999.99 * COIN, obfuScationPool.collateralPubKey)); chk_tx.vout.push_back(CTxOut(deposit - 0.01 * COIN, obfuScationPool.collateralPubKey)); tx = chk_tx; @@ -218,19 +217,21 @@ CValidationState CMasternodeMan::GetInputCheckingTx(const CTxIn& vin, CMutableTr return state; } -bool CMasternodeMan::Add(const CMasternode& mn) +bool CMasternodeMan::Add(CMasternode& mn) { LOCK(cs); - if(!mn.IsEnabled()) + if (!mn.IsEnabled()) return false; - if(Find(mn.vin)) - return false; + CMasternode* pmn = Find(mn.vin); + if (pmn == NULL) { + LogPrint("masternode", "CMasternodeMan: Adding new Masternode %s - %i now\n", mn.vin.prevout.hash.ToString(), size() + 1); + vMasternodes.push_back(mn); + return true; + } - LogPrint("masternode", "CMasternodeMan: Adding new Masternode %s - %i now\n", mn.vin.prevout.hash.ToString(), size() + 1); - vMasternodes.push_back(mn); - return true; + return false; } std::vector CMasternodeMan::GetFullMasternodeMap() @@ -259,7 +260,7 @@ void CMasternodeMan::Check() { LOCK(cs); - BOOST_FOREACH (CMasternode& mn, vMasternodes) { + for (CMasternode& mn : vMasternodes) { mn.Check(); } } @@ -437,7 +438,7 @@ int CMasternodeMan::stable_size(unsigned mnlevel) if(!mn.IsEnabled()) continue; // Skip not-enabled masternodes - ++nStable_size; + nStable_size++; } return nStable_size; @@ -461,15 +462,15 @@ unsigned CMasternodeMan::CountEnabled(unsigned mnlevel, int protocolVersion) }); } -std::map CMasternodeMan::CountEnabledByLevels(int protocolVersion) +std::map CMasternodeMan::CountEnabledByLevels(int protocolVersion) { if(protocolVersion == -1) protocolVersion = masternodePayments.GetMinMasternodePaymentsProto(); - std::map result; + std::map result; for(unsigned l = CMasternode::LevelValue::MIN; l <= CMasternode::LevelValue::MAX; ++l) - result.emplace(l, 0u); + result.emplace(l, 0); for(auto& mn : vMasternodes) { @@ -490,7 +491,7 @@ void CMasternodeMan::CountNetworks(int protocolVersion, int& ipv4, int& ipv6, in { protocolVersion = protocolVersion == -1 ? masternodePayments.GetMinMasternodePaymentsProto() : protocolVersion; - BOOST_FOREACH (CMasternode& mn, vMasternodes) { + for (CMasternode& mn : vMasternodes) { mn.Check(); std::string strHost; int port; @@ -549,14 +550,7 @@ bool CMasternodeMan::WinnersUpdate(CNode* node) } } - auto mn_counts = mnodeman.CountEnabledByLevels(); - - unsigned max_mn_count = 0u; - - for(const auto& count : mn_counts) - max_mn_count = std::max(max_mn_count, count.second); - - node->PushMessage("mnget", max_mn_count); + node->PushMessage("mnget", mnodeman.CountEnabled()); int64_t askAgain = GetTime() + MASTERNODES_DSEG_SECONDS; mWeAskedForWinnerMasternodeList[node->addr] = askAgain; return true; @@ -567,7 +561,7 @@ CMasternode* CMasternodeMan::Find(const CScript& payee) LOCK(cs); CScript payee2; - BOOST_FOREACH (CMasternode& mn, vMasternodes) { + for (CMasternode& mn : vMasternodes) { payee2 = GetScriptForDestination(mn.pubKeyCollateralAddress.GetID()); if (payee2 == payee) return &mn; @@ -579,7 +573,7 @@ CMasternode* CMasternodeMan::Find(const CTxIn& vin) { LOCK(cs); - BOOST_FOREACH (CMasternode& mn, vMasternodes) { + for (CMasternode& mn : vMasternodes) { if (mn.vin.prevout == vin.prevout) return &mn; } @@ -591,7 +585,7 @@ CMasternode* CMasternodeMan::Find(const CPubKey& pubKeyMasternode) { LOCK(cs); - BOOST_FOREACH (CMasternode& mn, vMasternodes) { + for (CMasternode& mn : vMasternodes) { if (mn.pubKeyMasternode == pubKeyMasternode) return &mn; } @@ -617,38 +611,39 @@ CMasternode* CMasternodeMan::GetNextMasternodeInQueueForPayment(int nBlockHeight { LOCK(cs); + CMasternode* pBestMasternode = nullptr; std::vector > vecMasternodeLastPaid; /* Make a vector with all of the last paid times */ - int nMnCount = CountEnabled(mnlevel); + auto nMnCount = CountEnabled(mnlevel); for(CMasternode& mn : vMasternodes) { - if(mn.Level() != mnlevel) - continue; + mn.Check(); - //check protocol version - if(mn.protocolVersion < masternodePayments.GetMinMasternodePaymentsProto()) + if (!mn.IsEnabled()) continue; - mn.Check(); + if(mn.Level() != mnlevel) + continue; - if(!mn.IsEnabled()) + //check protocol version + if (mn.protocolVersion < masternodePayments.GetMinMasternodePaymentsProto()) continue; - //it's in the list -- so let's skip it - if(masternodePayments.IsScheduled(mn, nMnCount, nBlockHeight)) + //it's in the list (up to 8 entries ahead of current block to allow propagation) -- so let's skip it + if (masternodePayments.IsScheduled(mn, nBlockHeight)) continue; //it's too new, wait for a cycle - if(fFilterSigTime && mn.sigTime + (nMnCount * 2.6 * 60) > GetAdjustedTime()) + if (fFilterSigTime && mn.sigTime + (nMnCount * 2.6 * 60) > GetAdjustedTime()) continue; //make sure it has as many confirmations as there are masternodes - if(mn.GetMasternodeInputAge() < nMnCount) + if (mn.GetMasternodeInputAge() < nMnCount) continue; vecMasternodeLastPaid.emplace_back(mn.SecondsSincePayment(), mn.vin); @@ -664,31 +659,24 @@ CMasternode* CMasternodeMan::GetNextMasternodeInQueueForPayment(int nBlockHeight sort(vecMasternodeLastPaid.rbegin(), vecMasternodeLastPaid.rend(), CompareLastPaid()); // Look at 1/10 of the oldest nodes (by last payment), calculate their scores and pay the best one - // -- This doesn't look at who is being paid in the scheduled blocks, allowing for double payments very rarely - - int nCountTenth = nMnCount / 10; + // -- This doesn't look at who is being paid in the +8-10 blocks, allowing for double payments very rarely + // -- 1/100 payments should be a double payment on mainnet - (1/(3000/10))*2 + // -- (chance per block * chances before IsScheduled will fire) + int nTenthNetwork = CountEnabled(mnlevel) / 10; + int nCountTenth = 0; uint256 nHigh = 0; - - CMasternode* pBestMasternode = nullptr; - - for(const auto& s : vecMasternodeLastPaid) { - + BOOST_FOREACH (PAIRTYPE(int64_t, CTxIn) & s, vecMasternodeLastPaid) { CMasternode* pmn = Find(s.second); - - if(!pmn) - continue; + if (!pmn) break; uint256 n = pmn->CalculateScore(1, nBlockHeight - 100); - - if(n > nHigh) { + if (n > nHigh) { nHigh = n; pBestMasternode = pmn; } - - if(--nCountTenth > 0) - break; + nCountTenth++; + if (nCountTenth >= nTenthNetwork) break; } - return pBestMasternode; } @@ -826,13 +814,13 @@ std::vector > CMasternodeMan::GetMasternodeRanks(int64_t if (!GetBlockHash(hash, nBlockHeight)) return vecMasternodeRanks; // scan for winner - BOOST_FOREACH (CMasternode& mn, vMasternodes) { + for (CMasternode& mn : vMasternodes) { mn.Check(); if (mn.protocolVersion < minProtocol) continue; if (!mn.IsEnabled()) { - vecMasternodeScores.push_back(make_pair(9999, mn)); + vecMasternodeScores.push_back(make_pair(40555, mn)); continue; } @@ -858,7 +846,7 @@ CMasternode* CMasternodeMan::GetMasternodeByRank(int nRank, int64_t nBlockHeight std::vector > vecMasternodeScores; // scan for winner - BOOST_FOREACH (CMasternode& mn, vMasternodes) { + for (CMasternode& mn : vMasternodes) { if (mn.protocolVersion < minProtocol) continue; if (fOnlyActive) { mn.Check(); @@ -890,9 +878,9 @@ void CMasternodeMan::ProcessMasternodeConnections() if (Params().NetworkID() == CBaseChainParams::REGTEST) return; LOCK(cs_vNodes); - BOOST_FOREACH (CNode* pnode, vNodes) { + for (CNode* pnode : vNodes) { if (pnode->fObfuScationMaster) { - if (obfuScationPool.pSubmittedToMasternode && pnode->addr == obfuScationPool.pSubmittedToMasternode->addr) + if (obfuScationPool.pSubmittedToMasternode != NULL && pnode->addr == obfuScationPool.pSubmittedToMasternode->addr) continue; LogPrintf("Closing Masternode connection peer=%i \n", pnode->GetId()); @@ -921,7 +909,7 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData if(pmn->IsEnabled()) { - LogPrintf("mnb - More than one vin used for single IP address\n"); + LogPrint("masternode","mnb - More than one vin used for single IP address\n"); Misbehaving(pfrom->GetId(), 100); return; } @@ -946,7 +934,7 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData // make sure the vout that was signed is related to the transaction that spawned the Masternode // - this is expensive, so it's only done once per Masternode if (!obfuScationSigner.IsVinAssociatedWithPubkey(mnb.vin, mnb.pubKeyCollateralAddress)) { - LogPrintf("mnb - Got mismatched pubkey and vin\n"); + LogPrint("masternode","mnb - Got mismatched pubkey and vin\n"); Misbehaving(pfrom->GetId(), 33); return; } @@ -958,7 +946,7 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData addrman.Add(CAddress(mnb.addr), pfrom->addr, 2 * 60 * 60); masternodeSync.AddedMasternodeList(mnb.GetHash()); } else { - LogPrintf("mnb - Rejected Masternode entry %s\n", mnb.vin.prevout.hash.ToString()); + LogPrint("masternode","mnb - Rejected Masternode entry %s\n", mnb.vin.prevout.hash.ToString()); if (nDoS > 0) { Misbehaving(pfrom->GetId(), nDoS); return; @@ -987,7 +975,7 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData // if nothing significant failed, search existing Masternode list CMasternode* pmn = Find(mnp.vin); // if it's known, don't ask for the mnb, just return - if (pmn) return; + if (pmn != NULL) return; } // something significant is broken or mn is unknown, @@ -1023,7 +1011,7 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData int nInvCount = 0; - BOOST_FOREACH (CMasternode& mn, vMasternodes) { + for (CMasternode& mn : vMasternodes) { if (mn.addr.IsRFC1918()) continue; //local network if (mn.IsEnabled()) { @@ -1094,20 +1082,18 @@ void CMasternodeMan::Remove(CTxIn vin) void CMasternodeMan::UpdateMasternodeList(CMasternodeBroadcast mnb) { - LOCK(cs); mapSeenMasternodePing.insert(std::make_pair(mnb.lastPing.GetHash(), mnb.lastPing)); mapSeenMasternodeBroadcast.insert(std::make_pair(mnb.GetHash(), mnb)); + masternodeSync.AddedMasternodeList(mnb.GetHash()); - LogPrintf("CMasternodeMan::UpdateMasternodeList -- masternode=%s addr=%s\n", mnb.vin.prevout.ToStringShort(), mnb.addr.ToString()); + LogPrint("masternode","CMasternodeMan::UpdateMasternodeList -- masternode=%s addr=%s\n", mnb.vin.prevout.ToStringShort(), mnb.addr.ToString()); CMasternode* pmn = Find(mnb.vin); - - if (!pmn) { - if (Add(CMasternode{mnb})) - masternodeSync.AddedMasternodeList(mnb.GetHash()); - - } else if (pmn->UpdateFromNewBroadcast(mnb)) { - masternodeSync.AddedMasternodeList(mnb.GetHash()); + if (pmn == NULL) { + CMasternode mn(mnb); + Add(mn); + } else { + pmn->UpdateFromNewBroadcast(mnb); } } @@ -1115,7 +1101,7 @@ std::string CMasternodeMan::ToString() const { std::ostringstream info; - info << "Masternodes: " << vMasternodes.size() << ", peers who asked us for Masternode list: " << (int)mAskedUsForMasternodeList.size() << ", peers we asked for Masternode list: " << (int)mWeAskedForMasternodeList.size() << ", entries in Masternode list we asked for: " << mWeAskedForMasternodeListEntry.size() << ", nDsqCount: " << nDsqCount; + info << "Masternodes: " << (int)vMasternodes.size() << ", peers who asked us for Masternode list: " << (int)mAskedUsForMasternodeList.size() << ", peers we asked for Masternode list: " << (int)mWeAskedForMasternodeList.size() << ", entries in Masternode list we asked for: " << (int)mWeAskedForMasternodeListEntry.size() << ", nDsqCount: " << (int)nDsqCount; return info.str(); } diff --git a/src/masternodeman.h b/src/masternodeman.h index fb03b4f..4dfd083 100644 --- a/src/masternodeman.h +++ b/src/masternodeman.h @@ -14,6 +14,7 @@ #include "sync.h" #include "util.h" +#define MASTERNODES_DUMP_SECONDS (15 * 60) #define MASTERNODES_DSEG_SECONDS (3 * 60 * 60) using namespace std; @@ -102,7 +103,7 @@ class CMasternodeMan static CValidationState GetInputCheckingTx(const CTxIn& vin, CMutableTransaction&); /// Add an entry - bool Add(const CMasternode& mn); + bool Add(CMasternode& mn); ///return all MN's std::vector GetFullMasternodeMap(); @@ -120,7 +121,7 @@ class CMasternodeMan void Clear(); unsigned CountEnabled(unsigned mnlevel = CMasternode::LevelValue::UNSPECIFIED, int protocolVersion = -1); - std::map CountEnabledByLevels(int protocolVersion = -1); + std::map CountEnabledByLevels(int protocolVersion = -1); void CountNetworks(int protocolVersion, int& ipv4, int& ipv6, int& onion); diff --git a/src/miner.cpp b/src/miner.cpp index c9addb6..a0a5017 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -2,7 +2,7 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -20,9 +20,10 @@ #include "util.h" #include "utilmoneystr.h" #ifdef ENABLE_WALLET -#include "wallet.h" +#include "wallet/wallet.h" #endif #include "masternode-payments.h" +#include "spork.h" #include #include @@ -105,7 +106,12 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, CWallet* pwallet, txNew.vin[0].prevout.SetNull(); txNew.vout.resize(1); txNew.vout[0].SetEmpty(); -// txNew.vout[0].scriptPubKey = scriptPubKeyIn; + + LogPrintf("CreateNewBlock() : chainActive.Height() = %s \n", chainActive.Height()); + if (chainActive.Height() >= Params().LAST_POW_BLOCK()) { + txNew.vout[0].scriptPubKey = scriptPubKeyIn; + + } pblock->vtx.push_back(txNew); @@ -132,7 +138,10 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, CWallet* pwallet, unsigned int nTxNewTime = 0; if (pwallet->CreateCoinStake(*pwallet, pblock->nTime, pblock->nBits, nSearchTime - nLastCoinStakeSearchTime, txCoinStake, nTxNewTime)) { pblock->nTime = nTxNewTime; -// pblock->vtx[0].vout[0].SetEmpty(); + + LogPrintf("CreateNewBlock() if fProofOfStake: chainActive.Height() = %s \n", chainActive.Height()); + pblock->vtx[0].vout[0].SetEmpty(); + pblock->vtx.push_back(CTransaction(txCoinStake)); fStakeFound = true; } @@ -187,7 +196,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, CWallet* pwallet, double dPriority = 0; CAmount nTotalIn = 0; bool fMissingInputs = false; - BOOST_FOREACH (const CTxIn& txin, tx.vin) { + for (const CTxIn& txin : tx.vin) { // Read prev transaction if (!view.HaveCoins(txin.prevout.hash)) { // This should never happen; all transactions in the memory @@ -321,7 +330,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, CWallet* pwallet, // Add transactions that depend on this one to the priority queue if (mapDependers.count(hash)) { - BOOST_FOREACH (COrphan* porphan, mapDependers[hash]) { + for (COrphan* porphan : mapDependers[hash]) { if (!porphan->setDependsOn.empty()) { porphan->setDependsOn.erase(hash); if (porphan->setDependsOn.empty()) { @@ -333,34 +342,29 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, CWallet* pwallet, } } - if (!fProofOfStake) - UpdateTime(pblock, pindexPrev); - - CAmount block_value = GetBlockValue(nHeight, pblock->nTime); - txNew.vin[0].scriptSig = CScript() << nHeight << OP_0; - + CAmount block_value = GetBlockValue(nHeight, pblock->nTime); // Compute final transaction. if (!fProofOfStake) { + txNew.vout[0].nValue = block_value + nFees; txNew.vout[0].scriptPubKey = scriptPubKeyIn; pblocktemplate->vTxFees[0] = -nFees; - } - pblock->vtx[0] = txNew; + pblock->vtx[0] = txNew; - if(nHeight > 1) { // exclude premine + if(nHeight > 1) { // exclude premine - auto reward_tx_idx = fProofOfStake ? 1 : 0; + auto reward_tx_idx = fProofOfStake ? 1 : 0; - CMutableTransaction txReward{pblock->vtx[reward_tx_idx]}; + CMutableTransaction txReward{pblock->vtx[reward_tx_idx]}; - auto reward_out_idx = txReward.vout.size() - 1; + auto reward_out_idx = txReward.vout.size() - 1; // Masternode payments auto mn_reward = masternodePayments.FillBlockPayee(txReward, pblock->nTime, block_value, fProofOfStake); - txReward.vout[reward_out_idx].nValue -= mn_reward; + txReward.vout[reward_out_idx].nValue -= mn_reward; // XDNA fees CScript scriptDevPubKeyIn = CScript{} << Params().xDNADevKey() << OP_CHECKSIG; @@ -376,6 +380,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, CWallet* pwallet, pblock->vtx[reward_tx_idx] = txReward; } + } nLastBlockTx = nBlockTx; nLastBlockSize = nBlockSize; @@ -383,15 +388,20 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, CWallet* pwallet, // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); + if (!fProofOfStake) + UpdateTime(pblock, pindexPrev); pblock->nBits = GetNextWorkRequired(pindexPrev, pblock->nTime); pblock->nNonce = 0; pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); CValidationState state; + LogPrintf("CreateNewBlock() if CValidationState: chainActive.Height() = %s \n", chainActive.Height()); + if (chainActive.Height() < Params().LAST_POW_BLOCK()) { if (!TestBlockValidity(state, *pblock, pindexPrev, false, false)) { LogPrintf("CreateNewBlock() : TestBlockValidity failed\n"); mempool.clear(); return nullptr; + } } } @@ -499,8 +509,15 @@ void BitcoinMiner(CWallet* pwallet, bool fProofOfStake) } //todo: check nTime < GMT: Thursday, 18 August 2016 г., 01:00:00 - while (chainActive.Tip()->nTime < 1471482000 || vNodes.empty() || pwallet->IsLocked() || !fMintableCoins || nReserveBalance >= pwallet->GetBalance() || !masternodeSync.IsSynced()) { + while (vNodes.empty() || pwallet->IsLocked() || !fMintableCoins || (pwallet->GetBalance() > 0 && nReserveBalance >= pwallet->GetBalance()) || !masternodeSync.IsSynced()) { nLastCoinStakeSearchInterval = 0; + if (!fMintableCoins) { + if (GetTime() - nMintableLastCheck > 1 * 60) // 1 minute check time + { + nMintableLastCheck = GetTime(); + fMintableCoins = pwallet->MintableCoins(); + } + } MilliSleep(5000); if (!fGenerateBitcoins && !fProofOfStake) continue; @@ -554,7 +571,6 @@ void BitcoinMiner(CWallet* pwallet, bool fProofOfStake) // // Search // - int64_t nStart = GetTime(); uint256 hashTarget = uint256().SetCompact(pblock->nBits); while (true) { diff --git a/src/net.cpp b/src/net.cpp index b7b3f4d..0cd345c 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2,7 +2,7 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -19,7 +19,9 @@ #include "obfuscation.h" #include "primitives/transaction.h" #include "ui_interface.h" -#include "wallet.h" +#ifdef ENABLE_WALLET +#include "wallet/wallet.h" +#endif // ENABLE_WALLET #ifdef WIN32 #include @@ -78,7 +80,6 @@ bool fListen = true; uint64_t nLocalServices = NODE_NETWORK; CCriticalSection cs_mapLocalHost; map mapLocalHost; -static bool vfReachable[NET_MAX] = {}; static bool vfLimited[NET_MAX] = {}; static CNode* pnodeLocalHost = NULL; uint64_t nLocalHostNonce = 0; @@ -86,6 +87,7 @@ static std::vector vhListenSocket; CAddrMan addrman; int nMaxConnections = 125; bool fAddressesInitialized = false; +std::string strSubVersion; vector vNodes; CCriticalSection cs_vNodes; @@ -238,14 +240,6 @@ void AdvertiseLocal(CNode* pnode) } } -void SetReachable(enum Network net, bool fFlag) -{ - LOCK(cs_mapLocalHost); - vfReachable[net] = fFlag; - if (net == NET_IPV6 && fFlag) - vfReachable[NET_IPV4] = true; -} - // learn a new local address bool AddLocal(const CService& addr, int nScore) { @@ -268,7 +262,6 @@ bool AddLocal(const CService& addr, int nScore) info.nScore = nScore + (fAlready ? 1 : 0); info.nPort = addr.GetPort(); } - SetReachable(addr.GetNetwork()); } return true; @@ -331,7 +324,7 @@ bool IsLocal(const CService& addr) bool IsReachable(enum Network net) { LOCK(cs_mapLocalHost); - return vfReachable[net] && !vfLimited[net]; + return !vfLimited[net]; } /** check whether a given address is in a network we can probably connect to */ @@ -355,16 +348,25 @@ CCriticalSection CNode::cs_totalBytesSent; CNode* FindNode(const CNetAddr& ip) { LOCK(cs_vNodes); - BOOST_FOREACH (CNode* pnode, vNodes) + for (CNode* pnode : vNodes) if ((CNetAddr)pnode->addr == ip) return (pnode); return NULL; } +CNode* FindNode(const CSubNet& subNet) +{ + LOCK(cs_vNodes); + for (CNode* pnode : vNodes) + if (subNet.Match((CNetAddr)pnode->addr)) + return (pnode); + return NULL; +} + CNode* FindNode(const std::string& addrName) { LOCK(cs_vNodes); - BOOST_FOREACH (CNode* pnode, vNodes) + for (CNode* pnode : vNodes) if (pnode->addrName == addrName) return (pnode); return NULL; @@ -373,7 +375,7 @@ CNode* FindNode(const std::string& addrName) CNode* FindNode(const CService& addr) { LOCK(cs_vNodes); - BOOST_FOREACH (CNode* pnode, vNodes) { + for (CNode* pnode : vNodes) { if (Params().NetworkID() == CBaseChainParams::REGTEST) { //if using regtest, just check the IP if ((CNetAddr)pnode->addr == (CNetAddr)addr) @@ -410,7 +412,7 @@ CNode* ConnectNode(CAddress addrConnect, const char* pszDest, bool obfuScationMa pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime) / 3600.0); // Connect - SOCKET hSocket; + SOCKET hSocket = INVALID_SOCKET; bool proxyConnectionFailed = false; if (pszDest ? ConnectSocketByName(addrConnect, hSocket, pszDest, Params().GetDefaultPort(), nConnectTimeout, &proxyConnectionFailed) : ConnectSocket(addrConnect, hSocket, nConnectTimeout, &proxyConnectionFailed)) { @@ -484,16 +486,23 @@ void CNode::PushVersion() else LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), id); PushMessage("version", PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe, - nLocalHostNonce, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector()), nBestHeight, true); + nLocalHostNonce, strSubVersion, nBestHeight, true); } -std::map CNode::setBanned; +banmap_t CNode::setBanned; CCriticalSection CNode::cs_setBanned; +bool CNode::setBannedIsDirty; void CNode::ClearBanned() { - setBanned.clear(); + { + LOCK(cs_setBanned); + setBanned.clear(); + setBannedIsDirty = true; + } + DumpBanlist(); // store banlist to Disk + uiInterface.BannedListChanged(); } bool CNode::IsBanned(CNetAddr ip) @@ -501,27 +510,146 @@ bool CNode::IsBanned(CNetAddr ip) bool fResult = false; { LOCK(cs_setBanned); - std::map::iterator i = setBanned.find(ip); + for (banmap_t::iterator it = setBanned.begin(); it != setBanned.end(); it++) + { + CSubNet subNet = (*it).first; + CBanEntry banEntry = (*it).second; + + if(subNet.Match(ip) && GetTime() < banEntry.nBanUntil) + fResult = true; + } + } + return fResult; +} + +bool CNode::IsBanned(CSubNet subnet) +{ + bool fResult = false; + { + LOCK(cs_setBanned); + banmap_t::iterator i = setBanned.find(subnet); if (i != setBanned.end()) { - int64_t t = (*i).second; - if (GetTime() < t) + CBanEntry banEntry = (*i).second; + if (GetTime() < banEntry.nBanUntil) fResult = true; } } return fResult; } -bool CNode::Ban(const CNetAddr& addr) +void CNode::Ban(const CNetAddr& addr, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) +{ + CSubNet subNet(addr); + Ban(subNet, banReason, bantimeoffset, sinceUnixEpoch); +} + +void CNode::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) +{ + CBanEntry banEntry(GetTime()); + banEntry.banReason = banReason; + if (bantimeoffset <= 0) + { + bantimeoffset = GetArg("-bantime", 60*60*24); // Default 24-hour ban + sinceUnixEpoch = false; + } + banEntry.nBanUntil = (sinceUnixEpoch ? 0 : GetTime() )+bantimeoffset; + + { + LOCK(cs_setBanned); + if (setBanned[subNet].nBanUntil < banEntry.nBanUntil) { + setBanned[subNet] = banEntry; + setBannedIsDirty = true; + } + else + return; + } + uiInterface.BannedListChanged(); + { + LOCK(cs_vNodes); + for (CNode* pnode : vNodes) { + if (subNet.Match((CNetAddr)pnode->addr)) + pnode->fDisconnect = true; + } + } + if(banReason == BanReasonManuallyAdded) + DumpBanlist(); //store banlist to disk immediately if user requested ban +} + +bool CNode::Unban(const CNetAddr &addr) +{ + CSubNet subNet(addr); + return Unban(subNet); +} + +bool CNode::Unban(const CSubNet &subNet) { - int64_t banTime = GetTime() + GetArg("-bantime", 60 * 60 * 24); // Default 24-hour ban { LOCK(cs_setBanned); - if (setBanned[addr] < banTime) - setBanned[addr] = banTime; + if (!setBanned.erase(subNet)) + return false; + setBannedIsDirty = true; } + uiInterface.BannedListChanged(); + DumpBanlist(); //store banlist to disk immediately return true; } +void CNode::GetBanned(banmap_t &banMap) +{ + LOCK(cs_setBanned); + // Sweep the banlist so expired bans are not returned + SweepBanned(); + banMap = setBanned; //create a thread safe copy +} + +void CNode::SetBanned(const banmap_t &banMap) +{ + LOCK(cs_setBanned); + setBanned = banMap; + setBannedIsDirty = true; +} + +void CNode::SweepBanned() +{ + int64_t now = GetTime(); + + bool notifyUI = false; + { + LOCK(cs_setBanned); + banmap_t::iterator it = setBanned.begin(); + while(it != setBanned.end()) + { + CSubNet subNet = (*it).first; + CBanEntry banEntry = (*it).second; + if(now > banEntry.nBanUntil) + { + setBanned.erase(it++); + setBannedIsDirty = true; + notifyUI = true; + LogPrint("net", "%s: Removed banned node ip/subnet from banlist.dat: %s\n", __func__, subNet.ToString()); + } + else + ++it; + } + } + // update UI + if(notifyUI) { + uiInterface.BannedListChanged(); + } +} + +bool CNode::BannedSetIsDirty() +{ + LOCK(cs_setBanned); + return setBannedIsDirty; +} + +void CNode::SetBannedSetDirty(bool dirty) +{ + LOCK(cs_setBanned); //reuse setBanned lock for the isDirty flag + setBannedIsDirty = dirty; +} + std::vector CNode::vWhitelistedRange; CCriticalSection CNode::cs_vWhitelistedRange; @@ -529,7 +657,7 @@ CCriticalSection CNode::cs_vWhitelistedRange; bool CNode::IsWhitelistedRange(const CNetAddr& addr) { LOCK(cs_vWhitelistedRange); - BOOST_FOREACH (const CSubNet& subnet, vWhitelistedRange) { + for (const CSubNet& subnet : vWhitelistedRange) { if (subnet.Match(addr)) return true; } @@ -551,6 +679,7 @@ void CNode::copyStats(CNodeStats& stats) X(nLastSend); X(nLastRecv); X(nTimeConnected); + X(nTimeOffset); X(addrName); X(nVersion); X(cleanSubVer); @@ -658,12 +787,25 @@ int CNetMessage::readData(const char* pch, unsigned int nBytes) vRecv.resize(std::min(hdr.nMessageSize, nDataPos + nCopy + 256 * 1024)); } + hasher.Write((const unsigned char*)pch, nCopy); memcpy(&vRecv[nDataPos], pch, nCopy); nDataPos += nCopy; return nCopy; } +const uint256& CNetMessage::GetMessageHash() const +{ + assert(complete()); + if (data_hash.IsNull()) + hasher.Finalize(data_hash.begin()); + return data_hash; +} + + + + + // requires LOCK(cs_vSend) void SocketSendData(CNode* pnode) @@ -721,7 +863,7 @@ void ThreadSocketHandler() LOCK(cs_vNodes); // Disconnect unused nodes vector vNodesCopy = vNodes; - BOOST_FOREACH (CNode* pnode, vNodesCopy) { + for (CNode* pnode : vNodesCopy) { if (pnode->fDisconnect || (pnode->GetRefCount() <= 0 && pnode->vRecvMsg.empty() && pnode->nSendSize == 0 && pnode->ssSend.empty())) { // remove from vNodes @@ -743,7 +885,7 @@ void ThreadSocketHandler() { // Delete disconnected nodes list vNodesDisconnectedCopy = vNodesDisconnected; - BOOST_FOREACH (CNode* pnode, vNodesDisconnectedCopy) { + for (CNode* pnode : vNodesDisconnectedCopy) { // wait until threads are done using it if (pnode->GetRefCount() <= 0) { bool fDelete = false; @@ -791,7 +933,7 @@ void ThreadSocketHandler() SOCKET hSocketMax = 0; bool have_fds = false; - BOOST_FOREACH (const ListenSocket& hListenSocket, vhListenSocket) { + for (const ListenSocket& hListenSocket : vhListenSocket) { FD_SET(hListenSocket.socket, &fdsetRecv); hSocketMax = max(hSocketMax, hListenSocket.socket); have_fds = true; @@ -799,7 +941,7 @@ void ThreadSocketHandler() { LOCK(cs_vNodes); - BOOST_FOREACH (CNode* pnode, vNodes) { + for (CNode* pnode : vNodes) { if (pnode->hSocket == INVALID_SOCKET) continue; FD_SET(pnode->hSocket, &fdsetError); @@ -856,7 +998,7 @@ void ThreadSocketHandler() // // Accept new connections // - BOOST_FOREACH (const ListenSocket& hListenSocket, vhListenSocket) { + for (const ListenSocket& hListenSocket : vhListenSocket) { if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv)) { struct sockaddr_storage sockaddr; socklen_t len = sizeof(sockaddr); @@ -871,7 +1013,7 @@ void ThreadSocketHandler() bool whitelisted = hListenSocket.whitelisted || CNode::IsWhitelistedRange(addr); { LOCK(cs_vNodes); - BOOST_FOREACH (CNode* pnode, vNodes) + for (CNode* pnode : vNodes) if (pnode->fInbound) nInbound++; } @@ -909,10 +1051,10 @@ void ThreadSocketHandler() { LOCK(cs_vNodes); vNodesCopy = vNodes; - BOOST_FOREACH (CNode* pnode, vNodesCopy) + for (CNode* pnode : vNodesCopy) pnode->AddRef(); } - BOOST_FOREACH (CNode* pnode, vNodesCopy) { + for (CNode* pnode : vNodesCopy) { boost::this_thread::interruption_point(); // @@ -984,7 +1126,7 @@ void ThreadSocketHandler() } { LOCK(cs_vNodes); - BOOST_FOREACH (CNode* pnode, vNodesCopy) + for (CNode* pnode : vNodesCopy) pnode->Release(); } } @@ -1119,14 +1261,14 @@ void ThreadDNSAddressSeed() LogPrintf("Loading addresses from DNS seeds (could take a while)\n"); - BOOST_FOREACH (const CDNSSeedData& seed, vSeeds) { + for (const CDNSSeedData& seed : vSeeds) { if (HaveNameProxy()) { AddOneShot(seed.host); } else { vector vIPs; vector vAdd; if (LookupHost(seed.host.c_str(), vIPs)) { - BOOST_FOREACH (CNetAddr& ip, vIPs) { + for (CNetAddr& ip : vIPs) { int nOneDay = 24 * 3600; CAddress addr = CAddress(CService(ip, Params().GetDefaultPort())); addr.nTime = GetTime() - 3 * nOneDay - GetRand(4 * nOneDay); // use a random age between 3 and 7 days old @@ -1153,6 +1295,12 @@ void DumpAddresses() addrman.size(), GetTimeMillis() - nStart); } +void DumpData() +{ + DumpAddresses(); + DumpBanlist(); +} + void static ProcessOneShot() { string strDest; @@ -1177,7 +1325,7 @@ void ThreadOpenConnections() if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) { for (int64_t nLoop = 0;; nLoop++) { ProcessOneShot(); - BOOST_FOREACH (string strAddr, mapMultiArgs["-connect"]) { + for (string strAddr : mapMultiArgs["-connect"]) { CAddress addr; OpenNetworkConnection(addr, NULL, strAddr.c_str()); for (int i = 0; i < 10 && i < nLoop; i++) { @@ -1219,7 +1367,7 @@ void ThreadOpenConnections() set > setConnected; { LOCK(cs_vNodes); - BOOST_FOREACH (CNode* pnode, vNodes) { + for (CNode* pnode : vNodes) { if (!pnode->fInbound) { setConnected.insert(pnode->addr.GetGroup()); nOutbound++; @@ -1279,10 +1427,10 @@ void ThreadOpenAddedConnections() list lAddresses(0); { LOCK(cs_vAddedNodes); - BOOST_FOREACH (string& strAddNode, vAddedNodes) + for (string& strAddNode : vAddedNodes) lAddresses.push_back(strAddNode); } - BOOST_FOREACH (string& strAddNode, lAddresses) { + for (string& strAddNode : lAddresses) { CAddress addr; CSemaphoreGrant grant(*semOutbound); OpenNetworkConnection(addr, &grant, strAddNode.c_str()); @@ -1296,18 +1444,18 @@ void ThreadOpenAddedConnections() list lAddresses(0); { LOCK(cs_vAddedNodes); - BOOST_FOREACH (string& strAddNode, vAddedNodes) + for (string& strAddNode : vAddedNodes) lAddresses.push_back(strAddNode); } list > lservAddressesToAdd(0); - BOOST_FOREACH (string& strAddNode, lAddresses) { + for (string& strAddNode : lAddresses) { vector vservNode(0); if (Lookup(strAddNode.c_str(), vservNode, Params().GetDefaultPort(), fNameLookup, 0)) { lservAddressesToAdd.push_back(vservNode); { LOCK(cs_setservAddNodeAddresses); - BOOST_FOREACH (CService& serv, vservNode) + for (CService& serv : vservNode) setservAddNodeAddresses.insert(serv); } } @@ -1316,16 +1464,16 @@ void ThreadOpenAddedConnections() // (keeping in mind that addnode entries can have many IPs if fNameLookup) { LOCK(cs_vNodes); - BOOST_FOREACH (CNode* pnode, vNodes) + for (CNode* pnode : vNodes) for (list >::iterator it = lservAddressesToAdd.begin(); it != lservAddressesToAdd.end(); it++) - BOOST_FOREACH (CService& addrNode, *(it)) + for (CService& addrNode : *(it)) if (pnode->addr == addrNode) { it = lservAddressesToAdd.erase(it); it--; break; } } - BOOST_FOREACH (vector& vserv, lservAddressesToAdd) { + for (vector& vserv : lservAddressesToAdd) { CSemaphoreGrant grant(*semOutbound); OpenNetworkConnection(CAddress(vserv[i % vserv.size()]), &grant); MilliSleep(500); @@ -1415,9 +1563,7 @@ void ThreadMessageHandler() // Send messages { TRY_LOCK(pnode->cs_vSend, lockSend); - - if(lockSend) { - + if (lockSend) { g_signals.SendMessages(pnode, pnode == pnodeTrickle || pnode->fWhitelisted); if(performRebroadcast) { @@ -1426,6 +1572,8 @@ void ThreadMessageHandler() if (nLastRebroadcast) pnode->setAddrKnown.clear(); + // Logging from quato + LogPrintf("Rebroadcast our address with AdvertiseLocal\n"); // Rebroadcast our address AdvertiseLocal(pnode); @@ -1436,6 +1584,7 @@ void ThreadMessageHandler() boost::this_thread::interruption_point(); } + { LOCK(cs_vNodes); for (CNode* pnode : vNodesCopy) @@ -1447,6 +1596,7 @@ void ThreadMessageHandler() } } +#ifdef ENABLE_WALLET // ppcoin: stake minter thread void static ThreadStakeMinter() { @@ -1463,6 +1613,7 @@ void static ThreadStakeMinter() } LogPrintf("ThreadStakeMinter exiting,\n"); } +#endif // ENABLE_WALLET bool BindListenPort(const CService& addrBind, string& strError, bool fWhitelisted) { @@ -1563,7 +1714,7 @@ void static Discover(boost::thread_group& threadGroup) if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) { vector vaddr; if (LookupHost(pszHostName, vaddr)) { - BOOST_FOREACH (const CNetAddr& addr, vaddr) { + for (const CNetAddr& addr : vaddr) { if (AddLocal(addr, LOCAL_IF)) LogPrintf("%s: %s - %s\n", __func__, pszHostName, addr.ToString()); } @@ -1605,6 +1756,21 @@ void StartNode(boost::thread_group& threadGroup) if (!adb.Read(addrman)) LogPrintf("Invalid or missing peers.dat; recreating\n"); } + + //try to read stored banlist + CBanDB bandb; + banmap_t banmap; + if (!bandb.Read(banmap)) + LogPrintf("Invalid or missing banlist.dat; recreating\n"); + + CNode::SetBanned(banmap); //thread save setter + CNode::SetBannedSetDirty(false); //no need to write down just read or nonexistent data + CNode::SweepBanned(); //sweap out unused entries + + // Initialize random numbers. Even when rand() is only usable for trivial use-cases most nodes should have a different + // seed after all the file-IO done at this point. Should be good enough even when nodes are started via scripts. + srand(time(NULL)); + LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman.size(), GetTimeMillis() - nStart); fAddressesInitialized = true; @@ -1647,9 +1813,11 @@ void StartNode(boost::thread_group& threadGroup) // Dump network addresses threadGroup.create_thread(boost::bind(&LoopForever, "dumpaddr", &DumpAddresses, DUMP_ADDRESSES_INTERVAL * 1000)); +#ifdef ENABLE_WALLET // ppcoin:mint proof-of-stake blocks in the background - if (GetBoolArg("-staking", true)) + if (GetBoolArg("-staking", true) && pwalletMain) threadGroup.create_thread(boost::bind(&TraceThread, "stakemint", &ThreadStakeMinter)); +#endif // ENABLE_WALLET } bool StopNode() @@ -1661,7 +1829,7 @@ bool StopNode() semOutbound->post(); if (fAddressesInitialized) { - DumpAddresses(); + DumpData(); fAddressesInitialized = false; } @@ -1676,18 +1844,18 @@ class CNetCleanup ~CNetCleanup() { // Close sockets - BOOST_FOREACH (CNode* pnode, vNodes) + for (CNode* pnode : vNodes) if (pnode->hSocket != INVALID_SOCKET) CloseSocket(pnode->hSocket); - BOOST_FOREACH (ListenSocket& hListenSocket, vhListenSocket) + for (ListenSocket& hListenSocket : vhListenSocket) if (hListenSocket.socket != INVALID_SOCKET) if (!CloseSocket(hListenSocket.socket)) LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError())); // clean up some globals (to help leak detection) - BOOST_FOREACH (CNode* pnode, vNodes) + for (CNode* pnode : vNodes) delete pnode; - BOOST_FOREACH (CNode* pnode, vNodesDisconnected) + for (CNode* pnode : vNodesDisconnected) delete pnode; vNodes.clear(); vNodesDisconnected.clear(); @@ -1736,7 +1904,7 @@ void RelayTransaction(const CTransaction& tx, const CDataStream& ss) vRelayExpiration.push_back(std::make_pair(GetTime() + 15 * 60, inv)); } LOCK(cs_vNodes); - BOOST_FOREACH (CNode* pnode, vNodes) { + for (CNode* pnode : vNodes) { if (!pnode->fRelayTxes) continue; LOCK(pnode->cs_filter); @@ -1754,7 +1922,7 @@ void RelayTransactionLockReq(const CTransaction& tx, bool relayToAll) //broadcast the new lock LOCK(cs_vNodes); - BOOST_FOREACH (CNode* pnode, vNodes) { + for (CNode* pnode : vNodes) { if (!relayToAll && !pnode->fRelayTxes) continue; @@ -1765,8 +1933,7 @@ void RelayTransactionLockReq(const CTransaction& tx, bool relayToAll) void RelayInv(CInv& inv) { LOCK(cs_vNodes); - BOOST_FOREACH (CNode* pnode, vNodes){ - if((pnode->nServices==NODE_BLOOM_WITHOUT_MN) && inv.IsMasterNodeType())continue; + for (CNode* pnode : vNodes){ if (pnode->nVersion >= ActiveProtocol()) pnode->PushInventory(inv); } @@ -1796,40 +1963,6 @@ uint64_t CNode::GetTotalBytesSent() return nTotalBytesSent; } -void CNode::Fuzz(int nChance) -{ - if (!fSuccessfullyConnected) return; // Don't fuzz initial handshake - if (GetRand(nChance) != 0) return; // Fuzz 1 of every nChance messages - - switch (GetRand(3)) { - case 0: - // xor a random byte with a random value: - if (!ssSend.empty()) { - CDataStream::size_type pos = GetRand(ssSend.size()); - ssSend[pos] ^= (unsigned char)(GetRand(256)); - } - break; - case 1: - // delete a random byte: - if (!ssSend.empty()) { - CDataStream::size_type pos = GetRand(ssSend.size()); - ssSend.erase(ssSend.begin() + pos); - } - break; - case 2: - // insert a random byte at a random position - { - CDataStream::size_type pos = GetRand(ssSend.size()); - char ch = (char)GetRand(256); - ssSend.insert(ssSend.begin() + pos, ch); - } - break; - } - // Chance of more than one change half the time: - // (more changes exponentially less likely): - Fuzz(2); -} - // // CAddrDB // @@ -1881,11 +2014,11 @@ bool CAddrDB::Read(CAddrMan& addr) return error("%s : Failed to open file %s", __func__, pathAddr.string()); // use file size to size memory buffer - int fileSize = boost::filesystem::file_size(pathAddr); - int dataSize = fileSize - sizeof(uint256); + uint64_t fileSize = boost::filesystem::file_size(pathAddr); + uint64_t dataSize = fileSize - sizeof(uint256); // Don't try to resize to a negative number if file is small - if (dataSize < 0) - dataSize = 0; + if (fileSize >= sizeof(uint256)) + dataSize = fileSize - sizeof(uint256); vector vchData; vchData.resize(dataSize); uint256 hashIn; @@ -1937,6 +2070,7 @@ CNode::CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn, bool fIn nSendBytes = 0; nRecvBytes = 0; nTimeConnected = GetTime(); + nTimeOffset = 0; addr = addrIn; addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; nVersion = 0; @@ -2047,11 +2181,11 @@ void CNode::EndMessage() UNLOCK_FUNCTION(cs_vSend) AbortMessage(); return; } - if (mapArgs.count("-fuzzmessagestest")) - Fuzz(GetArg("-fuzzmessagestest", 10)); - if (ssSend.size() == 0) + if (ssSend.size() == 0) { + LEAVE_CRITICAL_SECTION(cs_vSend); return; + } // Set the size unsigned int nSize = ssSend.size() - CMessageHeader::HEADER_SIZE; @@ -2076,3 +2210,124 @@ void CNode::EndMessage() UNLOCK_FUNCTION(cs_vSend) LEAVE_CRITICAL_SECTION(cs_vSend); } + +// +// CBanDB +// + +CBanDB::CBanDB() +{ + pathBanlist = GetDataDir() / "banlist.dat"; +} + +bool CBanDB::Write(const banmap_t& banSet) +{ + // Generate random temporary filename + unsigned short randv = 0; + GetRandBytes((unsigned char*)&randv, sizeof(randv)); + std::string tmpfn = strprintf("banlist.dat.%04x", randv); + + // serialize banlist, checksum data up to that point, then append csum + CDataStream ssBanlist(SER_DISK, CLIENT_VERSION); + ssBanlist << FLATDATA(Params().MessageStart()); + ssBanlist << banSet; + uint256 hash = Hash(ssBanlist.begin(), ssBanlist.end()); + ssBanlist << hash; + + // open temp output file, and associate with CAutoFile + boost::filesystem::path pathTmp = GetDataDir() / tmpfn; + FILE *file = fopen(pathTmp.string().c_str(), "wb"); + CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); + if (fileout.IsNull()) + return error("%s: Failed to open file %s", __func__, pathTmp.string()); + + // Write and commit header, data + try { + fileout << ssBanlist; + } + catch (const std::exception& e) { + return error("%s: Serialize or I/O error - %s", __func__, e.what()); + } + FileCommit(fileout.Get()); + fileout.fclose(); + + // replace existing banlist.dat, if any, with new banlist.dat.XXXX + if (!RenameOver(pathTmp, pathBanlist)) + return error("%s: Rename-into-place failed", __func__); + + return true; +} + +bool CBanDB::Read(banmap_t& banSet) +{ + // open input file, and associate with CAutoFile + FILE *file = fopen(pathBanlist.string().c_str(), "rb"); + CAutoFile filein(file, SER_DISK, CLIENT_VERSION); + if (filein.IsNull()) + return error("%s: Failed to open file %s", __func__, pathBanlist.string()); + + // use file size to size memory buffer + uint64_t fileSize = boost::filesystem::file_size(pathBanlist); + uint64_t dataSize = 0; + // Don't try to resize to a negative number if file is small + if (fileSize >= sizeof(uint256)) + dataSize = fileSize - sizeof(uint256); + vector vchData; + vchData.resize(dataSize); + uint256 hashIn; + + // read data and checksum from file + try { + filein.read((char *)&vchData[0], dataSize); + filein >> hashIn; + } + catch (const std::exception& e) { + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + } + filein.fclose(); + + CDataStream ssBanlist(vchData, SER_DISK, CLIENT_VERSION); + + // verify stored checksum matches input data + uint256 hashTmp = Hash(ssBanlist.begin(), ssBanlist.end()); + if (hashIn != hashTmp) + return error("%s: Checksum mismatch, data corrupted", __func__); + + unsigned char pchMsgTmp[4]; + try { + // de-serialize file header (network specific magic number) and .. + ssBanlist >> FLATDATA(pchMsgTmp); + + // ... verify the network matches ours + if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) + return error("%s: Invalid network magic number", __func__); + + // de-serialize address data into one CAddrMan object + ssBanlist >> banSet; + } + catch (const std::exception& e) { + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + } + + return true; +} + +void DumpBanlist() +{ + CNode::SweepBanned(); // clean unused entries (if bantime has expired) + + if (!CNode::BannedSetIsDirty()) + return; + + int64_t nStart = GetTimeMillis(); + + CBanDB bandb; + banmap_t banmap; + CNode::GetBanned(banmap); + if (bandb.Write(banmap)) { + CNode::SetBannedSetDirty(false); + } + + LogPrint("net", "Flushed %d banned node ips/subnets to banlist.dat %dms\n", + banmap.size(), GetTimeMillis() - nStart); +} diff --git a/src/net.h b/src/net.h index 159e05a..2fe1489 100644 --- a/src/net.h +++ b/src/net.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -51,6 +51,8 @@ static const unsigned int MAX_INV_SZ = 50000; static const unsigned int MAX_ADDR_TO_SEND = 1000; /** Maximum length of incoming protocol messages (no message over 2 MiB is currently acceptable). */ static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 2 * 1024 * 1024; +/** Maximum length of strSubVer in `version` message */ +static const unsigned int MAX_SUBVERSION_LENGTH = 256; /** -listen default */ static const bool DEFAULT_LISTEN = true; /** -upnp default */ @@ -69,6 +71,7 @@ void AddOneShot(std::string strDest); bool RecvLine(SOCKET hSocket, std::string& strLine); void AddressCurrentlyConnected(const CService& addr); CNode* FindNode(const CNetAddr& ip); +CNode* FindNode(const CSubNet& subNet); CNode* FindNode(const std::string& addrName); CNode* FindNode(const CService& ip); CNode* ConnectNode(CAddress addrConnect, const char* pszDest = NULL, bool obfuScationMaster = false); @@ -118,7 +121,6 @@ bool IsLocal(const CService& addr); bool GetLocal(CService& addr, const CNetAddr* paddrPeer = NULL); bool IsReachable(enum Network net); bool IsReachable(const CNetAddr& addr); -void SetReachable(enum Network net, bool fFlag = true); CAddress GetLocalAddress(const CNetAddr* paddrPeer = NULL); @@ -142,6 +144,9 @@ extern CCriticalSection cs_vAddedNodes; extern NodeId nLastNodeId; extern CCriticalSection cs_nLastNodeId; +/** Subversion as sent to the P2P network in `version` messages */ +extern std::string strSubVersion; + struct LocalServiceInfo { int nScore; int nPort; @@ -158,6 +163,7 @@ class CNodeStats int64_t nLastSend; int64_t nLastRecv; int64_t nTimeConnected; + int64_t nTimeOffset; std::string addrName; int nVersion; std::string cleanSubVer; @@ -174,6 +180,10 @@ class CNodeStats class CNetMessage { +private: + mutable CHash256 hasher; + mutable uint256 data_hash; + public: bool in_data; // parsing header (false) or data (true) @@ -202,6 +212,8 @@ class CNetMessage return (hdr.nMessageSize == nDataPos); } + const uint256& GetMessageHash() const; + void SetVersion(int nVersionIn) { hdrbuf.SetVersion(nVersionIn); @@ -212,6 +224,66 @@ class CNetMessage int readData(const char* pch, unsigned int nBytes); }; +typedef enum BanReason +{ + BanReasonUnknown = 0, + BanReasonNodeMisbehaving = 1, + BanReasonManuallyAdded = 2 +} BanReason; + +class CBanEntry +{ +public: + static const int CURRENT_VERSION=1; + int nVersion; + int64_t nCreateTime; + int64_t nBanUntil; + uint8_t banReason; + + CBanEntry() + { + SetNull(); + } + + CBanEntry(int64_t nCreateTimeIn) + { + SetNull(); + nCreateTime = nCreateTimeIn; + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(nCreateTime); + READWRITE(nBanUntil); + READWRITE(banReason); + } + + void SetNull() + { + nVersion = CBanEntry::CURRENT_VERSION; + nCreateTime = 0; + nBanUntil = 0; + banReason = BanReasonUnknown; + } + + std::string banReasonToString() + { + switch (banReason) { + case BanReasonNodeMisbehaving: + return "node misbehaving"; + case BanReasonManuallyAdded: + return "manually added"; + default: + return "unknown"; + } + } +}; + +typedef std::map banmap_t; /** Information about a peer */ class CNode @@ -236,6 +308,7 @@ class CNode int64_t nLastSend; int64_t nLastRecv; int64_t nTimeConnected; + int64_t nTimeOffset; CAddress addr; std::string addrName; CService addrLocal; @@ -272,8 +345,9 @@ class CNode protected: // Denial-of-service detection/prevention // Key is IP address, value is banned-until-time - static std::map setBanned; + static banmap_t setBanned; static CCriticalSection cs_setBanned; + static bool setBannedIsDirty; std::vector vecRequestsFulfilled; //keep track of what client has asked for @@ -341,7 +415,7 @@ class CNode unsigned int GetTotalRecvSize() { unsigned int total = 0; - BOOST_FOREACH (const CNetMessage& msg, vRecvMsg) + for (const CNetMessage& msg : vRecvMsg) total += msg.vRecv.size() + 24; return total; } @@ -353,7 +427,7 @@ class CNode void SetRecvVersion(int nVersionIn) { nRecvVersion = nVersionIn; - BOOST_FOREACH (CNetMessage& msg, vRecvMsg) + for (CNetMessage& msg : vRecvMsg) msg.SetVersion(nVersionIn); } @@ -589,7 +663,7 @@ class CNode bool HasFulfilledRequest(std::string strRequest) { - BOOST_FOREACH (std::string& type, vecRequestsFulfilled) { + for (std::string& type : vecRequestsFulfilled) { if (type == strRequest) return true; } return false; @@ -635,7 +709,21 @@ class CNode // new code. static void ClearBanned(); // needed for unit testing static bool IsBanned(CNetAddr ip); - static bool Ban(const CNetAddr& ip); + static bool IsBanned(CSubNet subnet); + static void Ban(const CNetAddr &ip, const BanReason &banReason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); + static void Ban(const CSubNet &subNet, const BanReason &banReason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); + static bool Unban(const CNetAddr &ip); + static bool Unban(const CSubNet &ip); + static void GetBanned(banmap_t &banmap); + static void SetBanned(const banmap_t &banmap); + + //!check is the banlist has unwritten changes + static bool BannedSetIsDirty(); + //!set the "dirty" flag for the banlist + static void SetBannedSetDirty(bool dirty=true); + //!clean unused entires (if bantime has expired) + static void SweepBanned(); + void copyStats(CNodeStats& stats); static bool IsWhitelistedRange(const CNetAddr& ip); @@ -673,4 +761,17 @@ class CAddrDB bool Read(CAddrMan& addr); }; +/** Access to the banlist database (banlist.dat) */ +class CBanDB +{ +private: + boost::filesystem::path pathBanlist; +public: + CBanDB(); + bool Write(const banmap_t& banSet); + bool Read(banmap_t& banSet); +}; + +void DumpBanlist(); + #endif // BITCOIN_NET_H diff --git a/src/netbase.cpp b/src/netbase.cpp index 115e269..ba4daa3 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2015-2017 The PIVX Core developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -1306,6 +1306,13 @@ CSubNet::CSubNet(const std::string& strSubnet, bool fAllowLookup) network.ip[x] &= netmask[x]; } +CSubNet::CSubNet(const CNetAddr &addr): + valid(addr.IsValid()) +{ + memset(netmask, 255, sizeof(netmask)); + network = addr; +} + bool CSubNet::Match(const CNetAddr& addr) const { if (!valid || !addr.IsValid()) @@ -1316,17 +1323,57 @@ bool CSubNet::Match(const CNetAddr& addr) const return true; } +static inline int NetmaskBits(uint8_t x) +{ + switch(x) { + case 0x00: return 0; break; + case 0x80: return 1; break; + case 0xc0: return 2; break; + case 0xe0: return 3; break; + case 0xf0: return 4; break; + case 0xf8: return 5; break; + case 0xfc: return 6; break; + case 0xfe: return 7; break; + case 0xff: return 8; break; + default: return -1; break; + } +} + std::string CSubNet::ToString() const { + /* Parse binary 1{n}0{N-n} to see if mask can be represented as /n */ + int cidr = 0; + bool valid_cidr = true; + int n = network.IsIPv4() ? 12 : 0; + for (; n < 16 && netmask[n] == 0xff; ++n) + cidr += 8; + if (n < 16) { + int bits = NetmaskBits(netmask[n]); + if (bits < 0) + valid_cidr = false; + else + cidr += bits; + ++n; + } + for (; n < 16 && valid_cidr; ++n) + if (netmask[n] != 0x00) + valid_cidr = false; + + /* Format output */ std::string strNetmask; - if (network.IsIPv4()) - strNetmask = strprintf("%u.%u.%u.%u", netmask[12], netmask[13], netmask[14], netmask[15]); - else - strNetmask = strprintf("%x:%x:%x:%x:%x:%x:%x:%x", - netmask[0] << 8 | netmask[1], netmask[2] << 8 | netmask[3], - netmask[4] << 8 | netmask[5], netmask[6] << 8 | netmask[7], - netmask[8] << 8 | netmask[9], netmask[10] << 8 | netmask[11], - netmask[12] << 8 | netmask[13], netmask[14] << 8 | netmask[15]); + if (valid_cidr) { + strNetmask = strprintf("%u", cidr); + } else { + if (network.IsIPv4()) + strNetmask = strprintf("%u.%u.%u.%u", netmask[12], netmask[13], netmask[14], netmask[15]); + else + strNetmask = strprintf("%x:%x:%x:%x:%x:%x:%x:%x", + netmask[0] << 8 | netmask[1], netmask[2] << 8 | netmask[3], + netmask[4] << 8 | netmask[5], netmask[6] << 8 | netmask[7], + netmask[8] << 8 | netmask[9], netmask[10] << 8 | netmask[11], + netmask[12] << 8 | netmask[13], netmask[14] << 8 | netmask[15]); + } + return network.ToString() + "/" + strNetmask; } @@ -1345,6 +1392,11 @@ bool operator!=(const CSubNet& a, const CSubNet& b) return !(a == b); } +bool operator<(const CSubNet& a, const CSubNet& b) +{ + return (a.network < b.network || (a.network == b.network && memcmp(a.netmask, b.netmask, 16) < 0)); +} + #ifdef WIN32 std::string NetworkErrorString(int err) { diff --git a/src/netbase.h b/src/netbase.h index deced7af..74cfb76 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2015-2017 The PIVX Core developers // Copyright (c) 2017-2018 The Bulwark developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -123,6 +123,9 @@ class CSubNet CSubNet(); explicit CSubNet(const std::string& strSubnet, bool fAllowLookup = false); + //constructor for single ip subnet (/32 or /128) + explicit CSubNet(const CNetAddr &addr); + bool Match(const CNetAddr& addr) const; std::string ToString() const; @@ -130,6 +133,16 @@ class CSubNet friend bool operator==(const CSubNet& a, const CSubNet& b); friend bool operator!=(const CSubNet& a, const CSubNet& b); + friend bool operator<(const CSubNet& a, const CSubNet& b); + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(network); + READWRITE(FLATDATA(netmask)); + READWRITE(FLATDATA(valid)); + } }; /** A combination of a network address (CNetAddr) and a (TCP) port */ diff --git a/src/noui.cpp b/src/noui.cpp index 18bb919..173d1e3 100644 --- a/src/noui.cpp +++ b/src/noui.cpp @@ -2,7 +2,7 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/obfuscation.cpp b/src/obfuscation.cpp index f862ad3..49fd378 100644 --- a/src/obfuscation.cpp +++ b/src/obfuscation.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -133,7 +133,7 @@ void CObfuscationPool::ProcessMessageObfuscation(CNode* pfrom, std::string& strC PrepareObfuscationDenominate(); } } else { - BOOST_FOREACH (CObfuscationQueue q, vecObfuscationQueue) { + for (CObfuscationQueue q : vecObfuscationQueue) { if (q.vin == dsq.vin) return; } @@ -204,7 +204,7 @@ void CObfuscationPool::ProcessMessageObfuscation(CNode* pfrom, std::string& strC CValidationState state; CMutableTransaction tx; - BOOST_FOREACH (const CTxOut o, out) { + for (const CTxOut o : out) { nValueOut += o.nValue; tx.vout.push_back(o); @@ -222,7 +222,7 @@ void CObfuscationPool::ProcessMessageObfuscation(CNode* pfrom, std::string& strC } } - BOOST_FOREACH (const CTxIn i, in) { + for (const CTxIn i : in) { tx.vin.push_back(i); LogPrint("obfuscation", "dsi -- tx in %s\n", i.ToString()); @@ -318,7 +318,7 @@ void CObfuscationPool::ProcessMessageObfuscation(CNode* pfrom, std::string& strC bool success = false; int count = 0; - BOOST_FOREACH (const CTxIn item, sigs) { + for (const CTxIn item : sigs) { if (AddScriptSig(item)) success = true; LogPrint("obfuscation", " -- sigs count %d %d\n", (int)sigs.size(), count); count++; @@ -438,7 +438,7 @@ void CObfuscationPool::UnlockCoins() MilliSleep(50); continue; } - BOOST_FOREACH (CTxIn v, lockedCoins) + for (CTxIn v : lockedCoins) pwalletMain->UnlockCoin(v.prevout); break; } @@ -540,10 +540,10 @@ void CObfuscationPool::Check() // make our new transaction for (unsigned int i = 0; i < entries.size(); i++) { - BOOST_FOREACH (const CTxOut& v, entries[i].vout) + for (const CTxOut& v : entries[i].vout) txNew.vout.push_back(v); - BOOST_FOREACH (const CTxDSIn& s, entries[i].sev) + for (const CTxDSIn& s : entries[i].sev) txNew.vin.push_back(s); } @@ -672,9 +672,9 @@ void CObfuscationPool::ChargeFees() if (r > 33) return; if (state == POOL_STATUS_ACCEPTING_ENTRIES) { - BOOST_FOREACH (const CTransaction& txCollateral, vecSessionCollateral) { + for (const CTransaction& txCollateral : vecSessionCollateral) { bool found = false; - BOOST_FOREACH (const CObfuScationEntry& v, entries) { + for (const CObfuScationEntry& v : entries) { if (v.collateral == txCollateral) { found = true; } @@ -690,8 +690,8 @@ void CObfuscationPool::ChargeFees() if (state == POOL_STATUS_SIGNING) { // who didn't sign? - BOOST_FOREACH (const CObfuScationEntry v, entries) { - BOOST_FOREACH (const CTxDSIn s, v.sev) { + for (const CObfuScationEntry v : entries) { + for (const CTxDSIn s : v.sev) { if (!s.fHasSig) { LogPrintf("CObfuscationPool::ChargeFees -- found uncooperative node (didn't sign). Found offence\n"); offences++; @@ -716,9 +716,9 @@ void CObfuscationPool::ChargeFees() r = rand() % 100; if (state == POOL_STATUS_ACCEPTING_ENTRIES) { - BOOST_FOREACH (const CTransaction& txCollateral, vecSessionCollateral) { + for (const CTransaction& txCollateral : vecSessionCollateral) { bool found = false; - BOOST_FOREACH (const CObfuScationEntry& v, entries) { + for (const CObfuScationEntry& v : entries) { if (v.collateral == txCollateral) { found = true; } @@ -743,8 +743,8 @@ void CObfuscationPool::ChargeFees() if (state == POOL_STATUS_SIGNING) { // who didn't sign? - BOOST_FOREACH (const CObfuScationEntry v, entries) { - BOOST_FOREACH (const CTxDSIn s, v.sev) { + for (const CObfuScationEntry v : entries) { + for (const CTxDSIn s : v.sev) { if (!s.fHasSig && r > target) { LogPrintf("CObfuscationPool::ChargeFees -- found uncooperative node (didn't sign). charging fees.\n"); @@ -770,7 +770,7 @@ void CObfuscationPool::ChargeRandomFees() if (fMasterNode) { int i = 0; - BOOST_FOREACH (const CTransaction& txCollateral, vecSessionCollateral) { + for (const CTransaction& txCollateral : vecSessionCollateral) { int r = rand() % 100; /* @@ -919,11 +919,11 @@ bool CObfuscationPool::SignatureValid(const CScript& newSig, const CTxIn& newVin CScript sigPubKey = CScript(); unsigned int i = 0; - BOOST_FOREACH (CObfuScationEntry& e, entries) { - BOOST_FOREACH (const CTxOut& out, e.vout) + for (CObfuScationEntry& e : entries) { + for (const CTxOut& out : e.vout) txNew.vout.push_back(out); - BOOST_FOREACH (const CTxDSIn& s, e.sev) { + for (const CTxDSIn& s : e.sev) { txNew.vin.push_back(s); if (s == newVin) { @@ -958,7 +958,7 @@ bool CObfuscationPool::IsCollateralValid(const CTransaction& txCollateral) int64_t nValueOut = 0; bool missingTx = false; - BOOST_FOREACH (const CTxOut o, txCollateral.vout) { + for (const CTxOut o : txCollateral.vout) { nValueOut += o.nValue; if (!o.scriptPubKey.IsNormalPaymentScript()) { @@ -967,7 +967,7 @@ bool CObfuscationPool::IsCollateralValid(const CTransaction& txCollateral) } } - BOOST_FOREACH (const CTxIn i, txCollateral.vin) { + for (const CTxIn i : txCollateral.vin) { CTransaction tx2; uint256 hash; if (GetTransaction(i.prevout.hash, tx2, hash, true)) { @@ -1012,7 +1012,7 @@ bool CObfuscationPool::AddEntry(const std::vector& newInput, const CAmoun { if (!fMasterNode) return false; - BOOST_FOREACH (CTxIn in, newInput) { + for (CTxIn in : newInput) { if (in.prevout.IsNull() || nAmount < 0) { LogPrint("obfuscation", "CObfuscationPool::AddEntry - input not valid!\n"); errorID = ERR_INVALID_INPUT; @@ -1035,10 +1035,10 @@ bool CObfuscationPool::AddEntry(const std::vector& newInput, const CAmoun return false; } - BOOST_FOREACH (CTxIn in, newInput) { + for (CTxIn in : newInput) { LogPrint("obfuscation", "looking for vin -- %s\n", in.ToString()); - BOOST_FOREACH (const CObfuScationEntry& v, entries) { - BOOST_FOREACH (const CTxDSIn& s, v.sev) { + for (const CObfuScationEntry& v : entries) { + for (const CTxDSIn& s : v.sev) { if ((CTxIn)s == in) { LogPrint("obfuscation", "CObfuscationPool::AddEntry - found in vin\n"); errorID = ERR_ALREADY_HAVE; @@ -1064,8 +1064,8 @@ bool CObfuscationPool::AddScriptSig(const CTxIn& newVin) LogPrint("obfuscation", "CObfuscationPool::AddScriptSig -- new sig %s\n", newVin.scriptSig.ToString().substr(0, 24)); - BOOST_FOREACH (const CObfuScationEntry& v, entries) { - BOOST_FOREACH (const CTxDSIn& s, v.sev) { + for (const CObfuScationEntry& v : entries) { + for (const CTxDSIn& s : v.sev) { if (s.scriptSig == newVin.scriptSig) { LogPrint("obfuscation", "CObfuscationPool::AddScriptSig - already exists\n"); return false; @@ -1080,7 +1080,7 @@ bool CObfuscationPool::AddScriptSig(const CTxIn& newVin) LogPrint("obfuscation", "CObfuscationPool::AddScriptSig -- sig %s\n", newVin.ToString()); - BOOST_FOREACH (CTxIn& vin, finalTransaction.vin) { + for (CTxIn& vin : finalTransaction.vin) { if (newVin.prevout == vin.prevout && vin.nSequence == newVin.nSequence) { vin.scriptSig = newVin.scriptSig; vin.prevPubKey = newVin.prevPubKey; @@ -1101,8 +1101,8 @@ bool CObfuscationPool::AddScriptSig(const CTxIn& newVin) // Check to make sure everything is signed bool CObfuscationPool::SignaturesComplete() { - BOOST_FOREACH (const CObfuScationEntry& v, entries) { - BOOST_FOREACH (const CTxDSIn& s, v.sev) { + for (const CObfuScationEntry& v : entries) { + for (const CTxDSIn& s : v.sev) { if (!s.fHasSig) return false; } } @@ -1126,13 +1126,13 @@ void CObfuscationPool::SendObfuscationDenominate(std::vector& vin, std::v } // lock the funds we're going to use - BOOST_FOREACH (CTxIn in, txCollateral.vin) + for (CTxIn in : txCollateral.vin) lockedCoins.push_back(in); - BOOST_FOREACH (CTxIn in, vin) + for (CTxIn in : vin) lockedCoins.push_back(in); - //BOOST_FOREACH(CTxOut o, vout) + //for(CTxOut o : vout) // LogPrintf(" vout - %s\n", o.ToString()); @@ -1165,12 +1165,12 @@ void CObfuscationPool::SendObfuscationDenominate(std::vector& vin, std::v CValidationState state; CMutableTransaction tx; - BOOST_FOREACH (const CTxOut& o, vout) { + for (const CTxOut& o : vout) { nValueOut += o.nValue; tx.vout.push_back(o); } - BOOST_FOREACH (const CTxIn& i, vin) { + for (const CTxIn& i : vin) { tx.vin.push_back(i); LogPrint("obfuscation", "dsi -- tx in %s\n", i.ToString()); @@ -1266,8 +1266,8 @@ bool CObfuscationPool::SignFinalTransaction(CTransaction& finalTransactionNew, C vector sigs; //make sure my inputs/outputs are present, otherwise refuse to sign - BOOST_FOREACH (const CObfuScationEntry e, entries) { - BOOST_FOREACH (const CTxDSIn s, e.sev) { + for (const CObfuScationEntry e : entries) { + for (const CTxDSIn s : e.sev) { /* Sign my transaction and all outputs */ int mine = -1; CScript prevPubKey = CScript(); @@ -1287,7 +1287,7 @@ bool CObfuscationPool::SignFinalTransaction(CTransaction& finalTransactionNew, C CAmount nValue2 = 0; for (unsigned int i = 0; i < finalTransaction.vout.size(); i++) { - BOOST_FOREACH (const CTxOut& o, e.vout) { + for (const CTxOut& o : e.vout) { if (finalTransaction.vout[i] == o) { foundOutputs++; nValue1 += finalTransaction.vout[i].nValue; @@ -1295,7 +1295,7 @@ bool CObfuscationPool::SignFinalTransaction(CTransaction& finalTransactionNew, C } } - BOOST_FOREACH (const CTxOut o, e.vout) + for (const CTxOut o : e.vout) nValue2 += o.nValue; int targetOuputs = e.vout.size(); @@ -1840,7 +1840,7 @@ bool CObfuscationPool::IsCompatibleWithEntries(std::vector& vout) { if (GetDenominations(vout) == 0) return false; - BOOST_FOREACH (const CObfuScationEntry v, entries) { + for (const CObfuScationEntry v : entries) { LogPrintf(" IsCompatibleWithEntries %d %d\n", GetDenominations(vout), GetDenominations(v.vout)); /* BOOST_FOREACH(CTxOut o1, vout) @@ -1950,7 +1950,7 @@ int CObfuscationPool::GetDenominations(const std::vector& vout) { std::vector vout2; - BOOST_FOREACH (CTxDSOut out, vout) + for (CTxDSOut out : vout) vout2.push_back(out); return GetDenominations(vout2); @@ -1962,13 +1962,13 @@ int CObfuscationPool::GetDenominations(const std::vector& vout, bool fSi std::vector > denomUsed; // make a list of denominations, with zero uses - BOOST_FOREACH (int64_t d, obfuScationDenominations) + for (int64_t d : obfuScationDenominations) denomUsed.push_back(make_pair(d, 0)); // look for denominations and update uses to 1 - BOOST_FOREACH (CTxOut out, vout) { + for (CTxOut out : vout) { bool found = false; - BOOST_FOREACH (PAIRTYPE(int64_t, int) & s, denomUsed) { + for (PAIRTYPE(int64_t, int) & s : denomUsed) { if (out.nValue == s.first) { s.second = 1; found = true; @@ -1981,7 +1981,7 @@ int CObfuscationPool::GetDenominations(const std::vector& vout, bool fSi int c = 0; // if the denomination is used, shift the bit on. // then move to the next - BOOST_FOREACH (PAIRTYPE(int64_t, int) & s, denomUsed) { + for (PAIRTYPE(int64_t, int) & s : denomUsed) { int bit = (fSingleRandomDenom ? rand() % 2 : 1) * s.second; denom |= bit << c++; if (fSingleRandomDenom && bit) break; // use just one random denomination @@ -2214,7 +2214,7 @@ bool CObfuscationQueue::Sign() bool CObfuscationQueue::Relay() { LOCK(cs_vNodes); - BOOST_FOREACH (CNode* pnode, vNodes) { + for (CNode* pnode : vNodes) { // always relay to everyone pnode->PushMessage("dsq", (*this)); } @@ -2244,7 +2244,7 @@ bool CObfuscationQueue::CheckSignature() void CObfuscationPool::RelayFinalTransaction(const int sessionID, const CTransaction& txNew) { LOCK(cs_vNodes); - BOOST_FOREACH (CNode* pnode, vNodes) { + for (CNode* pnode : vNodes) { pnode->PushMessage("dsf", sessionID, txNew); } } @@ -2256,10 +2256,10 @@ void CObfuscationPool::RelayIn(const std::vector& vin, const int64_t& n std::vector vin2; std::vector vout2; - BOOST_FOREACH (CTxDSIn in, vin) + for (CTxDSIn in : vin) vin2.push_back(in); - BOOST_FOREACH (CTxDSOut out, vout) + for (CTxDSOut out : vout) vout2.push_back(out); CNode* pnode = FindNode(pSubmittedToMasternode->addr); @@ -2272,14 +2272,14 @@ void CObfuscationPool::RelayIn(const std::vector& vin, const int64_t& n void CObfuscationPool::RelayStatus(const int sessionID, const int newState, const int newEntriesCount, const int newAccepted, const int errorID) { LOCK(cs_vNodes); - BOOST_FOREACH (CNode* pnode, vNodes) + for (CNode* pnode : vNodes) pnode->PushMessage("dssu", sessionID, newState, newEntriesCount, newAccepted, errorID); } void CObfuscationPool::RelayCompletedTransaction(const int sessionID, const bool error, const int errorID) { LOCK(cs_vNodes); - BOOST_FOREACH (CNode* pnode, vNodes) + for (CNode* pnode : vNodes) pnode->PushMessage("dsc", sessionID, error, errorID); } diff --git a/src/obfuscation.h b/src/obfuscation.h index 92b184d..711befb 100644 --- a/src/obfuscation.h +++ b/src/obfuscation.h @@ -411,15 +411,15 @@ class CObfuscationPool void UpdateState(unsigned int newState) { if (fMasterNode && (newState == POOL_STATUS_ERROR || newState == POOL_STATUS_SUCCESS)) { - LogPrint("obfuscation", "CObfuscationPool::UpdateState() - Can't set state to ERROR or SUCCESS as a Masternode. \n"); + // LogPrint("obfuscation", "CObfuscationPool::UpdateState() - Can't set state to ERROR or SUCCESS as a Masternode. \n"); return; } - LogPrintf("CObfuscationPool::UpdateState() == %d | %d \n", state, newState); + // LogPrintf("CObfuscationPool::UpdateState() == %d | %d \n", state, newState); if (state != newState) { lastTimeChanged = GetTimeMillis(); if (fMasterNode) { - RelayStatus(obfuScationPool.sessionID, newState, obfuScationPool.GetEntriesCount(), MASTERNODE_RESET); + RelayStatus(obfuScationPool.sessionID, obfuScationPool.GetState(), obfuScationPool.GetEntriesCount(), MASTERNODE_RESET); } } state = newState; diff --git a/src/pow.cpp b/src/pow.cpp index 5749e9f..41effb6 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -30,7 +30,9 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, uint32_t nTime) uint256 PastDifficultyAverage; uint256 PastDifficultyAveragePrev; - if (BlockLastSolved == NULL || BlockLastSolved->nHeight == 0 || BlockLastSolved->nHeight < PastBlocksMin) { + if (BlockLastSolved == NULL || + BlockLastSolved->nHeight == 0 || + BlockLastSolved->nHeight < PastBlocksMin) { return Params().StartWork().GetCompact(); } diff --git a/src/pow.h b/src/pow.h index d80230a..5a80fc0 100644 --- a/src/pow.h +++ b/src/pow.h @@ -8,8 +8,10 @@ #include +class CBlockHeader; class CBlockIndex; class uint256; +class arith_uint256; unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, uint32_t nTime); diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index 45e98db..021fa60 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -86,7 +86,7 @@ uint256 CBlock::BuildMerkleTree(bool* fMutated) const mutated = true; } vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]), - BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2]))); + BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2]))); } j += nSize; } @@ -153,7 +153,6 @@ void CBlock::print() const LogPrintf("%s", ToString()); } -// ppcoin: sign block bool CBlock::SignBlock(const CKeyStore& keystore) { std::vector vSolutions; diff --git a/src/primitives/block.h b/src/primitives/block.h index 16a34dd..42739a6 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -7,7 +7,7 @@ #ifndef BITCOIN_PRIMITIVES_BLOCK_H #define BITCOIN_PRIMITIVES_BLOCK_H -#include "transaction.h" +#include "primitives/transaction.h" #include "../keystore.h" #include "../serialize.h" #include "../uint256.h" diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 1967270..094b2a6 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -14,7 +14,6 @@ #include "tinyformat.h" #include "utilstrencodings.h" -#include extern bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow); diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index faef4b7..ccaa580 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -7,13 +7,13 @@ #ifndef BITCOIN_PRIMITIVES_TRANSACTION_H #define BITCOIN_PRIMITIVES_TRANSACTION_H -#include - #include "../amount.h" #include "../script/script.h" #include "../serialize.h" #include "../uint256.h" +#include + class CTransaction; /** An outpoint - a combination of a transaction hash and an index n into its vout */ @@ -276,7 +276,6 @@ class CTransaction } std::string ToString() const; - bool GetCoinAge(uint64_t& nCoinAge) const; // ppcoin: get transaction coin age }; diff --git a/src/protocol.cpp b/src/protocol.cpp index dedfd28..fbed8b0 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -14,7 +14,7 @@ #endif static const char* ppszTypeName[] = -{ + { "ERROR", "tx", "block", @@ -23,10 +23,15 @@ static const char* ppszTypeName[] = "tx lock vote", "spork", "mn winner", + "mn scan error", + "mn budget vote", + "mn budget proposal", + "mn budget finalized", + "mn budget finalized vote", + "mn quorum", "mn announce", "mn ping", - "dstx" -}; + "dstx"}; CMessageHeader::CMessageHeader() { diff --git a/src/protocol.h b/src/protocol.h index d97a8da..d5c462f 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -162,6 +162,12 @@ enum { MSG_TXLOCK_VOTE, MSG_SPORK, MSG_MASTERNODE_WINNER, + MSG_MASTERNODE_SCANNING_ERROR, + MSG_BUDGET_VOTE, + MSG_BUDGET_PROPOSAL, + MSG_BUDGET_FINALIZED, + MSG_BUDGET_FINALIZED_VOTE, + MSG_MASTERNODE_QUORUM, MSG_MASTERNODE_ANNOUNCE, MSG_MASTERNODE_PING, MSG_DSTX diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index 3bcd32a..6ee21c0 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 10eeb64..f3a4444 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -11,7 +11,7 @@ #include "walletmodel.h" #include "base58.h" -#include "wallet.h" +#include "wallet/wallet.h" #include #include @@ -78,7 +78,7 @@ class AddressTablePriv cachedAddressTable.clear(); { LOCK(wallet->cs_wallet); - BOOST_FOREACH (const PAIRTYPE(CTxDestination, CAddressBookData) & item, wallet->mapAddressBook) { + for (const PAIRTYPE(CTxDestination, CAddressBookData) & item : wallet->mapAddressBook) { const CBitcoinAddress& address = item.first; bool fMine = IsMine(*wallet, address.Get()); AddressTableEntry::Type addressType = translateTransactionType( diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index 8add193..5cd5e63 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index a013b91..c238672 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/bip38tooldialog.cpp b/src/qt/bip38tooldialog.cpp index 5d990f5..b2a7ac4 100644 --- a/src/qt/bip38tooldialog.cpp +++ b/src/qt/bip38tooldialog.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -15,7 +15,7 @@ #include "base58.h" #include "bip38.h" #include "init.h" -#include "wallet.h" +#include "wallet/wallet.h" #include #include diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp index b6d8d19..9dbfac3 100644 --- a/src/qt/bitcoinaddressvalidator.cpp +++ b/src/qt/bitcoinaddressvalidator.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index fb1a46a..71f257f 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -203,8 +203,8 @@ QString windowTitle = tr("XDNA Core") + " - "; frameBlocksLayout->addWidget(labelEncryptionIcon); } - - // frameBlocksLayout->addWidget(labelStakingIcon); + + frameBlocksLayout->addWidget(labelStakingIcon); frameBlocksLayout->addWidget(labelBlocksIcon); frameBlocksLayout->addWidget(labelConnectionsIcon); frameBlocksLayout->setAlignment(Qt::AlignRight); @@ -280,13 +280,13 @@ QString windowTitle = tr("XDNA Core") + " - "; subscribeToCoreSignals(); //will be activate when pow ends - /* + labelStakingIcon->hide(); QTimer* timerStakingIcon = new QTimer(labelStakingIcon); connect(timerStakingIcon, SIGNAL(timeout()), this, SLOT(setStakingStatus())); timerStakingIcon->start(10000); setStakingStatus(); - */ + } BitcoinGUI::~BitcoinGUI() diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 0758dc9..182b010 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index 4ec3ced..2be4b4e 100755 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index 6f27d13..c9e90bf 100755 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/blockexplorer.cpp b/src/qt/blockexplorer.cpp index c0a5229..0865028 100644 --- a/src/qt/blockexplorer.cpp +++ b/src/qt/blockexplorer.cpp @@ -401,7 +401,7 @@ std::string AddressToString(const CBitcoinAddress& Address) { std::vector Txs; paddressmap->GetTxs(Txs, AddressScript.GetID()); - BOOST_FOREACH (const CDiskTxPos& pos, Txs) + for (const CDiskTxPos& pos : Txs) { CTransaction tx; CBlock block; diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index e077fbb..57cc59e 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -62,7 +62,7 @@ int ClientModel::getNumConnections(unsigned int flags) const return vNodes.size(); int nNum = 0; - BOOST_FOREACH (CNode* pnode, vNodes) + for (CNode* pnode : vNodes) if (flags & (pnode->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT)) nNum++; diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 7cac01a..60a51f7 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 176d37b..3bf5c53 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -18,7 +18,7 @@ #include "coincontrol.h" #include "main.h" #include "obfuscation.h" -#include "wallet.h" +#include "wallet/wallet.h" #include "multisigdialog.h" #include // for 'map_list_of()' @@ -441,13 +441,6 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column) else { coinControl->Select(outpt); CTxIn vin(outpt); - int rounds = pwalletMain->GetInputObfuscationRounds(vin); - if (coinControl->useObfuScation && rounds < nObfuscationRounds) { - QMessageBox::warning(this, windowTitle(), - tr("Non-anonymized input selected. Obfuscation will be disabled.

If you still want to use Obfuscation, please deselect all non-nonymized inputs first and then check Obfuscation checkbox again."), - QMessageBox::Ok, QMessageBox::Ok); - coinControl->useObfuScation = false; - } } // selection changed -> update labels @@ -582,7 +575,7 @@ void CoinControlDialog::updateLabels(WalletModel* model, QDialog* dialog) coinControl->ListSelected(vCoinControl); model->getOutputs(vCoinControl, vOutputs); - BOOST_FOREACH (const COutput& out, vOutputs) { + for (const COutput& out : vOutputs) { // unselect already spent, very unlikely scenario, this could happen // when selected are spent elsewhere, like rpc or another computer uint256 txhash = out.tx->GetHash(); @@ -766,7 +759,7 @@ void CoinControlDialog::updateView() map> mapCoins; model->listCoins(mapCoins); - BOOST_FOREACH (PAIRTYPE(QString, vector) coins, mapCoins) { + for (PAIRTYPE(QString, vector) coins : mapCoins) { QTreeWidgetItem* itemWalletAddress = new QTreeWidgetItem(); itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); QString sWalletAddress = coins.first; @@ -869,17 +862,6 @@ void CoinControlDialog::updateView() itemOutput->setToolTip(COLUMN_DATE, GUIUtil::dateTimeStr(out.tx->GetTxTime())); itemOutput->setText(COLUMN_DATE_INT64, strPad(QString::number(out.tx->GetTxTime()), 20, " ")); - - // ds+ rounds - CTxIn vin = CTxIn(out.tx->GetHash(), out.i); - int rounds = pwalletMain->GetInputObfuscationRounds(vin); - - if (rounds >= 0) - itemOutput->setText(COLUMN_OBFUSCATION_ROUNDS, strPad(QString::number(rounds), 11, " ")); - else - itemOutput->setText(COLUMN_OBFUSCATION_ROUNDS, strPad(QString(tr("n/a")), 11, " ")); - - // confirmations itemOutput->setText(COLUMN_CONFIRMATIONS, strPad(QString::number(out.nDepth), 8, " ")); diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h index e0970e1..b66c043 100644 --- a/src/qt/coincontroldialog.h +++ b/src/qt/coincontroldialog.h @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index 0117b98..bb6602b 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index 1c687f6..8554683 100755 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -791,13 +791,6 @@ - - - - 0 - - - @@ -817,47 +810,14 @@ - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - BitGun level: - - - - - - - 0 - - - - + Block reward: - - - - Nethash MHs: - - - - + 0 @@ -903,397 +863,6 @@ - - - Obfuscation - - - - 1 - - - 1 - - - 1 - - - 1 - - - - - - 320 - 280 - - - - Qt::LeftToRight - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 6 - - - 6 - - - 6 - - - 2 - - - - - - 0 - 100 - - - - - QFormLayout::AllNonFixedFieldsGrow - - - 6 - - - 9 - - - - - Status: - - - - - - - Enabled/Disabled - - - - - - - - 0 - 17 - - - - Completion: - - - - - - - - 0 - 17 - - - - - 154 - 15 - - - - 0 - - - - - - - - 0 - 17 - - - - Obfuscation Balance: - - - - - - - - 0 - 17 - - - - - 75 - true - - - - 0 BWK - - - - - - - Amount and Rounds: - - - - - - - 0 XDNA / 0 Rounds - - - - - - - Submitted Denom: - - - - - - - The denominations you submitted to the Masternode.<br>To mix, other users must submit the exact same denominations. - - - n/a - - - - - - - - - - - 0 - 50 - - - - - 16777215 - 200 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 2 - - - 0 - - - 0 - - - 0 - - - 6 - - - - - 2 - - - 4 - - - 10 - - - 4 - - - 0 - - - - - - 0 - 0 - - - - - 300 - 20 - - - - - 300 - 100 - - - - (Last Message) - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - true - - - - - - - Qt::Vertical - - - QSizePolicy::MinimumExpanding - - - - 20 - 20 - - - - - - - - - - - 0 - 0 - - - - - 0 - 27 - - - - - 16777215 - 27 - - - - ArrowCursor - - - Start/Stop Mixing - - - - - - - - - 3 - - - QLayout::SetFixedSize - - - 3 - - - - - - 0 - 0 - - - - - 65 - 27 - - - - - 150 - 27 - - - - Reset the current status of Obfuscation (can interrupt Obfuscation if it's in the process of Mixing, which can cost you money!) - - - false - - - Reset - - - - - - - - 0 - 0 - - - - - 65 - 27 - - - - - 150 - 27 - - - - Try to manually submit a Obfuscation request. - - - Try Mix - - - - - - - - - - - - - - - diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 3b4b9ee..472172d 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 22c4e07..019bbdf 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index ffe1b01..0e8c895 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 116f313..506f652 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/masternodelist.cpp b/src/qt/masternodelist.cpp index 94bb308..cfb737a 100644 --- a/src/qt/masternodelist.cpp +++ b/src/qt/masternodelist.cpp @@ -9,9 +9,9 @@ #include "../masternodeconfig.h" #include "../masternodeman.h" #include "../sync.h" -#include "../wallet.h" +#include "../wallet/wallet.h" #include "walletmodel.h" -#include "../rpcserver.h" +#include "../rpc/server.h" #include #include @@ -134,7 +134,7 @@ void MasternodeList::StartAlias(std::string strAlias) { std::string strStatusHtml; strStatusHtml += "
Alias: " + strAlias; - BOOST_FOREACH (CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) { + for (CMasternodeConfig::CMasternodeEntry mne : masternodeConfig.getEntries()) { if (mne.getAlias() == strAlias) { std::string strError; std::string strOverall; @@ -164,7 +164,7 @@ void MasternodeList::StartAll(std::string strCommand) int nCountFailed = 0; std::string strFailedHtml; - BOOST_FOREACH (CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) { + for (CMasternodeConfig::CMasternodeEntry mne : masternodeConfig.getEntries()) { std::string strError; std::string strAlias = mne.getAlias(); std::string strOverall; @@ -258,7 +258,7 @@ void MasternodeList::updateMyNodeList(bool fForce) nTimeMyListUpdated = GetTime(); ui->tableWidgetMyMasternodes->setSortingEnabled(false); - BOOST_FOREACH (CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) { + for (CMasternodeConfig::CMasternodeEntry mne : masternodeConfig.getEntries()) { int nIndex; if(!mne.castOutputIndex(nIndex)) continue; @@ -447,4 +447,4 @@ void MasternodeList::on_tableWidgetMyMasternodes_itemSelectionChanged() void MasternodeList::on_UpdateButton_clicked() { updateMyNodeList(true); -} \ No newline at end of file +} diff --git a/src/qt/multisenddialog.cpp b/src/qt/multisenddialog.cpp index 9d9c442..13ff4f3 100644 --- a/src/qt/multisenddialog.cpp +++ b/src/qt/multisenddialog.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers #include "multisenddialog.h" #include "addressbookpage.h" @@ -12,6 +12,7 @@ #include "walletmodel.h" #include #include +#include #include using namespace std; diff --git a/src/qt/multisigdialog.cpp b/src/qt/multisigdialog.cpp index 23f77dd..219264c 100644 --- a/src/qt/multisigdialog.cpp +++ b/src/qt/multisigdialog.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -16,7 +16,7 @@ #include "coins.h" #include "keystore.h" #include "init.h" -#include "wallet.h" +#include "wallet/wallet.h" #include "script/sign.h" #include "script/interpreter.h" #include "utilmoneystr.h" diff --git a/src/qt/obfuscationconfig.cpp b/src/qt/obfuscationconfig.cpp index 0649fee..eea25cf 100644 --- a/src/qt/obfuscationconfig.cpp +++ b/src/qt/obfuscationconfig.cpp @@ -77,11 +77,4 @@ void ObfuscationConfig::clickMax() void ObfuscationConfig::configure(bool enabled, int coins, int rounds) { - QSettings settings; - - settings.setValue("nObfuscationRounds", rounds); - settings.setValue("nAnonymizeXDnaAmount", coins); - - nObfuscationRounds = rounds; - nAnonymizeXDnaAmount = coins; } diff --git a/src/qt/openuridialog.cpp b/src/qt/openuridialog.cpp index de6447c..8318265 100644 --- a/src/qt/openuridialog.cpp +++ b/src/qt/openuridialog.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 46c28e2..528f2a0 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -19,7 +19,7 @@ #include "txdb.h" // for -dbcache defaults #ifdef ENABLE_WALLET -#include "wallet.h" // for CWallet::minTxFee +#include "wallet/wallet.h" // for CWallet::minTxFee #endif #include @@ -273,7 +273,7 @@ void OptionsDialog::on_resetButton_clicked() void OptionsDialog::on_okButton_clicked() { mapper->submit(); - obfuScationPool.cachedNumBlocks = std::numeric_limits::max(); + // obfuScationPool.cachedNumBlocks = std::numeric_limits::max(); pwalletMain->MarkDirty(); accept(); } diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index c0070cd..e723de6 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -22,8 +22,8 @@ #ifdef ENABLE_WALLET #include "masternodeconfig.h" -#include "wallet.h" -#include "walletdb.h" +#include "wallet/wallet.h" +#include "wallet/walletdb.h" #endif #include @@ -73,15 +73,6 @@ void OptionsModel::Init() settings.setValue("fCoinControlFeatures", false); fCoinControlFeatures = settings.value("fCoinControlFeatures", false).toBool(); - if (!settings.contains("nObfuscationRounds")) - settings.setValue("nObfuscationRounds", 2); - - if (!settings.contains("nAnonymizeXDnaAmount")) - settings.setValue("nAnonymizeXDnaAmount", 1000); - - nObfuscationRounds = settings.value("nObfuscationRounds").toLongLong(); - nAnonymizeXDnaAmount = settings.value("nAnonymizeXDnaAmount").toLongLong(); - if (!settings.contains("fShowMasternodesTab")) settings.setValue("fShowMasternodesTab", masternodeConfig.getCount()); @@ -226,10 +217,6 @@ QVariant OptionsModel::data(const QModelIndex& index, int role) const return settings.value("nDatabaseCache"); case ThreadsScriptVerif: return settings.value("nThreadsScriptVerif"); - case ObfuscationRounds: - return QVariant(nObfuscationRounds); - case AnonymizeXDnaAmount: - return QVariant(nAnonymizeXDnaAmount); case Listen: return settings.value("fListen"); default: @@ -333,16 +320,6 @@ bool OptionsModel::setData(const QModelIndex& index, const QVariant& value, int setRestartRequired(true); } break; - case ObfuscationRounds: - nObfuscationRounds = value.toInt(); - settings.setValue("nObfuscationRounds", nObfuscationRounds); - emit obfuscationRoundsChanged(nObfuscationRounds); - break; - case AnonymizeXDnaAmount: - nAnonymizeXDnaAmount = value.toInt(); - settings.setValue("nAnonymizeXDnaAmount", nAnonymizeXDnaAmount); - emit anonymizeXDnaAmountChanged(nAnonymizeXDnaAmount); - break; case CoinControlFeatures: fCoinControlFeatures = value.toBool(); settings.setValue("fCoinControlFeatures", fCoinControlFeatures); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index b8bec35..ba2efed 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index f4f67d4..2c9b04c 100755 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -127,7 +127,6 @@ OverviewPage::OverviewPage(QWidget* parent) : QWidget(parent), // Recent transactions ui->listTransactions->setItemDelegate(txdelegate); ui->listTransactions->setIconSize(QSize(DECORATION_SIZE, DECORATION_SIZE)); - //ui->listTransactions->setMinimumHeight(NUM_ITEMS * (DECORATION_SIZE + 2)); ui->listTransactions->setAttribute(Qt::WA_MacShowFocusRect, false); connect(ui->listTransactions, SIGNAL(clicked(QModelIndex)), this, SLOT(handleTransactionClicked(QModelIndex))); @@ -135,28 +134,8 @@ OverviewPage::OverviewPage(QWidget* parent) : QWidget(parent), // init "out of sync" warning labels ui->labelWalletStatus->setText("(" + tr("out of sync") + ")"); - // ui->labelObfuscationSyncStatus->setText("(" + tr("out of sync") + ")"); ui->labelTransactionsStatus->setText("(" + tr("out of sync") + ")"); - if (fLiteMode) { - ui->frameObfuscation->setVisible(false); - } else { - if (fMasterNode) { - ui->toggleObfuscation->setText("(" + tr("Disabled") + ")"); - ui->obfuscationAuto->setText("(" + tr("Disabled") + ")"); - ui->obfuscationReset->setText("(" + tr("Disabled") + ")"); - ui->frameObfuscation->setEnabled(false); - } else { - if (!fEnableObfuscation) { - ui->toggleObfuscation->setText(tr("Start Obfuscation")); - } else { - ui->toggleObfuscation->setText(tr("Stop Obfuscation")); - } - timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), this, SLOT(obfuScationStatus())); - timer->start(1000); - } - } //information block update timerinfo_mn = new QTimer(this); connect(timerinfo_mn, SIGNAL(timeout()), this, SLOT(updateMasternodeInfo())); @@ -184,7 +163,7 @@ OverviewPage::~OverviewPage() void OverviewPage::setBalance(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance, const CAmount& anonymizedBalance, const CAmount& watchOnlyBalance, const CAmount& watchUnconfBalance, const CAmount& watchImmatureBalance) { - currentBalance = balance; + currentBalance = balance - immatureBalance; currentUnconfirmedBalance = unconfirmedBalance; currentImmatureBalance = immatureBalance; currentAnonymizedBalance = anonymizedBalance; @@ -195,11 +174,11 @@ void OverviewPage::setBalance(const CAmount& balance, const CAmount& unconfirmed // XDNA labels if(balance != 0) - ui->labelBalance->setText(BitcoinUnits::floorHtmlWithoutUnit(nDisplayUnit, balance, false, BitcoinUnits::separatorNever)); + ui->labelBalance->setText(BitcoinUnits::floorHtmlWithoutUnit(nDisplayUnit, currentBalance, false, BitcoinUnits::separatorNever)); ui->labelUnconfirmed->setText(BitcoinUnits::floorHtmlWithoutUnit(nDisplayUnit, unconfirmedBalance, false, BitcoinUnits::separatorNever)); ui->labelImmature->setText(BitcoinUnits::floorHtmlWithoutUnit(nDisplayUnit, immatureBalance, false, BitcoinUnits::separatorNever)); - ui->labelAnonymized->setText(BitcoinUnits::floorHtmlWithoutUnit(nDisplayUnit, anonymizedBalance, false, BitcoinUnits::separatorAlways)); - ui->labelTotal->setText(BitcoinUnits::floorHtmlWithoutUnit(nDisplayUnit, balance + unconfirmedBalance + immatureBalance, false, BitcoinUnits::separatorNever)); + //ui->labelAnonymized->setText(BitcoinUnits::floorHtmlWithoutUnit(nDisplayUnit, anonymizedBalance, false, BitcoinUnits::separatorAlways)); + ui->labelTotal->setText(BitcoinUnits::floorHtmlWithoutUnit(nDisplayUnit, currentBalance + unconfirmedBalance + immatureBalance, false, BitcoinUnits::separatorNever)); // Watchonly labels @@ -274,9 +253,9 @@ void OverviewPage::setWalletModel(WalletModel* model) connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); - connect(ui->obfuscationAuto, SIGNAL(clicked()), this, SLOT(obfuscationAuto())); - connect(ui->obfuscationReset, SIGNAL(clicked()), this, SLOT(obfuscationReset())); - connect(ui->toggleObfuscation, SIGNAL(clicked()), this, SLOT(toggleObfuscation())); + // connect(ui->obfuscationAuto, SIGNAL(clicked()), this, SLOT(obfuscationAuto())); + // connect(ui->obfuscationReset, SIGNAL(clicked()), this, SLOT(obfuscationReset())); + // connect(ui->toggleObfuscation, SIGNAL(clicked()), this, SLOT(toggleObfuscation())); connect(model, SIGNAL(notifyWatchonlyChanged(bool)), this, SLOT(updateWatchOnlyLabels(bool))); connect(ui->blabel_XDNA, SIGNAL(clicked()), this, SLOT(openMyAddresses())); @@ -353,46 +332,13 @@ void OverviewPage::updatBlockChainInfo() uint32_t tip_time = chainActive.Tip()->GetBlockTime(); - int CurrentBlock = chainActive.Height(); - int64_t netHashRate = chainActive.GetNetworkHashPS(24, CurrentBlock); - int64_t BlockReward = Params().SubsidyValue(netHashRate, tip_time, CurrentBlock); + int CurrentBlock = (int)chainActive.Height(); + int64_t BlockReward = GetBlockValue(chainActive.Height(), tip_time); double BlockRewardXDNA = static_cast(BlockReward)/static_cast(COIN); - //int64_t XDNASupply = chainActive.Tip()->nMoneySupply / COIN; ui->label_CurrentBlock_value->setText(QString::number(CurrentBlock)); - int BitGunLevel = 0; - - for(const auto& it : Params().GetSubsidySwitchPoints(tip_time, CurrentBlock)) - { - BitGunLevel++; - - if(it.second == BlockReward) - break; - } - - ui->label_CurrentBitGun_value->setText(QString::number(BitGunLevel)); - - double nethash_mhs = static_cast(netHashRate/1000000) ; - - if(nethash_mhs >= 1000000) - { - ui->label_Nethash->setText(tr("Nethash THs:")); - ui->label_Nethash_value->setText(QString::number(nethash_mhs/1000000,'f',2)); - } - else if(nethash_mhs >= 1000) - { - ui->label_Nethash->setText(tr("Nethash GHs:")); - ui->label_Nethash_value->setText(QString::number(nethash_mhs/1000,'f',2)); - } - else - { - ui->label_Nethash->setText(tr("Nethash MHs:")); - ui->label_Nethash_value->setText(QString::number(nethash_mhs)); - } - ui->label_CurrentBlockReward_value->setText(QString::number(BlockRewardXDNA)); - //ui->label_XDNASupply_value->setText(QString::number(XDNASupply)); } void OverviewPage::openMyAddresses() @@ -412,234 +358,26 @@ void OverviewPage::showOutOfSyncWarning(bool fShow) void OverviewPage::updateObfuscationProgress() { - if (!masternodeSync.IsBlockchainSynced() || ShutdownRequested()) return; - - if (!pwalletMain) return; - - QString strAmountAndRounds; - QString strAnonymizeXDnaAmount = BitcoinUnits::formatHtmlWithUnit(nDisplayUnit, nAnonymizeXDnaAmount * COIN, false, BitcoinUnits::separatorAlways); - - if (currentBalance == 0) { - ui->obfuscationProgress->setValue(0); - ui->obfuscationProgress->setToolTip(tr("No inputs detected")); - - // when balance is zero just show info from settings - strAnonymizeXDnaAmount = strAnonymizeXDnaAmount.remove(strAnonymizeXDnaAmount.indexOf("."), BitcoinUnits::decimals(nDisplayUnit) + 1); - strAmountAndRounds = strAnonymizeXDnaAmount + " / " + tr("%n Rounds", "", nObfuscationRounds); - - ui->labelAmountRounds->setToolTip(tr("No inputs detected")); - ui->labelAmountRounds->setText(strAmountAndRounds); - return; - } - - CAmount nDenominatedConfirmedBalance; - CAmount nDenominatedUnconfirmedBalance; - CAmount nAnonymizableBalance; - CAmount nNormalizedAnonymizedBalance; - double nAverageAnonymizedRounds; - - { - TRY_LOCK(cs_main, lockMain); - if (!lockMain) return; - - nDenominatedConfirmedBalance = pwalletMain->GetDenominatedBalance(); - nDenominatedUnconfirmedBalance = pwalletMain->GetDenominatedBalance(true); - nAnonymizableBalance = pwalletMain->GetAnonymizedBalance(); - nNormalizedAnonymizedBalance = pwalletMain->GetNormalizedAnonymizedBalance(); - nAverageAnonymizedRounds = pwalletMain->GetAverageAnonymizedRounds(); - } - - CAmount nMaxToAnonymize = nAnonymizableBalance + currentAnonymizedBalance + nDenominatedUnconfirmedBalance; - - // If it's more than the anon threshold, limit to that. - if (nMaxToAnonymize > nAnonymizeXDnaAmount * COIN) nMaxToAnonymize = nAnonymizeXDnaAmount * COIN; - - if (nMaxToAnonymize == 0) return; - - if (nMaxToAnonymize >= nAnonymizeXDnaAmount * COIN) { - ui->labelAmountRounds->setToolTip(tr("Found enough compatible inputs to anonymize %1") - .arg(strAnonymizeXDnaAmount)); - strAnonymizeXDnaAmount = strAnonymizeXDnaAmount.remove(strAnonymizeXDnaAmount.indexOf("."), BitcoinUnits::decimals(nDisplayUnit) + 1); - strAmountAndRounds = strAnonymizeXDnaAmount + " / " + tr("%n Rounds", "", nObfuscationRounds); - } else { - QString strMaxToAnonymize = BitcoinUnits::formatHtmlWithUnit(nDisplayUnit, nMaxToAnonymize, false, BitcoinUnits::separatorAlways); - ui->labelAmountRounds->setToolTip(tr("Not enough compatible inputs to anonymize %1,
" - "will anonymize %2 instead") - .arg(strAnonymizeXDnaAmount) - .arg(strMaxToAnonymize)); - strMaxToAnonymize = strMaxToAnonymize.remove(strMaxToAnonymize.indexOf("."), BitcoinUnits::decimals(nDisplayUnit) + 1); - strAmountAndRounds = "" + - QString(BitcoinUnits::factor(nDisplayUnit) == 1 ? "" : "~") + strMaxToAnonymize + - " / " + tr("%n Rounds", "", nObfuscationRounds) + ""; - } - ui->labelAmountRounds->setText(strAmountAndRounds); - - // calculate parts of the progress, each of them shouldn't be higher than 1 - // progress of denominating - float denomPart = 0; - // mixing progress of denominated balance - float anonNormPart = 0; - // completeness of full amount anonimization - float anonFullPart = 0; - - CAmount denominatedBalance = nDenominatedConfirmedBalance + nDenominatedUnconfirmedBalance; - denomPart = (float)denominatedBalance / nMaxToAnonymize; - denomPart = denomPart > 1 ? 1 : denomPart; - denomPart *= 100; - - anonNormPart = (float)nNormalizedAnonymizedBalance / nMaxToAnonymize; - anonNormPart = anonNormPart > 1 ? 1 : anonNormPart; - anonNormPart *= 100; - - anonFullPart = (float)currentAnonymizedBalance / nMaxToAnonymize; - anonFullPart = anonFullPart > 1 ? 1 : anonFullPart; - anonFullPart *= 100; - - // apply some weights to them ... - float denomWeight = 1; - float anonNormWeight = nObfuscationRounds; - float anonFullWeight = 2; - float fullWeight = denomWeight + anonNormWeight + anonFullWeight; - // ... and calculate the whole progress - float denomPartCalc = ceilf((denomPart * denomWeight / fullWeight) * 100) / 100; - float anonNormPartCalc = ceilf((anonNormPart * anonNormWeight / fullWeight) * 100) / 100; - float anonFullPartCalc = ceilf((anonFullPart * anonFullWeight / fullWeight) * 100) / 100; - float progress = denomPartCalc + anonNormPartCalc + anonFullPartCalc; - if (progress >= 100) progress = 100; - - ui->obfuscationProgress->setValue(progress); - - QString strToolPip = ("" + tr("Overall progress") + ": %1%
" + - tr("Denominated") + ": %2%
" + - tr("Mixed") + ": %3%
" + - tr("Anonymized") + ": %4%
" + - tr("Denominated inputs have %5 of %n rounds on average", "", nObfuscationRounds)) - .arg(progress) - .arg(denomPart) - .arg(anonNormPart) - .arg(anonFullPart) - .arg(nAverageAnonymizedRounds); - ui->obfuscationProgress->setToolTip(strToolPip); + return; } void OverviewPage::obfuScationStatus() { - static int64_t nLastDSProgressBlockTime = 0; - - int nBestHeight = chainActive.Tip()->nHeight; - - // we we're processing more then 1 block per second, we'll just leave - if (((nBestHeight - obfuScationPool.cachedNumBlocks) / (GetTimeMillis() - nLastDSProgressBlockTime + 1) > 1)) return; - nLastDSProgressBlockTime = GetTimeMillis(); - - if (!fEnableObfuscation) { - if (nBestHeight != obfuScationPool.cachedNumBlocks) { - obfuScationPool.cachedNumBlocks = nBestHeight; - updateObfuscationProgress(); - - ui->obfuscationEnabled->setText(tr("Disabled")); - ui->obfuscationStatus->setText(""); - ui->toggleObfuscation->setText(tr("Start Obfuscation")); - } - - return; - } - - // check obfuscation status and unlock if needed - if (nBestHeight != obfuScationPool.cachedNumBlocks) { - // Balance and number of transactions might have changed - obfuScationPool.cachedNumBlocks = nBestHeight; - updateObfuscationProgress(); - - ui->obfuscationEnabled->setText(tr("Enabled")); - } - - QString strStatus = QString(obfuScationPool.GetStatus().c_str()); - - QString s = strStatus; - - if (s != ui->obfuscationStatus->text()) - LogPrintf("Last Obfuscation message: %s\n", strStatus.toStdString()); - - ui->obfuscationStatus->setText(s); - - if (obfuScationPool.sessionDenom == 0) { - ui->labelSubmittedDenom->setText(tr("N/A")); - } else { - std::string out; - obfuScationPool.GetDenominationsToString(obfuScationPool.sessionDenom, out); - QString s2(out.c_str()); - ui->labelSubmittedDenom->setText(s2); - } + return; } void OverviewPage::obfuscationAuto() { - obfuScationPool.DoAutomaticDenominating(); + return; } void OverviewPage::obfuscationReset() { - obfuScationPool.Reset(); - - QMessageBox::warning(this, tr("Obfuscation"), - tr("Obfuscation was successfully reset."), - QMessageBox::Ok, QMessageBox::Ok); + return; } void OverviewPage::toggleObfuscation() { - QSettings settings; - // Popup some information on first mixing - QString hasMixed = settings.value("hasMixed").toString(); - if (hasMixed.isEmpty()) { - QMessageBox::information(this, tr("Obfuscation"), - tr("If you don't want to see internal Obfuscation fees/transactions select \"Most Common\" as Type on the \"Transactions\" tab."), - QMessageBox::Ok, QMessageBox::Ok); - settings.setValue("hasMixed", "hasMixed"); - } - if (!fEnableObfuscation) { - int64_t balance = currentBalance; - float minAmount = 14.90 * COIN; - if (balance < minAmount) { - QString strMinAmount(BitcoinUnits::formatWithUnit(nDisplayUnit, minAmount)); - QMessageBox::warning(this, tr("Obfuscation"), - tr("Obfuscation requires at least %1 to use.").arg(strMinAmount), - QMessageBox::Ok, QMessageBox::Ok); - return; - } - - // if wallet is locked, ask for a passphrase - if (walletModel->getEncryptionStatus() == WalletModel::Locked) { - WalletModel::UnlockContext ctx(walletModel->requestUnlock(false)); - if (!ctx.isValid()) { - //unlock was cancelled - obfuScationPool.cachedNumBlocks = std::numeric_limits::max(); - QMessageBox::warning(this, tr("Obfuscation"), - tr("Wallet is locked and user declined to unlock. Disabling Obfuscation."), - QMessageBox::Ok, QMessageBox::Ok); - if (fDebug) LogPrintf("Wallet is locked and user declined to unlock. Disabling Obfuscation.\n"); - return; - } - } - } - - fEnableObfuscation = !fEnableObfuscation; - obfuScationPool.cachedNumBlocks = std::numeric_limits::max(); - - if (!fEnableObfuscation) { - ui->toggleObfuscation->setText(tr("Start Obfuscation")); - obfuScationPool.UnlockCoins(); - } else { - ui->toggleObfuscation->setText(tr("Stop Obfuscation")); - - /* show obfuscation configuration if client has defaults set */ - - if (nAnonymizeXDnaAmount == 0) { - ObfuscationConfig dlg(this); - dlg.setModel(walletModel); - dlg.exec(); - } - } + return; } diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index bd7ed83..0b551a5 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/paymentrequestplus.h b/src/qt/paymentrequestplus.h index 72b2b96..801882e 100644 --- a/src/qt/paymentrequestplus.h +++ b/src/qt/paymentrequestplus.h @@ -1,4 +1,5 @@ // Copyright (c) 2011-2014 The Bitcoin developers +// Copyright (c) 2017-2018 The PIVX developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -12,6 +13,8 @@ #include "base58.h" +#include + #include #include #include diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 663c0fc..d94f1fd 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -15,7 +15,7 @@ #include "chainparams.h" #include "ui_interface.h" #include "util.h" -#include "wallet.h" +#include "wallet/wallet.h" #include diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index 9f6ef7a..a0d732a 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -61,7 +61,7 @@ class PeerTablePriv #if QT_VERSION >= 0x040700 cachedNodeStats.reserve(vNodes.size()); #endif - BOOST_FOREACH (CNode* pnode, vNodes) { + for (CNode* pnode : vNodes) { CNodeCombinedStats stats; stats.nodeStateStats.nMisbehavior = 0; stats.nodeStateStats.nSyncHeight = -1; @@ -75,7 +75,7 @@ class PeerTablePriv { TRY_LOCK(cs_main, lockMain); if (lockMain) { - BOOST_FOREACH (CNodeCombinedStats& stats, cachedNodeStats) + for (CNodeCombinedStats& stats : cachedNodeStats) stats.fNodeStateStatsAvailable = GetNodeStateStats(stats.nodeStats.nodeid, stats.nodeStateStats); } } @@ -87,7 +87,7 @@ class PeerTablePriv // build index map mapNodeRows.clear(); int row = 0; - BOOST_FOREACH (CNodeCombinedStats& stats, cachedNodeStats) + for (CNodeCombinedStats& stats : cachedNodeStats) mapNodeRows.insert(std::pair(stats.nodeStats.nodeid, row++)); } diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp index 96f6881..ad182fd 100644 --- a/src/qt/recentrequeststablemodel.cpp +++ b/src/qt/recentrequeststablemodel.cpp @@ -20,7 +20,7 @@ RecentRequestsTableModel::RecentRequestsTableModel(CWallet* wallet, WalletModel* // Load entries from wallet std::vector vReceiveRequests; parent->loadReceiveRequests(vReceiveRequests); - BOOST_FOREACH (const std::string& request, vReceiveRequests) + for (const std::string& request : vReceiveRequests) addNewRequest(request); /* These columns must match the indices in the ColumnIndex enumeration */ diff --git a/src/qt/res/icons/staking_active.png b/src/qt/res/icons/staking_active.png index 9b70fe6..28bc60c 100644 Binary files a/src/qt/res/icons/staking_active.png and b/src/qt/res/icons/staking_active.png differ diff --git a/src/qt/res/icons/staking_inactive.png b/src/qt/res/icons/staking_inactive.png index f182fdf..4db2969 100644 Binary files a/src/qt/res/icons/staking_inactive.png and b/src/qt/res/icons/staking_inactive.png differ diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 97b2639..c6dbeb3 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -14,8 +14,8 @@ #include "chainparams.h" #include "main.h" -#include "rpcclient.h" -#include "rpcserver.h" +#include "rpc/client.h" +#include "rpc/server.h" #include "util.h" #include diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index be4fa41..4af9c13 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -22,7 +22,7 @@ #include "coincontrol.h" #include "ui_interface.h" #include "utilmoneystr.h" -#include "wallet.h" +#include "wallet/wallet.h" #include #include diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index 71cc400..5cd174a 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -13,7 +13,7 @@ #include "base58.h" #include "init.h" -#include "wallet.h" +#include "wallet/wallet.h" #include #include diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 518264c..8f2fbd8 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -15,7 +15,7 @@ #include "version.h" #ifdef ENABLE_WALLET -#include "wallet.h" +#include "wallet/wallet.h" #endif #include @@ -40,7 +40,7 @@ SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle* networkStyle) QString copyrightTextBtc = QChar(0xA9) + QString(" 2009-2014 ").arg(COPYRIGHT_YEAR) + QString(tr("The Bitcoin Core developers")); QString copyrightTextDash = QChar(0xA9) + QString(" 2014-2015 ").arg(COPYRIGHT_YEAR) + QString(tr("The Dash Core developers")); QString copyrightTextPIVX = QChar(0xA9) + QString(" 2015-2017 ").arg(COPYRIGHT_YEAR) + QString(tr("The PIVX Core developers")); - QString copyrightTextXDNA = QChar(0xA9) + QString(" 2017-2018 ").arg(COPYRIGHT_YEAR) + QString(tr("The XDNA Core developers")); + QString copyrightTextXDNA = QChar(0xA9) + QString(" 2017-2019 ").arg(COPYRIGHT_YEAR) + QString(tr("The XDNA Core developers")); QString titleAddText = networkStyle->getTitleAddText(); QString font = QApplication::font().toString(); @@ -48,7 +48,7 @@ SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle* networkStyle) this->setWindowFlags(Qt::FramelessWindowHint); // load the bitmap for writing some text over it pixmap = networkStyle->getSplashImage(); - + QPainter pixPaint(&pixmap); pixPaint.setPen(QColor(255, 255, 255)); @@ -96,7 +96,7 @@ SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle* networkStyle) QRect r(QPoint(), pixmap.size()); resize(r.size()); setFixedSize(r.size()); - + move(QApplication::desktop()->screenGeometry().center() - r.center()); subscribeToCoreSignals(); diff --git a/src/qt/toolspage.cpp b/src/qt/toolspage.cpp index b2169b5..56442c4 100644 --- a/src/qt/toolspage.cpp +++ b/src/qt/toolspage.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -15,8 +15,8 @@ #include "chainparams.h" #include "main.h" -#include "rpcclient.h" -#include "rpcserver.h" +#include "rpc/client.h" +#include "rpc/server.h" #include "util.h" #include diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index dccf79b..c2e4f6b 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -18,7 +18,7 @@ #include "timedata.h" #include "ui_interface.h" #include "util.h" -#include "wallet.h" +#include "wallet/wallet.h" #include #include @@ -159,7 +159,7 @@ QString TransactionDesc::toHTML(CWallet* wallet, CWalletTx& wtx, TransactionReco // Coinbase // CAmount nUnmatured = 0; - BOOST_FOREACH (const CTxOut& txout, wtx.vout) + for (const CTxOut& txout: wtx.vout) nUnmatured += wallet->GetCredit(txout, ISMINE_ALL); strHTML += "" + tr("Credit") + ": "; if (wtx.IsInMainChain()) @@ -174,13 +174,13 @@ QString TransactionDesc::toHTML(CWallet* wallet, CWalletTx& wtx, TransactionReco strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, nNet) + "
"; } else { isminetype fAllFromMe = ISMINE_SPENDABLE; - BOOST_FOREACH (const CTxIn& txin, wtx.vin) { + for (const CTxIn& txin: wtx.vin) { isminetype mine = wallet->IsMine(txin); if (fAllFromMe > mine) fAllFromMe = mine; } isminetype fAllToMe = ISMINE_SPENDABLE; - BOOST_FOREACH (const CTxOut& txout, wtx.vout) { + for (const CTxOut& txout: wtx.vout) { isminetype mine = wallet->IsMine(txout); if (fAllToMe > mine) fAllToMe = mine; } @@ -192,7 +192,7 @@ QString TransactionDesc::toHTML(CWallet* wallet, CWalletTx& wtx, TransactionReco // // Debit // - BOOST_FOREACH (const CTxOut& txout, wtx.vout) { + for (const CTxOut& txout: wtx.vout) { // Ignore change isminetype toSelf = wallet->IsMine(txout); if ((toSelf == ISMINE_SPENDABLE) && (fAllFromMe == ISMINE_SPENDABLE)) @@ -234,10 +234,10 @@ QString TransactionDesc::toHTML(CWallet* wallet, CWalletTx& wtx, TransactionReco // // Mixed debit transaction // - BOOST_FOREACH (const CTxIn& txin, wtx.vin) + for (const CTxIn& txin: wtx.vin) if (wallet->IsMine(txin)) strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "
"; - BOOST_FOREACH (const CTxOut& txout, wtx.vout) + for (const CTxOut& txout: wtx.vout) if (wallet->IsMine(txout)) strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "
"; } @@ -284,10 +284,10 @@ QString TransactionDesc::toHTML(CWallet* wallet, CWalletTx& wtx, TransactionReco // if (fDebug) { strHTML += "

" + tr("Debug information") + "

"; - BOOST_FOREACH (const CTxIn& txin, wtx.vin) + for (const CTxIn& txin: wtx.vin) if (wallet->IsMine(txin)) strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "
"; - BOOST_FOREACH (const CTxOut& txout, wtx.vout) + for (const CTxOut& txout: wtx.vout) if (wallet->IsMine(txout)) strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "
"; @@ -297,7 +297,7 @@ QString TransactionDesc::toHTML(CWallet* wallet, CWalletTx& wtx, TransactionReco strHTML += "
" + tr("Inputs") + ":"; strHTML += "
    "; - BOOST_FOREACH (const CTxIn& txin, wtx.vin) { + for (const CTxIn& txin: wtx.vin) { COutPoint prevout = txin.prevout; CCoins prev; diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index a2e1d78..73c5b21 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -11,7 +11,7 @@ #include "obfuscation.h" #include "swifttx.h" #include "timedata.h" -#include "wallet.h" +#include "wallet/wallet.h" #include @@ -74,7 +74,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet* // // Credit // - BOOST_FOREACH (const CTxOut& txout, wtx.vout) { + for (const CTxOut& txout : wtx.vout) { isminetype mine = wallet->IsMine(txout); if (mine) { TransactionRecord sub(hash, nTime); @@ -104,7 +104,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet* int nFromMe = 0; bool involvesWatchAddress = false; isminetype fAllFromMe = ISMINE_SPENDABLE; - BOOST_FOREACH (const CTxIn& txin, wtx.vin) { + for (const CTxIn& txin : wtx.vin) { if (wallet->IsMine(txin)) { fAllFromMeDenom = fAllFromMeDenom && wallet->IsDenominated(txin); nFromMe++; @@ -117,7 +117,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet* isminetype fAllToMe = ISMINE_SPENDABLE; bool fAllToMeDenom = true; int nToMe = 0; - BOOST_FOREACH (const CTxOut& txout, wtx.vout) { + for (const CTxOut& txout : wtx.vout) { if (wallet->IsMine(txout)) { fAllToMeDenom = fAllToMeDenom && wallet->IsDenominatedAmount(txout.nValue); nToMe++; diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 4c052ab..c003685 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -16,7 +16,7 @@ #include "sync.h" #include "uint256.h" #include "util.h" -#include "wallet.h" +#include "wallet/wallet.h" #include #include diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index d7875b2..2d69918 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index 1594d48..9b23a77 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index 4aa52ec..caa2e28 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h index c39095e..547b7c1 100644 --- a/src/qt/walletframe.h +++ b/src/qt/walletframe.h @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -17,7 +17,6 @@ class ClientModel; class SendCoinsRecipient; class WalletModel; class WalletView; -class TradingDialog; class BlockExplorer; class ToolsPage; diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index c87e61d..c334908 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -19,8 +19,8 @@ #include "spork.h" #include "sync.h" #include "ui_interface.h" -#include "wallet.h" -#include "walletdb.h" // for BackupWallet +#include "wallet/wallet.h" +#include "wallet/walletdb.h" // for BackupWallet #include #include @@ -63,7 +63,7 @@ CAmount WalletModel::getBalance(const CCoinControl* coinControl) const CAmount nBalance = 0; std::vector vCoins; wallet->AvailableCoins(vCoins, true, coinControl); - BOOST_FOREACH (const COutput& out, vCoins) + for (const COutput& out : vCoins) if (out.fSpendable) nBalance += out.tx->vout[out.i].nValue; @@ -76,7 +76,7 @@ CAmount WalletModel::getBalance(const CCoinControl* coinControl) const CAmount WalletModel::getAnonymizedBalance() const { - return wallet->GetAnonymizedBalance(); + return(0); } CAmount WalletModel::getUnconfirmedBalance() const @@ -129,12 +129,11 @@ void WalletModel::pollBalanceChanged() if (!lockWallet) return; - if (fForceCheckBalanceChanged || chainActive.Height() != cachedNumBlocks || nObfuscationRounds != cachedObfuscationRounds || cachedTxLocks != nCompleteTXLocks) { + if (fForceCheckBalanceChanged || chainActive.Height() != cachedNumBlocks || cachedTxLocks != nCompleteTXLocks) { fForceCheckBalanceChanged = false; // Balance and number of transactions might have changed cachedNumBlocks = chainActive.Height(); - cachedObfuscationRounds = nObfuscationRounds; checkBalanceChanged(); if (transactionTableModel) { @@ -591,7 +590,7 @@ bool WalletModel::getPubKey(const CKeyID& address, CPubKey& vchPubKeyOut) const void WalletModel::getOutputs(const std::vector& vOutpoints, std::vector& vOutputs) { LOCK2(cs_main, wallet->cs_wallet); - BOOST_FOREACH (const COutPoint& outpoint, vOutpoints) { + for (const COutPoint& outpoint : vOutpoints) { if (!wallet->mapWallet.count(outpoint.hash)) continue; int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); if (nDepth < 0) continue; @@ -617,7 +616,7 @@ void WalletModel::listCoins(std::map >& mapCoins) wallet->ListLockedCoins(vLockedCoins); // add locked coins - BOOST_FOREACH (const COutPoint& outpoint, vLockedCoins) { + for (const COutPoint& outpoint : vLockedCoins) { if (!wallet->mapWallet.count(outpoint.hash)) continue; int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); if (nDepth < 0) continue; @@ -626,7 +625,7 @@ void WalletModel::listCoins(std::map >& mapCoins) vCoins.push_back(out); } - BOOST_FOREACH (const COutput& out, vCoins) { + for (const COutput& out : vCoins) { COutput cout = out; while (wallet->IsChange(cout.tx->vout[cout.i]) && cout.tx->vin.size() > 0 && wallet->IsMine(cout.tx->vin[0])) { @@ -668,8 +667,8 @@ void WalletModel::listLockedCoins(std::vector& vOutpts) void WalletModel::loadReceiveRequests(std::vector& vReceiveRequests) { LOCK(wallet->cs_wallet); - BOOST_FOREACH (const PAIRTYPE(CTxDestination, CAddressBookData) & item, wallet->mapAddressBook) - BOOST_FOREACH (const PAIRTYPE(std::string, std::string) & item2, item.second.destdata) + for (const PAIRTYPE(CTxDestination, CAddressBookData) & item : wallet->mapAddressBook) + for (const PAIRTYPE(std::string, std::string) & item2 : item.second.destdata) if (item2.first.size() > 2 && item2.first.substr(0, 2) == "rr") // receive request vReceiveRequests.push_back(item2.second); } diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 290adc2..087c467 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -13,7 +13,7 @@ #include "allocators.h" /* for SecureString */ #include "swifttx.h" -#include "wallet.h" +#include "wallet/wallet.h" #include #include diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp index deced7ad..1a6d20a 100644 --- a/src/qt/walletmodeltransaction.cpp +++ b/src/qt/walletmodeltransaction.cpp @@ -4,7 +4,7 @@ #include "walletmodeltransaction.h" -#include "wallet.h" +#include "wallet/wallet.h" WalletModelTransaction::WalletModelTransaction(const QList& recipients) : recipients(recipients), walletTransaction(0), diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index bd60e4d..f4c4462 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/walletview.h b/src/qt/walletview.h index 26166f3..397d3bd 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -1,7 +1,7 @@ // Copyright (c) 2011-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/xdna.cpp b/src/qt/xdna.cpp index 2aae4fb..743cad4 100644 --- a/src/qt/xdna.cpp +++ b/src/qt/xdna.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -30,12 +30,12 @@ #include "init.h" #include "main.h" -#include "rpcserver.h" +#include "rpc/server.h" #include "ui_interface.h" #include "util.h" #ifdef ENABLE_WALLET -#include "wallet.h" +#include "wallet/wallet.h" #endif #include @@ -186,7 +186,7 @@ public slots: void runawayException(const QString& message); private: - boost::thread_group threadGroup; + //boost::thread_group threadGroup; /// Flag indicating a restart bool execute_restart; @@ -271,7 +271,7 @@ void BitcoinCore::initialize() try { qDebug() << __func__ << ": Running AppInit2 in thread"; - int rv = AppInit2(threadGroup); + int rv = AppInit2(); if (rv) { /* Start a dummy RPC thread if no RPC thread is active yet * to handle timeouts. @@ -292,8 +292,9 @@ void BitcoinCore::restart(QStringList args) execute_restart = false; try { qDebug() << __func__ << ": Running Restart in thread"; - threadGroup.interrupt_all(); - threadGroup.join_all(); + //threadGroup.interrupt_all(); + //threadGroup.join_all(); + Interrupt(); PrepareShutdown(); qDebug() << __func__ << ": Shutdown finished"; emit shutdownResult(1); @@ -313,8 +314,9 @@ void BitcoinCore::shutdown() { try { qDebug() << __func__ << ": Running Shutdown in thread"; - threadGroup.interrupt_all(); - threadGroup.join_all(); + //threadGroup.interrupt_all(); + //threadGroup.join_all(); + Interrupt(); Shutdown(); qDebug() << __func__ << ": Shutdown finished"; emit shutdownResult(1); diff --git a/src/rest.cpp b/src/rest.cpp index 55da5a6..969b981 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -6,7 +6,7 @@ #include "main.h" #include "primitives/block.h" #include "primitives/transaction.h" -#include "rpcserver.h" +#include "rpc/server.h" #include "streams.h" #include "sync.h" #include "utilstrencodings.h" diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp new file mode 100644 index 0000000..999f7a6 --- /dev/null +++ b/src/rpc/blockchain.cpp @@ -0,0 +1,742 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Copyright (c) 2014-2015 The Dash developers +// Copyright (c) 2015-2017 The PIVX developers +// Copyright (c) 2017-2019 The XDNA Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "checkpoints.h" +#include "main.h" +#include "rpc/server.h" +#include "sync.h" +#include "util.h" + +#include +#include + +using namespace std; + +struct CUpdatedBlock +{ + uint256 hash; + int height; +}; +static std::mutex cs_blockchange; +static std::condition_variable cond_blockchange; +static CUpdatedBlock latestblock; + +extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); +void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); + +double GetDifficulty(const CBlockIndex* blockindex) +{ + // Floating point number that is a multiple of the minimum difficulty, + // minimum difficulty = 1.0. + if (blockindex == NULL) { + if (chainActive.Tip() == NULL) + return 1.0; + else + blockindex = chainActive.Tip(); + } + + int nShift = (blockindex->nBits >> 24) & 0xff; + + double dDiff = + (double)0x0000ffff / (double)(blockindex->nBits & 0x00ffffff); + + while (nShift < 29) { + dDiff *= 256.0; + nShift++; + } + while (nShift > 29) { + dDiff /= 256.0; + nShift--; + } + + return dDiff; +} + + +UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false) +{ + UniValue result(UniValue::VOBJ); + result.push_back(Pair("hash", block.GetHash().GetHex())); + int confirmations = -1; + // Only report confirmations if the block is on the main chain + if (chainActive.Contains(blockindex)) + confirmations = chainActive.Height() - blockindex->nHeight + 1; + result.push_back(Pair("confirmations", confirmations)); + result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); + result.push_back(Pair("height", blockindex->nHeight)); + result.push_back(Pair("version", block.nVersion)); + result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); + UniValue txs(UniValue::VARR); + for (const CTransaction& tx : block.vtx) { + if (txDetails) { + UniValue objTx(UniValue::VOBJ); + TxToJSON(tx, uint256(0), objTx); + txs.push_back(objTx); + } else + txs.push_back(tx.GetHash().GetHex()); + } + result.push_back(Pair("tx", txs)); + result.push_back(Pair("time", block.GetBlockTime())); + result.push_back(Pair("nonce", (uint64_t)block.nNonce)); + result.push_back(Pair("bits", strprintf("%08x", block.nBits))); + result.push_back(Pair("difficulty", GetDifficulty(blockindex))); + result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); + + if (blockindex->pprev) + result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); + CBlockIndex* pnext = chainActive.Next(blockindex); + if (pnext) + result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); + return result; +} + + +UniValue blockHeaderToJSON(const CBlock& block, const CBlockIndex* blockindex) +{ + UniValue result(UniValue::VOBJ); + result.push_back(Pair("version", block.nVersion)); + if (blockindex->pprev) + result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); + result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); + result.push_back(Pair("time", block.GetBlockTime())); + result.push_back(Pair("bits", strprintf("%08x", block.nBits))); + result.push_back(Pair("nonce", (uint64_t)block.nNonce)); + return result; +} + + +UniValue getblockcount(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getblockcount\n" + "\nReturns the number of blocks in the longest block chain.\n" + "\nResult:\n" + "n (numeric) The current block count\n" + "\nExamples:\n" + + HelpExampleCli("getblockcount", "") + HelpExampleRpc("getblockcount", "")); + + LOCK(cs_main); + return chainActive.Height(); +} + +UniValue getbestblockhash(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getbestblockhash\n" + "\nReturns the hash of the best (tip) block in the longest block chain.\n" + "\nResult\n" + "\"hex\" (string) the block hash hex encoded\n" + "\nExamples\n" + + HelpExampleCli("getbestblockhash", "") + HelpExampleRpc("getbestblockhash", "")); + + LOCK(cs_main); + return chainActive.Tip()->GetBlockHash().GetHex(); +} + +void RPCNotifyBlockChange(const uint256 hashBlock) +{ + CBlockIndex* pindex = nullptr; + pindex = mapBlockIndex.at(hashBlock); + if(pindex) { + std::lock_guard lock(cs_blockchange); + latestblock.hash = pindex->GetBlockHash(); + latestblock.height = pindex->nHeight; + } + cond_blockchange.notify_all(); +} +UniValue getdifficulty(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getdifficulty\n" + "\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n" + "\nResult:\n" + "n.nnn (numeric) the proof-of-work difficulty as a multiple of the minimum difficulty.\n" + "\nExamples:\n" + + HelpExampleCli("getdifficulty", "") + HelpExampleRpc("getdifficulty", "")); + + LOCK(cs_main); + return GetDifficulty(); +} + + +UniValue getrawmempool(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getrawmempool ( verbose )\n" + "\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n" + "\nArguments:\n" + "1. verbose (boolean, optional, default=false) true for a json object, false for array of transaction ids\n" + "\nResult: (for verbose = false):\n" + "[ (json array of string)\n" + " \"transactionid\" (string) The transaction id\n" + " ,...\n" + "]\n" + "\nResult: (for verbose = true):\n" + "{ (json object)\n" + " \"transactionid\" : { (json object)\n" + " \"size\" : n, (numeric) transaction size in bytes\n" + " \"fee\" : n, (numeric) transaction fee in xdna\n" + " \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n" + " \"height\" : n, (numeric) block height when transaction entered pool\n" + " \"startingpriority\" : n, (numeric) priority when transaction entered pool\n" + " \"currentpriority\" : n, (numeric) transaction priority now\n" + " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n" + " \"transactionid\", (string) parent transaction id\n" + " ... ]\n" + " }, ...\n" + "]\n" + "\nExamples\n" + + HelpExampleCli("getrawmempool", "true") + HelpExampleRpc("getrawmempool", "true")); + + bool fVerbose = false; + if (params.size() > 0) + fVerbose = params[0].get_bool(); + + if (fVerbose) { + LOCK(mempool.cs); + UniValue o(UniValue::VOBJ); + for (const PAIRTYPE(uint256, CTxMemPoolEntry) & entry : mempool.mapTx) { + const uint256& hash = entry.first; + const CTxMemPoolEntry& e = entry.second; + UniValue info(UniValue::VOBJ); + info.push_back(Pair("size", (int)e.GetTxSize())); + info.push_back(Pair("fee", ValueFromAmount(e.GetFee()))); + info.push_back(Pair("time", e.GetTime())); + info.push_back(Pair("height", (int)e.GetHeight())); + info.push_back(Pair("startingpriority", e.GetPriority(e.GetHeight()))); + info.push_back(Pair("currentpriority", e.GetPriority(chainActive.Height()))); + const CTransaction& tx = e.GetTx(); + set setDepends; + for (const CTxIn& txin : tx.vin) { + if (mempool.exists(txin.prevout.hash)) + setDepends.insert(txin.prevout.hash.ToString()); + } + + UniValue depends(UniValue::VARR); + for (const string& dep : setDepends) { + depends.push_back(dep); + } + + info.push_back(Pair("depends", depends)); + o.push_back(Pair(hash.ToString(), info)); + } + return o; + } else { + vector vtxid; + mempool.queryHashes(vtxid); + + UniValue a(UniValue::VARR); + for (const uint256& hash : vtxid) + a.push_back(hash.ToString()); + + return a; + } +} + +UniValue getblockhash(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getblockhash index\n" + "\nReturns hash of block in best-block-chain at index provided.\n" + "\nArguments:\n" + "1. index (numeric, required) The block index\n" + "\nResult:\n" + "\"hash\" (string) The block hash\n" + "\nExamples:\n" + + HelpExampleCli("getblockhash", "1000") + HelpExampleRpc("getblockhash", "1000")); + + LOCK(cs_main); + + int nHeight = params[0].get_int(); + if (nHeight < 0 || nHeight > chainActive.Height()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); + + CBlockIndex* pblockindex = chainActive[nHeight]; + return pblockindex->GetBlockHash().GetHex(); +} + +UniValue getblock(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getblock \"hash\" ( verbose )\n" + "\nIf verbose is false, returns a string that is serialized, hex-encoded data for block 'hash'.\n" + "If verbose is true, returns an Object with information about block .\n" + "\nArguments:\n" + "1. \"hash\" (string, required) The block hash\n" + "2. verbose (boolean, optional, default=true) true for a json object, false for the hex encoded data\n" + "\nResult (for verbose = true):\n" + "{\n" + " \"hash\" : \"hash\", (string) the block hash (same as provided)\n" + " \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n" + " \"size\" : n, (numeric) The block size\n" + " \"height\" : n, (numeric) The block height or index\n" + " \"version\" : n, (numeric) The block version\n" + " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" + " \"tx\" : [ (array of string) The transaction ids\n" + " \"transactionid\" (string) The transaction id\n" + " ,...\n" + " ],\n" + " \"time\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"nonce\" : n, (numeric) The nonce\n" + " \"bits\" : \"1d00ffff\", (string) The bits\n" + " \"difficulty\" : x.xxx, (numeric) The difficulty\n" + " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" + " \"nextblockhash\" : \"hash\" (string) The hash of the next block\n" + "}\n" + "\nResult (for verbose=false):\n" + "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n" + "\nExamples:\n" + + HelpExampleCli("getblock", "\"00000000000fd08c2fb661d2fcb0d49abb3a91e5f27082ce64feed3b4dede2e2\"") + HelpExampleRpc("getblock", "\"00000000000fd08c2fb661d2fcb0d49abb3a91e5f27082ce64feed3b4dede2e2\"")); + LOCK(cs_main); + + std::string strHash = params[0].get_str(); + uint256 hash(strHash); + + bool fVerbose = true; + if (params.size() > 1) + fVerbose = params[1].get_bool(); + + if (mapBlockIndex.count(hash) == 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + + CBlock block; + CBlockIndex* pblockindex = mapBlockIndex[hash]; + + if (!ReadBlockFromDisk(block, pblockindex)) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); + + if (!fVerbose) { + CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); + ssBlock << block; + std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()); + return strHex; + } + + return blockToJSON(block, pblockindex); +} + +UniValue getblockheader(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getblockheader \"hash\" ( verbose )\n" + "\nIf verbose is false, returns a string that is serialized, hex-encoded data for block 'hash' header.\n" + "If verbose is true, returns an Object with information about block header.\n" + "\nArguments:\n" + "1. \"hash\" (string, required) The block hash\n" + "2. verbose (boolean, optional, default=true) true for a json object, false for the hex encoded data\n" + "\nResult (for verbose = true):\n" + "{\n" + " \"version\" : n, (numeric) The block version\n" + " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" + " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" + " \"time\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"bits\" : \"1d00ffff\", (string) The bits\n" + " \"nonce\" : n, (numeric) The nonce\n" + "}\n" + "\nResult (for verbose=false):\n" + "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash' header.\n" + "\nExamples:\n" + + HelpExampleCli("getblockheader", "\"00000000000fd08c2fb661d2fcb0d49abb3a91e5f27082ce64feed3b4dede2e2\"") + HelpExampleRpc("getblockheader", "\"00000000000fd08c2fb661d2fcb0d49abb3a91e5f27082ce64feed3b4dede2e2\"")); + + std::string strHash = params[0].get_str(); + uint256 hash(strHash); + + bool fVerbose = true; + if (params.size() > 1) + fVerbose = params[1].get_bool(); + + if (mapBlockIndex.count(hash) == 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + + CBlock block; + CBlockIndex* pblockindex = mapBlockIndex[hash]; + + if (!ReadBlockFromDisk(block, pblockindex)) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); + + if (!fVerbose) { + CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); + ssBlock << block.GetBlockHeader(); + std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()); + return strHex; + } + + return blockHeaderToJSON(block, pblockindex); +} + +UniValue gettxoutsetinfo(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "gettxoutsetinfo\n" + "\nReturns statistics about the unspent transaction output set.\n" + "Note this call may take some time.\n" + "\nResult:\n" + "{\n" + " \"height\":n, (numeric) The current block height (index)\n" + " \"bestblock\": \"hex\", (string) the best block hash hex\n" + " \"transactions\": n, (numeric) The number of transactions\n" + " \"txouts\": n, (numeric) The number of output transactions\n" + " \"bytes_serialized\": n, (numeric) The serialized size\n" + " \"hash_serialized\": \"hash\", (string) The serialized hash\n" + " \"total_amount\": x.xxx (numeric) The total amount\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("gettxoutsetinfo", "") + HelpExampleRpc("gettxoutsetinfo", "")); + + LOCK(cs_main); + + UniValue ret(UniValue::VOBJ); + + CCoinsStats stats; + FlushStateToDisk(); + if (pcoinsTip->GetStats(stats)) { + ret.push_back(Pair("height", (int64_t)stats.nHeight)); + ret.push_back(Pair("bestblock", stats.hashBlock.GetHex())); + ret.push_back(Pair("transactions", (int64_t)stats.nTransactions)); + ret.push_back(Pair("txouts", (int64_t)stats.nTransactionOutputs)); + ret.push_back(Pair("bytes_serialized", (int64_t)stats.nSerializedSize)); + ret.push_back(Pair("hash_serialized", stats.hashSerialized.GetHex())); + ret.push_back(Pair("total_amount", ValueFromAmount(stats.nTotalAmount))); + } + return ret; +} + +UniValue gettxout(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 3) + throw runtime_error( + "gettxout \"txid\" n ( includemempool )\n" + "\nReturns details about an unspent transaction output.\n" + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction id\n" + "2. n (numeric, required) vout value\n" + "3. includemempool (boolean, optional) Whether to included the mem pool\n" + "\nResult:\n" + "{\n" + " \"bestblock\" : \"hash\", (string) the block hash\n" + " \"confirmations\" : n, (numeric) The number of confirmations\n" + " \"value\" : x.xxx, (numeric) The transaction value in btc\n" + " \"scriptPubKey\" : { (json object)\n" + " \"asm\" : \"code\", (string) \n" + " \"hex\" : \"hex\", (string) \n" + " \"reqSigs\" : n, (numeric) Number of required signatures\n" + " \"type\" : \"pubkeyhash\", (string) The type, eg pubkeyhash\n" + " \"addresses\" : [ (array of string) array of xdna addresses\n" + " \"xdnaaddress\" (string) xdna address\n" + " ,...\n" + " ]\n" + " },\n" + " \"version\" : n, (numeric) The version\n" + " \"coinbase\" : true|false (boolean) Coinbase or not\n" + "}\n" + + "\nExamples:\n" + "\nGet unspent transactions\n" + + HelpExampleCli("listunspent", "") + + "\nView the details\n" + HelpExampleCli("gettxout", "\"txid\" 1") + + "\nAs a json rpc call\n" + HelpExampleRpc("gettxout", "\"txid\", 1")); + LOCK(cs_main); + + UniValue ret(UniValue::VOBJ); + + std::string strHash = params[0].get_str(); + uint256 hash(strHash); + int n = params[1].get_int(); + bool fMempool = true; + if (params.size() > 2) + fMempool = params[2].get_bool(); + + CCoins coins; + if (fMempool) { + LOCK(mempool.cs); + CCoinsViewMemPool view(pcoinsTip, mempool); + if (!view.GetCoins(hash, coins)) + return NullUniValue; + mempool.pruneSpent(hash, coins); // TODO: this should be done by the CCoinsViewMemPool + } else { + if (!pcoinsTip->GetCoins(hash, coins)) + return NullUniValue; + } + if (n < 0 || (unsigned int)n >= coins.vout.size() || coins.vout[n].IsNull()) + return NullUniValue; + + BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); + CBlockIndex* pindex = it->second; + ret.push_back(Pair("bestblock", pindex->GetBlockHash().GetHex())); + if ((unsigned int)coins.nHeight == MEMPOOL_HEIGHT) + ret.push_back(Pair("confirmations", 0)); + else + ret.push_back(Pair("confirmations", pindex->nHeight - coins.nHeight + 1)); + ret.push_back(Pair("value", ValueFromAmount(coins.vout[n].nValue))); + UniValue o(UniValue::VOBJ); + ScriptPubKeyToJSON(coins.vout[n].scriptPubKey, o, true); + ret.push_back(Pair("scriptPubKey", o)); + ret.push_back(Pair("version", coins.nVersion)); + ret.push_back(Pair("coinbase", coins.fCoinBase)); + + return ret; +} + +UniValue verifychain(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "verifychain ( checklevel numblocks )\n" + "\nVerifies blockchain database.\n" + "\nArguments:\n" + "1. checklevel (numeric, optional, 0-4, default=3) How thorough the block verification is.\n" + "2. numblocks (numeric, optional, default=288, 0=all) The number of blocks to check.\n" + "\nResult:\n" + "true|false (boolean) Verified or not\n" + "\nExamples:\n" + + HelpExampleCli("verifychain", "") + HelpExampleRpc("verifychain", "")); + + LOCK(cs_main); + + int nCheckLevel = GetArg("-checklevel", 3); + int nCheckDepth = GetArg("-checkblocks", 288); + if (params.size() > 0) + nCheckLevel = params[0].get_int(); + if (params.size() > 1) + nCheckDepth = params[1].get_int(); + + return CVerifyDB().VerifyDB(pcoinsTip, nCheckLevel, nCheckDepth); +} + +UniValue getblockchaininfo(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getblockchaininfo\n" + "Returns an object containing various state info regarding block chain processing.\n" + "\nResult:\n" + "{\n" + " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" + " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" + " \"headers\": xxxxxx, (numeric) the current number of headers we have validated\n" + " \"bestblockhash\": \"...\", (string) the hash of the currently best block\n" + " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" + " \"verificationprogress\": xxxx, (numeric) estimate of verification progress [0..1]\n" + " \"chainwork\": \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getblockchaininfo", "") + HelpExampleRpc("getblockchaininfo", "")); + + LOCK(cs_main); + + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("chain", Params().NetworkIDString())); + obj.push_back(Pair("blocks", (int)chainActive.Height())); + obj.push_back(Pair("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1)); + obj.push_back(Pair("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex())); + obj.push_back(Pair("difficulty", (double)GetDifficulty())); + obj.push_back(Pair("verificationprogress", Checkpoints::GuessVerificationProgress(chainActive.Tip()))); + obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex())); + return obj; +} + +/** Comparison function for sorting the getchaintips heads. */ +struct CompareBlocksByHeight { + bool operator()(const CBlockIndex* a, const CBlockIndex* b) const + { + /* Make sure that unequal blocks with the same height do not compare + equal. Use the pointers themselves to make a distinction. */ + + if (a->nHeight != b->nHeight) + return (a->nHeight > b->nHeight); + + return a < b; + } +}; + +UniValue getchaintips(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getchaintips\n" + "Return information about all known tips in the block tree," + " including the main chain as well as orphaned branches.\n" + "\nResult:\n" + "[\n" + " {\n" + " \"height\": xxxx, (numeric) height of the chain tip\n" + " \"hash\": \"xxxx\", (string) block hash of the tip\n" + " \"branchlen\": 0 (numeric) zero for main chain\n" + " \"status\": \"active\" (string) \"active\" for the main chain\n" + " },\n" + " {\n" + " \"height\": xxxx,\n" + " \"hash\": \"xxxx\",\n" + " \"branchlen\": 1 (numeric) length of branch connecting the tip to the main chain\n" + " \"status\": \"xxxx\" (string) status of the chain (active, valid-fork, valid-headers, headers-only, invalid)\n" + " }\n" + "]\n" + "Possible values for status:\n" + "1. \"invalid\" This branch contains at least one invalid block\n" + "2. \"headers-only\" Not all blocks for this branch are available, but the headers are valid\n" + "3. \"valid-headers\" All blocks are available for this branch, but they were never fully validated\n" + "4. \"valid-fork\" This branch is not part of the active chain, but is fully validated\n" + "5. \"active\" This is the tip of the active main chain, which is certainly valid\n" + "\nExamples:\n" + + HelpExampleCli("getchaintips", "") + HelpExampleRpc("getchaintips", "")); + + LOCK(cs_main); + /* Build up a list of chain tips. We start with the list of all + known blocks, and successively remove blocks that appear as pprev + of another block. */ + std::set setTips; + for (const PAIRTYPE(const uint256, CBlockIndex*) & item : mapBlockIndex) + setTips.insert(item.second); + for (const PAIRTYPE(const uint256, CBlockIndex*) & item : mapBlockIndex) { + const CBlockIndex* pprev = item.second->pprev; + if (pprev) + setTips.erase(pprev); + } + + // Always report the currently active tip. + setTips.insert(chainActive.Tip()); + + /* Construct the output array. */ + UniValue res(UniValue::VARR); + for (const CBlockIndex* block : setTips) { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("height", block->nHeight)); + obj.push_back(Pair("hash", block->phashBlock->GetHex())); + + const int branchLen = block->nHeight - chainActive.FindFork(block)->nHeight; + obj.push_back(Pair("branchlen", branchLen)); + + string status; + if (chainActive.Contains(block)) { + // This block is part of the currently active chain. + status = "active"; + } else if (block->nStatus & BLOCK_FAILED_MASK) { + // This block or one of its ancestors is invalid. + status = "invalid"; + } else if (block->nChainTx == 0) { + // This block cannot be connected because full block data for it or one of its parents is missing. + status = "headers-only"; + } else if (block->IsValid(BLOCK_VALID_SCRIPTS)) { + // This block is fully validated, but no longer part of the active chain. It was probably the active block once, but was reorganized. + status = "valid-fork"; + } else if (block->IsValid(BLOCK_VALID_TREE)) { + // The headers for this block are valid, but it has not been validated. It was probably never part of the most-work chain. + status = "valid-headers"; + } else { + // No clue. + status = "unknown"; + } + obj.push_back(Pair("status", status)); + + res.push_back(obj); + } + + return res; +} + +UniValue getmempoolinfo(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getmempoolinfo\n" + "\nReturns details on the active state of the TX memory pool.\n" + "\nResult:\n" + "{\n" + " \"size\": xxxxx (numeric) Current tx count\n" + " \"bytes\": xxxxx (numeric) Sum of all tx sizes\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getmempoolinfo", "") + HelpExampleRpc("getmempoolinfo", "")); + + UniValue ret(UniValue::VOBJ); + ret.push_back(Pair("size", (int64_t)mempool.size())); + ret.push_back(Pair("bytes", (int64_t)mempool.GetTotalTxSize())); + + return ret; +} + +UniValue invalidateblock(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "invalidateblock \"hash\"\n" + "\nPermanently marks a block as invalid, as if it violated a consensus rule.\n" + "\nArguments:\n" + "1. hash (string, required) the hash of the block to mark as invalid\n" + "\nResult:\n" + "\nExamples:\n" + + HelpExampleCli("invalidateblock", "\"blockhash\"") + HelpExampleRpc("invalidateblock", "\"blockhash\"")); + + std::string strHash = params[0].get_str(); + uint256 hash(strHash); + CValidationState state; + + { + LOCK(cs_main); + if (mapBlockIndex.count(hash) == 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + + CBlockIndex* pblockindex = mapBlockIndex[hash]; + InvalidateBlock(state, pblockindex); + } + + if (state.IsValid()) { + ActivateBestChain(state); + } + + if (!state.IsValid()) { + throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); + } + + return NullUniValue; +} + +UniValue reconsiderblock(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "reconsiderblock \"hash\"\n" + "\nRemoves invalidity status of a block and its descendants, reconsider them for activation.\n" + "This can be used to undo the effects of invalidateblock.\n" + "\nArguments:\n" + "1. hash (string, required) the hash of the block to reconsider\n" + "\nResult:\n" + "\nExamples:\n" + + HelpExampleCli("reconsiderblock", "\"blockhash\"") + HelpExampleRpc("reconsiderblock", "\"blockhash\"")); + + std::string strHash = params[0].get_str(); + uint256 hash(strHash); + CValidationState state; + + { + LOCK(cs_main); + if (mapBlockIndex.count(hash) == 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + + CBlockIndex* pblockindex = mapBlockIndex[hash]; + ReconsiderBlock(state, pblockindex); + } + + if (state.IsValid()) { + ActivateBestChain(state); + } + + if (!state.IsValid()) { + throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); + } + + return NullUniValue; +} diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp new file mode 100644 index 0000000..e371e6c --- /dev/null +++ b/src/rpc/client.cpp @@ -0,0 +1,168 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Copyright (c) 2014-2015 The Dash developers +// Copyright (c) 2015-2017 The PIVX developers +// Copyright (c) 2017-2019 The XDNA Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "rpc/client.h" + +#include "rpc/protocol.h" +#include "ui_interface.h" +#include "util.h" + +#include +#include + +#include // for to_lower() +#include + +using namespace std; + +class CRPCConvertParam +{ +public: + std::string methodName; //! method whose params want conversion + int paramIdx; //! 0-based idx of param to convert +}; +// ***TODO*** +static const CRPCConvertParam vRPCConvertParams[] = { + {"stop", 0}, + {"setmocktime", 0}, + {"getaddednodeinfo", 0}, + {"setgenerate", 0}, + {"setgenerate", 1}, + {"getnetworkhashps", 0}, + {"getnetworkhashps", 1}, + {"sendtoaddress", 1}, + {"sendtoaddressix", 1}, + {"settxfee", 0}, + {"getreceivedbyaddress", 1}, + {"getreceivedbyaccount", 1}, + {"listreceivedbyaddress", 0}, + {"listreceivedbyaddress", 1}, + {"listreceivedbyaddress", 2}, + {"listreceivedbyaccount", 0}, + {"listreceivedbyaccount", 1}, + {"listreceivedbyaccount", 2}, + {"getbalance", 1}, + {"getbalance", 2}, + {"getblockhash", 0}, + {"move", 2}, + {"move", 3}, + {"sendfrom", 2}, + {"sendfrom", 3}, + {"listtransactions", 1}, + {"listtransactions", 2}, + {"listtransactions", 3}, + {"listaccounts", 0}, + {"listaccounts", 1}, + {"walletpassphrase", 1}, + {"walletpassphrase", 2}, + {"getblocktemplate", 0}, + {"listsinceblock", 1}, + {"listsinceblock", 2}, + {"sendmany", 1}, + {"sendmany", 2}, + {"addmultisigaddress", 0}, + {"addmultisigaddress", 1}, + {"createmultisig", 0}, + {"createmultisig", 1}, + {"listunspent", 0}, + {"listunspent", 1}, + {"listunspent", 2}, + {"listunspent", 3}, + {"getblock", 1}, + {"getblockheader", 1}, + {"gettransaction", 1}, + {"getrawtransaction", 1}, + {"createrawtransaction", 0}, + {"createrawtransaction", 1}, + {"signrawtransaction", 1}, + {"signrawtransaction", 2}, + {"sendrawtransaction", 1}, + {"sendrawtransaction", 2}, + {"gettxout", 1}, + {"gettxout", 2}, + {"lockunspent", 0}, + {"lockunspent", 1}, + {"importprivkey", 2}, + {"importaddress", 2}, + {"verifychain", 0}, + {"verifychain", 1}, + {"keypoolrefill", 0}, + {"getrawmempool", 0}, + {"estimatefee", 0}, + {"estimatepriority", 0}, + {"prioritisetransaction", 1}, + {"prioritisetransaction", 2}, + {"spork", 1}, + // disabled until removal of the legacy 'masternode' command + //{"startmasternode", 1}, + {"reservebalance", 0}, + {"reservebalance", 1}, + {"setstakesplitthreshold", 0}, + {"autocombinerewards", 0}, + {"autocombinerewards", 1}, + {"obfuscation", 1}, +}; + +class CRPCConvertTable +{ +private: + std::set > members; + +public: + CRPCConvertTable(); + + bool convert(const std::string& method, int idx) + { + return (members.count(std::make_pair(method, idx)) > 0); + } +}; + +CRPCConvertTable::CRPCConvertTable() +{ + const unsigned int n_elem = + (sizeof(vRPCConvertParams) / sizeof(vRPCConvertParams[0])); + + for (unsigned int i = 0; i < n_elem; i++) { + members.insert(std::make_pair(vRPCConvertParams[i].methodName, + vRPCConvertParams[i].paramIdx)); + } +} + +static CRPCConvertTable rpcCvtTable; + +/** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null) + * as well as objects and arrays. + */ +UniValue ParseNonRFCJSONValue(const std::string& strVal) +{ + UniValue jVal; + if (!jVal.read(std::string("[")+strVal+std::string("]")) || + !jVal.isArray() || jVal.size()!=1) + throw runtime_error(string("Error parsing JSON:")+strVal); + return jVal[0]; +} + +/** Convert strings to command-specific RPC representation */ +UniValue RPCConvertValues(const std::string &strMethod, const std::vector &strParams) +{ + UniValue params(UniValue::VARR); + + for (unsigned int idx = 0; idx < strParams.size(); idx++) { + const std::string& strVal = strParams[idx]; + + // insert string value directly + if (!rpcCvtTable.convert(strMethod, idx)) { + params.push_back(strVal); + } else { + // parse string as JSON, insert bool/number/object/etc. value + params.push_back(ParseNonRFCJSONValue(strVal)); + } + } + + return params; +} diff --git a/src/rpc/client.h b/src/rpc/client.h new file mode 100644 index 0000000..14f1cc1 --- /dev/null +++ b/src/rpc/client.h @@ -0,0 +1,17 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 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_RPCCLIENT_H +#define BITCOIN_RPCCLIENT_H + +#include + +UniValue RPCConvertValues(const std::string& strMethod, const std::vector& strParams); +/** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null) + * as well as objects and arrays. + */ +UniValue ParseNonRFCJSONValue(const std::string& strVal); + +#endif // BITCOIN_RPCCLIENT_H diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp new file mode 100644 index 0000000..8b53619 --- /dev/null +++ b/src/rpc/masternode.cpp @@ -0,0 +1,1055 @@ +// Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2015-2017 The PIVX developers +// Copyright (c) 2017-2018 The Bulwark developers +// Copyright (c) 2017-2019 The XDNA Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "activemasternode.h" +#include "db.h" +#include "init.h" +#include "main.h" +#include "masternode-payments.h" +#include "masternodeconfig.h" +#include "masternodeman.h" +#include "rpc/server.h" +#include "utilmoneystr.h" + +#include + +#include +#include + +void ltrim(std::string &s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { + return !std::isspace(ch); + })); +} + +UniValue getpoolinfo(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getpoolinfo\n" + "\nReturns anonymous pool-related information\n" + + "\nResult:\n" + "{\n" + " \"current\": \"addr\", (string) XDNA address of current masternode\n" + " \"state\": xxxx, (string) unknown\n" + " \"entries\": xxxx, (numeric) Number of entries\n" + " \"accepted\": xxxx, (numeric) Number of entries accepted\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("getpoolinfo", "") + HelpExampleRpc("getpoolinfo", "")); + + //fixme: GetCurrentMasterNode add MN level + const auto* currentMasterNode = mnodeman.GetCurrentMasterNode(CMasternode::LevelValue::UNSPECIFIED); + + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("current_masternode", currentMasterNode ? currentMasterNode->addr.ToString() : std::string{"null"})); + obj.push_back(Pair("state", obfuScationPool.GetState())); + obj.push_back(Pair("entries", obfuScationPool.GetEntriesCount())); + obj.push_back(Pair("entries_accepted", obfuScationPool.GetCountEntriesAccepted())); + return obj; +} + +UniValue listmasternodes(const UniValue& params, bool fHelp) +{ + std::string strFilter = ""; + + if (params.size() == 1) strFilter = params[0].get_str(); + + if (fHelp || (params.size() > 1)) + throw runtime_error( + "listmasternodes ( \"filter\" )\n" + "\nGet a ranked list of masternodes\n" + + "\nArguments:\n" + "1. \"filter\" (string, optional) Filter search text. Partial match by txhash, status, or addr.\n" + + "\nResult:\n" + "[\n" + " {\n" + " \"level\": n, (numeric) Masternode Level\n" + " \"rank\": n, (numeric) Masternode Rank (or 0 if not enabled)\n" + " \"txhash\": \"hash\", (string) Collateral transaction hash\n" + " \"outidx\": n, (numeric) Collateral transaction output index\n" + " \"status\": s, (string) Status (ENABLED/EXPIRED/REMOVE/etc)\n" + " \"addr\": \"addr\", (string) Masternode XDNA address\n" + " \"version\": v, (numeric) Masternode protocol version\n" + " \"lastseen\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) of the last seen\n" + " \"activetime\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) masternode has been active\n" + " \"lastpaid\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) masternode was last paid\n" + " }\n" + " ,...\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("masternodelist", "") + HelpExampleRpc("masternodelist", "")); + + UniValue ret(UniValue::VARR); + int nHeight; + { + LOCK(cs_main); + CBlockIndex* pindex = chainActive.Tip(); + if(!pindex) return 0; + nHeight = pindex->nHeight; + } + std::vector > vMasternodeRanks = mnodeman.GetMasternodeRanks(nHeight); + for (PAIRTYPE(int, CMasternode) & s : vMasternodeRanks) { + UniValue obj(UniValue::VOBJ); + std::string strVin = s.second.vin.prevout.ToStringShort(); + std::string strTxHash = s.second.vin.prevout.hash.ToString(); + uint32_t oIdx = s.second.vin.prevout.n; + + CMasternode* mn = mnodeman.Find(s.second.vin); + + if (mn != NULL) { + if (strFilter != "" && strTxHash.find(strFilter) == string::npos && + mn->Status().find(strFilter) == string::npos && + CBitcoinAddress(mn->pubKeyCollateralAddress.GetID()).ToString().find(strFilter) == string::npos) continue; + + std::string strStatus = mn->Status(); + std::string strHost; + int port; + SplitHostPort(mn->addr.ToString(), port, strHost); + CNetAddr node = CNetAddr(strHost, false); + std::string strNetwork = GetNetworkName(node.GetNetwork()); + + obj.push_back(Pair("level", mn->Level())); + obj.push_back(Pair("rank", (strStatus == "ENABLED" ? s.first : 0))); + obj.push_back(Pair("network", strNetwork)); + obj.push_back(Pair("txhash", strTxHash)); + obj.push_back(Pair("outidx", (uint64_t)oIdx)); + obj.push_back(Pair("pubkey", HexStr(mn->pubKeyMasternode))); + obj.push_back(Pair("status", strStatus)); + obj.push_back(Pair("addr", CBitcoinAddress(mn->pubKeyCollateralAddress.GetID()).ToString())); + obj.push_back(Pair("version", mn->protocolVersion)); + obj.push_back(Pair("lastseen", (int64_t)mn->lastPing.sigTime)); + obj.push_back(Pair("activetime", (int64_t)(mn->lastPing.sigTime - mn->sigTime))); + obj.push_back(Pair("lastpaid", (int64_t)mn->GetLastPaid())); + + ret.push_back(obj); + } + } + + return ret; +} + +UniValue masternodeconnect(const UniValue& params, bool fHelp) +{ + if (fHelp || (params.size() != 1)) + throw runtime_error( + "masternodeconnect \"address\"\n" + "\nAttempts to connect to specified masternode address\n" + + "\nArguments:\n" + "1. \"address\" (string, required) IP or net address to connect to\n" + + "\nExamples:\n" + + HelpExampleCli("masternodeconnect", "\"192.168.0.6:1945\"") + HelpExampleRpc("masternodeconnect", "\"192.168.0.6:1945\"")); + + std::string strAddress = params[0].get_str(); + + CService addr = CService(strAddress); + + CNode* pnode = ConnectNode((CAddress)addr, NULL, false); + if (pnode) { + pnode->Release(); + return NullUniValue; + } else { + throw runtime_error("error connecting\n"); + } +} + +UniValue getmasternodecount (const UniValue& params, bool fHelp) +{ + if (fHelp || (params.size() > 0)) + throw runtime_error( + "getmasternodecount\n" + "\nGet masternode count values\n" + + "\nResult:\n" + "{\n" + " \"grandtotal\": n, (numeric) Grand Total masternodes\n" + " \"total\": [\n" + " {\n" + " \"level\": n, (numeric) Masternodes level\n" + " \"count\": n (numeric) Total count\n" + " }\n" + " ,...\n" + " ],\n" + " \"enabled\": [\n" + " {\n" + " \"level\": n, (numeric) Masternodes level\n" + " \"count\": n (numeric) Enabled masternodes\n" + " }\n" + " ,...\n" + " ],\n" + " \"obfcompat\": [\n" + " {\n" + " \"level\": n, (numeric) Masternodes level\n" + " \"count\": n (numeric) Obfuscation Compatible\n" + " }\n" + " ,...\n" + " ],\n" + " \"stable\": [\n" + " {\n" + " \"level\": n, (numeric) Masternodes level\n" + " \"count\": n (numeric) Stable count\n" + " }\n" + " ,...\n" + " ],\n" + " \"inqueue\": [\n" + " {\n" + " \"level\": n, (numeric) Masternodes level\n" + " \"count\": n (numeric) Masternodes in queue\n" + " }\n" + " ,...\n" + " ]\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getmasternodecount", "") + HelpExampleRpc("getmasternodecount", "")); + + UniValue total{UniValue::VARR}; + UniValue stable{UniValue::VARR}; + UniValue obfcompat{UniValue::VARR}; + UniValue enabled{UniValue::VARR}; + UniValue inqueue{UniValue::VARR}; + + auto chain_tip = chainActive.Tip(); + + for(unsigned l = CMasternode::LevelValue::MIN; l <= CMasternode::LevelValue::MAX; ++l) { + + UniValue total_item{UniValue::VOBJ}; + + total_item.push_back(Pair("level", (int64_t)l)); + total_item.push_back(Pair("count", mnodeman.size(l))); + + total.push_back(total_item); + + UniValue stable_item{UniValue::VOBJ}; + + stable_item.push_back(Pair("level", (int64_t)l)); + stable_item.push_back(Pair("count", mnodeman.stable_size(l))); + + stable.push_back(stable_item); + + UniValue enabled_item{UniValue::VOBJ}; + + enabled_item.push_back(Pair("level", (int64_t)l)); + enabled_item.push_back(Pair("count", (int64_t)mnodeman.CountEnabled(l))); + + enabled.push_back(enabled_item); + + UniValue inqueue_item{UniValue::VOBJ}; + + unsigned inqueue_count = 0u; + + if(chain_tip) + mnodeman.GetNextMasternodeInQueueForPayment(chain_tip->nHeight, l, true, inqueue_count); + + inqueue_item.push_back(Pair("level", (int64_t)l)); + inqueue_item.push_back(Pair("count", (int64_t)inqueue_count)); + + inqueue.push_back(inqueue_item); + + UniValue obfcomat_item{UniValue::VOBJ}; + + obfcomat_item.push_back(Pair("level", (int64_t)l)); + obfcomat_item.push_back(Pair("count", (int64_t)mnodeman.CountEnabled(l, ActiveProtocol()))); + + obfcompat.push_back(obfcomat_item); + + } + + UniValue obj(UniValue::VOBJ); + + obj.push_back(Pair("grandtotal", mnodeman.size())); + obj.push_back(Pair("total", total)); + obj.push_back(Pair("enabled", enabled)); + obj.push_back(Pair("obfcompat", obfcompat)); + obj.push_back(Pair("stable", stable)); + obj.push_back(Pair("inqueue", inqueue)); + + return obj; +} + +UniValue masternodecurrent (const UniValue& params, bool fHelp) +{ + if (fHelp || (params.size() != 0)) + throw runtime_error( + "masternodecurrent\n" + "\nGet current masternode winners\n" + + "\nResult:\n" + "{\n" + " \"protocol\": xxxx, (numeric) Protocol version\n" + " \"txhash\": \"xxxx\", (string) Collateral transaction hash\n" + " \"pubkey\": \"xxxx\", (string) MN Public key\n" + " \"lastseen\": xxx, (numeric) Time since epoch of last seen\n" + " \"activeseconds\": xxx, (numeric) Seconds MN has been active\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("masternodecurrent", "") + HelpExampleRpc("masternodecurrent", "")); + + //fixme: GetCurrentMasterNode add MN level + CMasternode* winner = mnodeman.GetCurrentMasterNode(CMasternode::LevelValue::UNSPECIFIED, 1); + if (winner) { + UniValue obj(UniValue::VOBJ); + + obj.push_back(Pair("protocol", (int64_t)winner->protocolVersion)); + obj.push_back(Pair("txhash", winner->vin.prevout.hash.ToString())); + obj.push_back(Pair("pubkey", CBitcoinAddress(winner->pubKeyCollateralAddress.GetID()).ToString())); + obj.push_back(Pair("lastseen", (winner->lastPing == CMasternodePing()) ? winner->sigTime : (int64_t)winner->lastPing.sigTime)); + obj.push_back(Pair("activeseconds", (winner->lastPing == CMasternodePing()) ? 0 : (int64_t)(winner->lastPing.sigTime - winner->sigTime))); + return obj; + } + + throw runtime_error("unknown"); +} + +UniValue masternodedebug (const UniValue& params, bool fHelp) +{ + if (fHelp || (params.size() != 0)) + throw runtime_error( + "masternodedebug\n" + "\nPrint masternode status\n" + + "\nResult:\n" + "\"status\" (string) Masternode status message\n" + "\nExamples:\n" + + HelpExampleCli("masternodedebug", "") + HelpExampleRpc("masternodedebug", "")); + + if (activeMasternode.status != ACTIVE_MASTERNODE_INITIAL || !masternodeSync.IsSynced()) + return activeMasternode.GetStatus(); + + CTxIn vin = CTxIn(); + CPubKey pubkey; + CKey key; + if (!activeMasternode.GetMasterNodeVin(vin, pubkey, key)) + throw runtime_error("Missing masternode input, please look at the documentation for instructions on masternode creation\n"); + else + return activeMasternode.GetStatus(); +} + +UniValue startmasternode (const UniValue& params, bool fHelp) +{ + std::string strCommand; + if (params.size() >= 1) { + strCommand = params[0].get_str(); + + // Backwards compatibility with legacy 'masternode' super-command forwarder + if (strCommand == "start") strCommand = "local"; + if (strCommand == "start-alias") strCommand = "alias"; + if (strCommand == "start-all") strCommand = "all"; + if (strCommand == "start-many") strCommand = "many"; + if (strCommand == "start-missing") strCommand = "missing"; + if (strCommand == "start-disabled") strCommand = "disabled"; + } + + if (fHelp || params.size() < 2 || params.size() > 3 || + (params.size() == 2 && (strCommand != "local" && strCommand != "all" && strCommand != "many" && strCommand != "missing" && strCommand != "disabled")) || + (params.size() == 3 && strCommand != "alias")) + throw runtime_error( + "startmasternode \"local|all|many|missing|disabled|alias\" lockwallet ( \"alias\" )\n" + "\nAttempts to start one or more masternode(s)\n" + + "\nArguments:\n" + "1. set (string, required) Specify which set of masternode(s) to start.\n" + "2. lockwallet (boolean, required) Lock wallet after completion.\n" + "3. alias (string) Masternode alias. Required if using 'alias' as the set.\n" + + "\nResult: (for 'local' set):\n" + "\"status\" (string) Masternode status message\n" + + "\nResult: (for other sets):\n" + "{\n" + " \"overall\": \"xxxx\", (string) Overall status message\n" + " \"detail\": [\n" + " {\n" + " \"node\": \"xxxx\", (string) Node name or alias\n" + " \"result\": \"xxxx\", (string) 'success' or 'failed'\n" + " \"error\": \"xxxx\" (string) Error message, if failed\n" + " }\n" + " ,...\n" + " ]\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("startmasternode", "\"alias\" \"0\" \"my_mn\"") + HelpExampleRpc("startmasternode", "\"alias\" \"0\" \"my_mn\"")); + + bool fLock = (params[1].get_str() == "true" ? true : false); + + if (strCommand == "local") { + if (!fMasterNode) throw runtime_error("you must set masternode=1 in the configuration\n"); + + if (pwalletMain->IsLocked()) + throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); + + if (activeMasternode.status != ACTIVE_MASTERNODE_STARTED) { + activeMasternode.status = ACTIVE_MASTERNODE_INITIAL; // TODO: consider better way + activeMasternode.ManageStatus(); + if (fLock) + pwalletMain->Lock(); + } + return activeMasternode.GetStatus(); + } + + if (strCommand == "all" || strCommand == "many" || strCommand == "missing" || strCommand == "disabled") { + if (pwalletMain->IsLocked()) + throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); + + if ((strCommand == "missing" || strCommand == "disabled") && + (masternodeSync.RequestedMasternodeAssets <= MASTERNODE_SYNC_LIST || + masternodeSync.RequestedMasternodeAssets == MASTERNODE_SYNC_FAILED)) { + throw runtime_error("You can't use this command until masternode list is synced\n"); + } + + std::vector mnEntries; + mnEntries = masternodeConfig.getEntries(); + + int successful = 0; + int failed = 0; + + UniValue resultsObj(UniValue::VARR); + + for (CMasternodeConfig::CMasternodeEntry mne : masternodeConfig.getEntries()) { + std::string errorMessage; + int nIndex; + if(!mne.castOutputIndex(nIndex)) + continue; + CTxIn vin = CTxIn(uint256(mne.getTxHash()), uint32_t(nIndex)); + CMasternode* pmn = mnodeman.Find(vin); + CMasternodeBroadcast mnb; + + if (pmn != NULL) { + if (strCommand == "missing") continue; + if (strCommand == "disabled" && pmn->IsEnabled()) continue; + } + + bool result = activeMasternode.CreateBroadcast(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), errorMessage, mnb); + + UniValue statusObj(UniValue::VOBJ); + statusObj.push_back(Pair("alias", mne.getAlias())); + statusObj.push_back(Pair("result", result ? "success" : "failed")); + + if (result) { + successful++; + statusObj.push_back(Pair("error", "")); + } else { + failed++; + statusObj.push_back(Pair("error", errorMessage)); + } + + resultsObj.push_back(statusObj); + } + if (fLock) + pwalletMain->Lock(); + + UniValue returnObj(UniValue::VOBJ); + returnObj.push_back(Pair("overall", strprintf("Successfully started %d masternodes, failed to start %d, total %d", successful, failed, successful + failed))); + returnObj.push_back(Pair("detail", resultsObj)); + + return returnObj; + } + + if (strCommand == "alias") { + std::string alias = params[2].get_str(); + + if (pwalletMain->IsLocked()) + throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); + + bool found = false; + int successful = 0; + int failed = 0; + + UniValue resultsObj(UniValue::VARR); + UniValue statusObj(UniValue::VOBJ); + statusObj.push_back(Pair("alias", alias)); + + for (CMasternodeConfig::CMasternodeEntry mne : masternodeConfig.getEntries()) { + if (mne.getAlias() == alias) { + found = true; + std::string errorMessage; + CMasternodeBroadcast mnb; + + bool result = activeMasternode.CreateBroadcast(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), errorMessage, mnb); + + statusObj.push_back(Pair("result", result ? "successful" : "failed")); + + if (result) { + successful++; + mnodeman.UpdateMasternodeList(mnb); + mnb.Relay(); + } else { + failed++; + statusObj.push_back(Pair("errorMessage", errorMessage)); + } + break; + } + } + + if (!found) { + failed++; + statusObj.push_back(Pair("result", "failed")); + statusObj.push_back(Pair("error", "could not find alias in config. Verify with list-conf.")); + } + + resultsObj.push_back(statusObj); + + if (fLock) + pwalletMain->Lock(); + + UniValue returnObj(UniValue::VOBJ); + returnObj.push_back(Pair("overall", strprintf("Successfully started %d masternodes, failed to start %d, total %d", successful, failed, successful + failed))); + returnObj.push_back(Pair("detail", resultsObj)); + + return returnObj; + } + return NullUniValue; +} + +UniValue createmasternodekey (const UniValue& params, bool fHelp) +{ + if (fHelp || (params.size() != 0)) + throw runtime_error( + "createmasternodekey\n" + "\nCreate a new masternode private key\n" + + "\nResult:\n" + "\"key\" (string) Masternode private key\n" + "\nExamples:\n" + + HelpExampleCli("createmasternodekey", "") + HelpExampleRpc("createmasternodekey", "")); + + CKey secret; + secret.MakeNewKey(false); + + return CBitcoinSecret(secret).ToString(); +} + +UniValue getmasternodeoutputs (const UniValue& params, bool fHelp) +{ + if (fHelp || (params.size() != 0)) + throw runtime_error( + "getmasternodeoutputs\n" + "\nPrint all masternode transaction outputs\n" + + "\nResult:\n" + "[\n" + " {\n" + " \"txhash\": \"xxxx\", (string) output transaction hash\n" + " \"outputidx\": n (numeric) output index number\n" + " }\n" + " ,...\n" + "]\n" + + "\nExamples:\n" + + HelpExampleCli("getmasternodeoutputs", "") + HelpExampleRpc("getmasternodeoutputs", "")); + + // Find possible candidates + vector possibleCoins = activeMasternode.SelectCoinsMasternode(); + + UniValue ret(UniValue::VARR); + for (COutput& out : possibleCoins) { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("txhash", out.tx->GetHash().ToString())); + obj.push_back(Pair("outputidx", out.i)); + ret.push_back(obj); + } + + return ret; +} + +UniValue listmasternodeconf (const UniValue& params, bool fHelp) +{ + std::string strFilter = ""; + + if (params.size() == 1) strFilter = params[0].get_str(); + + if (fHelp || (params.size() > 1)) + throw runtime_error( + "listmasternodeconf ( \"filter\" )\n" + "\nPrint masternode.conf in JSON format\n" + + "\nArguments:\n" + "1. \"filter\" (string, optional) Filter search text. Partial match on alias, address, txHash, or status.\n" + + "\nResult:\n" + "[\n" + " {\n" + " \"alias\": \"xxxx\", (string) masternode alias\n" + " \"address\": \"xxxx\", (string) masternode IP address\n" + " \"privateKey\": \"xxxx\", (string) masternode private key\n" + " \"txHash\": \"xxxx\", (string) transaction hash\n" + " \"outputIndex\": n, (numeric) transaction output index\n" + " \"status\": \"xxxx\" (string) masternode status\n" + " }\n" + " ,...\n" + "]\n" + + "\nExamples:\n" + + HelpExampleCli("listmasternodeconf", "") + HelpExampleRpc("listmasternodeconf", "")); + + std::vector mnEntries; + mnEntries = masternodeConfig.getEntries(); + + UniValue ret(UniValue::VARR); + + for (CMasternodeConfig::CMasternodeEntry mne : masternodeConfig.getEntries()) { + int nIndex; + if(!mne.castOutputIndex(nIndex)) + continue; + CTxIn vin = CTxIn(uint256(mne.getTxHash()), uint32_t(nIndex)); + CMasternode* pmn = mnodeman.Find(vin); + + std::string strStatus = pmn ? pmn->Status() : "MISSING"; + + if (strFilter != "" && mne.getAlias().find(strFilter) == string::npos && + mne.getIp().find(strFilter) == string::npos && + mne.getTxHash().find(strFilter) == string::npos && + strStatus.find(strFilter) == string::npos) continue; + + UniValue mnObj(UniValue::VOBJ); + mnObj.push_back(Pair("alias", mne.getAlias())); + mnObj.push_back(Pair("address", mne.getIp())); + mnObj.push_back(Pair("privateKey", mne.getPrivKey())); + mnObj.push_back(Pair("txHash", mne.getTxHash())); + mnObj.push_back(Pair("outputIndex", mne.getOutputIndex())); + mnObj.push_back(Pair("status", strStatus)); + ret.push_back(mnObj); + } + + return ret; +} + +UniValue getmasternodestatus (const UniValue& params, bool fHelp) +{ + if (fHelp || (params.size() != 0)) + throw runtime_error( + "getmasternodestatus\n" + "\nPrint masternode status\n" + + "\nResult:\n" + "{\n" + " \"txhash\": \"xxxx\", (string) Collateral transaction hash\n" + " \"outputidx\": n, (numeric) Collateral transaction output index number\n" + " \"netaddr\": \"xxxx\", (string) Masternode network address\n" + " \"addr\": \"xxxx\", (string) XDNA address for masternode payments\n" + " \"status\": \"xxxx\", (string) Masternode status\n" + " \"message\": \"xxxx\" (string) Masternode status message\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("getmasternodestatus", "") + HelpExampleRpc("getmasternodestatus", "")); + + if (!fMasterNode) throw runtime_error("This is not a masternode"); + + CMasternode* pmn = mnodeman.Find(activeMasternode.vin); + + if (pmn) { + UniValue mnObj(UniValue::VOBJ); + mnObj.push_back(Pair("txhash", activeMasternode.vin.prevout.hash.ToString())); + mnObj.push_back(Pair("outputidx", (uint64_t)activeMasternode.vin.prevout.n)); + mnObj.push_back(Pair("netaddr", activeMasternode.service.ToString())); + mnObj.push_back(Pair("addr", CBitcoinAddress(pmn->pubKeyCollateralAddress.GetID()).ToString())); + mnObj.push_back(Pair("status", activeMasternode.status)); + mnObj.push_back(Pair("message", activeMasternode.GetStatus())); + return mnObj; + } + throw runtime_error("Masternode not found in the list of available masternodes. Current status: " + + activeMasternode.GetStatus()); +} + +UniValue getmasternodewinners(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 3) + throw runtime_error( + "getmasternodewinners ( blocks \"filter\" )\n" + "\nPrint the masternode winners for the last n blocks\n" + + "\nArguments:\n" + "1. blocks (numeric, optional) Number of previous blocks to show (default: 10)\n" + "2. filter (string, optional) Search filter matching MN address\n" + + "\nResult (single winner):\n" + "[\n" + " {\n" + " \"nHeight\": n, (numeric) block height\n" + " \"winner\": {\n" + " \"address\": \"xxxx\", (string) XDNA MN Address\n" + " \"level\": n, (numeric) Masternode level\n" + " \"nVotes\": n, (numeric) Number of votes for winner\n" + " }\n" + " }\n" + " ,...\n" + "]\n" + + "\nResult (multiple winners):\n" + "[\n" + " {\n" + " \"nHeight\": n, (numeric) block height\n" + " \"winner\": [\n" + " {\n" + " \"address\": \"xxxx\", (string) XDNA MN Address\n" + " \"level\": n, (numeric) Masternode level\n" + " \"nVotes\": n, (numeric) Number of votes for winner\n" + " }\n" + " ,...\n" + " ]\n" + " }\n" + " ,...\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("getmasternodewinners", "") + HelpExampleRpc("getmasternodewinners", "")); + + int nHeight; + { + LOCK(cs_main); + CBlockIndex* pindex = chainActive.Tip(); + if(!pindex) return 0; + nHeight = pindex->nHeight; + } + + int nLast = 10; + std::string strFilter = ""; + + if(params.size() >= 1) + nLast = atoi(params[0].get_str()); + + if(params.size() == 2) + strFilter = params[1].get_str(); + + UniValue ret(UniValue::VARR); + + for(int i = nHeight - nLast; i < nHeight + 20; i++) { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("nHeight", i)); + + std::string strPayment = GetRequiredPaymentsString(i); + if (strFilter != "" && strPayment.find(strFilter) == std::string::npos) continue; + + if (strPayment.find(',') != std::string::npos) { + UniValue winner(UniValue::VARR); + boost::char_separator sep(","); + boost::tokenizer< boost::char_separator > tokens(strPayment, sep); + for (const string& t : tokens) { + UniValue addr(UniValue::VOBJ); + std::size_t pos1 = t.find(":"); + std::size_t pos2 = t.rfind(":"); + std::string strAddress = t.substr(0, pos1); + ltrim(strAddress); + uint64_t level = atoi(t.substr(pos1 + 1, pos2)); + uint64_t nVotes = atoi(t.substr(pos2 + 1)); + addr.push_back(Pair("address", strAddress)); + addr.push_back(Pair("level", level)); + addr.push_back(Pair("nVotes", nVotes)); + winner.push_back(addr); + } + obj.push_back(Pair("winner", winner)); + } else if (strPayment.find("Unknown") == std::string::npos) { + UniValue winner(UniValue::VOBJ); + std::size_t pos1 = strPayment.find(":"); + std::size_t pos2 = strPayment.rfind(":"); + std::string strAddress = strPayment.substr(0, pos1); + ltrim(strAddress); + uint64_t level = atoi(strPayment.substr(pos1 + 1, pos2)); + uint64_t nVotes = atoi(strPayment.substr(pos2 + 1)); + winner.push_back(Pair("address", strAddress)); + winner.push_back(Pair("level", level)); + winner.push_back(Pair("nVotes", nVotes)); + obj.push_back(Pair("winner", winner)); + } else { + UniValue winner(UniValue::VOBJ); + winner.push_back(Pair("address", strPayment)); + winner.push_back(Pair("nVotes", 0)); + obj.push_back(Pair("winner", winner)); + } + + ret.push_back(obj); + } + + return ret; +} + +UniValue getmasternodescores (const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getmasternodescores ( blocks )\n" + "\nPrint list of winning masternode by score\n" + + "\nArguments:\n" + "1. blocks (numeric, optional) Show the last n blocks (default 10)\n" + + "\nResult:\n" + "{\n" + " xxxx: \"xxxx\" (numeric : string) Block height : Masternode hash\n" + " ,...\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getmasternodescores", "") + HelpExampleRpc("getmasternodescores", "")); + + int nLast = 10; + + if (params.size() == 1) { + try { + nLast = std::stoi(params[0].get_str()); + } catch (const std::invalid_argument&) { + throw runtime_error("Exception on param 2"); + } + } + UniValue obj(UniValue::VOBJ); + + std::vector vMasternodes = mnodeman.GetFullMasternodeVector(); + for (int nHeight = chainActive.Tip()->nHeight - nLast; nHeight < chainActive.Tip()->nHeight + 20; nHeight++) { + uint256 nHigh = 0; + CMasternode* pBestMasternode = NULL; + for (CMasternode& mn : vMasternodes) { + uint256 n = mn.CalculateScore(1, nHeight - 100); + if (n > nHigh) { + nHigh = n; + pBestMasternode = &mn; + } + } + if (pBestMasternode) + obj.push_back(Pair(strprintf("%d", nHeight), pBestMasternode->vin.prevout.hash.ToString().c_str())); + } + + return obj; +} + +bool DecodeHexMnb(CMasternodeBroadcast& mnb, std::string strHexMnb) { + + if (!IsHex(strHexMnb)) + return false; + + vector mnbData(ParseHex(strHexMnb)); + CDataStream ssData(mnbData, SER_NETWORK, PROTOCOL_VERSION); + try { + ssData >> mnb; + } + catch (const std::exception&) { + return false; + } + + return true; +} +UniValue createmasternodebroadcast(const UniValue& params, bool fHelp) +{ + string strCommand; + if (params.size() >= 1) + strCommand = params[0].get_str(); + if (fHelp || (strCommand != "alias" && strCommand != "all") || (strCommand == "alias" && params.size() < 2)) + throw runtime_error( + "createmasternodebroadcast \"command\" ( \"alias\")\n" + "\nCreates a masternode broadcast message for one or all masternodes configured in masternode.conf\n" + + HelpRequiringPassphrase() + "\n" + + "\nArguments:\n" + "1. \"command\" (string, required) \"alias\" for single masternode, \"all\" for all masternodes\n" + "2. \"alias\" (string, required if command is \"alias\") Alias of the masternode\n" + + "\nResult (all):\n" + "{\n" + " \"overall\": \"xxx\", (string) Overall status message indicating number of successes.\n" + " \"detail\": [ (array) JSON array of broadcast objects.\n" + " {\n" + " \"alias\": \"xxx\", (string) Alias of the masternode.\n" + " \"success\": true|false, (boolean) Success status.\n" + " \"hex\": \"xxx\" (string, if success=true) Hex encoded broadcast message.\n" + " \"error_message\": \"xxx\" (string, if success=false) Error message, if any.\n" + " }\n" + " ,...\n" + " ]\n" + "}\n" + + "\nResult (alias):\n" + "{\n" + " \"alias\": \"xxx\", (string) Alias of the masternode.\n" + " \"success\": true|false, (boolean) Success status.\n" + " \"hex\": \"xxx\" (string, if success=true) Hex encoded broadcast message.\n" + " \"error_message\": \"xxx\" (string, if success=false) Error message, if any.\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("createmasternodebroadcast", "alias mymn1") + HelpExampleRpc("createmasternodebroadcast", "alias mymn1")); + + EnsureWalletIsUnlocked(); + + if (strCommand == "alias") + { + // wait for reindex and/or import to finish + if (fImporting || fReindex) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Wait for reindex and/or import to finish"); + + std::string alias = params[1].get_str(); + bool found = false; + + UniValue statusObj(UniValue::VOBJ); + statusObj.push_back(Pair("alias", alias)); + + for (CMasternodeConfig::CMasternodeEntry mne : masternodeConfig.getEntries()) { + if(mne.getAlias() == alias) { + found = true; + std::string errorMessage; + CMasternodeBroadcast mnb; + + bool success = activeMasternode.CreateBroadcast(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), errorMessage, mnb, true); + + statusObj.push_back(Pair("success", success)); + if(success) { + CDataStream ssMnb(SER_NETWORK, PROTOCOL_VERSION); + ssMnb << mnb; + statusObj.push_back(Pair("hex", HexStr(ssMnb.begin(), ssMnb.end()))); + } else { + statusObj.push_back(Pair("error_message", errorMessage)); + } + break; + } + } + + if(!found) { + statusObj.push_back(Pair("success", false)); + statusObj.push_back(Pair("error_message", "Could not find alias in config. Verify with list-conf.")); + } + + return statusObj; + + } + + if (strCommand == "all") + { + // wait for reindex and/or import to finish + if (fImporting || fReindex) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Wait for reindex and/or import to finish"); + + std::vector mnEntries; + mnEntries = masternodeConfig.getEntries(); + + int successful = 0; + int failed = 0; + + UniValue resultsObj(UniValue::VARR); + + for (CMasternodeConfig::CMasternodeEntry mne : masternodeConfig.getEntries()) { + std::string errorMessage; + + CTxIn vin = CTxIn(uint256S(mne.getTxHash()), uint32_t(atoi(mne.getOutputIndex().c_str()))); + CMasternodeBroadcast mnb; + + bool success = activeMasternode.CreateBroadcast(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), errorMessage, mnb, true); + + UniValue statusObj(UniValue::VOBJ); + statusObj.push_back(Pair("alias", mne.getAlias())); + statusObj.push_back(Pair("success", success)); + + if(success) { + successful++; + CDataStream ssMnb(SER_NETWORK, PROTOCOL_VERSION); + ssMnb << mnb; + statusObj.push_back(Pair("hex", HexStr(ssMnb.begin(), ssMnb.end()))); + } else { + failed++; + statusObj.push_back(Pair("error_message", errorMessage)); + } + + resultsObj.push_back(statusObj); + } + + UniValue returnObj(UniValue::VOBJ); + returnObj.push_back(Pair("overall", strprintf("Successfully created broadcast messages for %d masternodes, failed to create %d, total %d", successful, failed, successful + failed))); + returnObj.push_back(Pair("detail", resultsObj)); + + return returnObj; + } + return NullUniValue; +} + +UniValue decodemasternodebroadcast(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "decodemasternodebroadcast \"hexstring\"\n" + "\nCommand to decode masternode broadcast messages\n" + + "\nArgument:\n" + "1. \"hexstring\" (string) The hex encoded masternode broadcast message\n" + + "\nResult:\n" + "{\n" + " \"vin\": \"xxxx\" (string) The unspent output which is holding the masternode collateral\n" + " \"addr\": \"xxxx\" (string) IP address of the masternode\n" + " \"pubkeycollateral\": \"xxxx\" (string) Collateral address's public key\n" + " \"pubkeymasternode\": \"xxxx\" (string) Masternode's public key\n" + " \"vchsig\": \"xxxx\" (string) Base64-encoded signature of this message (verifiable via pubkeycollateral)\n" + " \"sigtime\": \"nnn\" (numeric) Signature timestamp\n" + " \"protocolversion\": \"nnn\" (numeric) Masternode's protocol version\n" + " \"nlastdsq\": \"nnn\" (numeric) The last time the masternode sent a DSQ message (for mixing) (DEPRECATED)\n" + " \"lastping\" : { (object) JSON object with information about the masternode's last ping\n" + " \"vin\": \"xxxx\" (string) The unspent output of the masternode which is signing the message\n" + " \"blockhash\": \"xxxx\" (string) Current chaintip blockhash minus 12\n" + " \"sigtime\": \"nnn\" (numeric) Signature time for this ping\n" + " \"vchsig\": \"xxxx\" (string) Base64-encoded signature of this ping (verifiable via pubkeymasternode)\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("decodemasternodebroadcast", "hexstring") + HelpExampleRpc("decodemasternodebroadcast", "hexstring")); + + CMasternodeBroadcast mnb; + + if (!DecodeHexMnb(mnb, params[0].get_str())) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Masternode broadcast message decode failed"); + + if(!mnb.VerifySignature()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Masternode broadcast signature verification failed"); + + UniValue resultObj(UniValue::VOBJ); + + resultObj.push_back(Pair("vin", mnb.vin.prevout.ToString())); + resultObj.push_back(Pair("addr", mnb.addr.ToString())); + resultObj.push_back(Pair("pubkeycollateral", CBitcoinAddress(mnb.pubKeyCollateralAddress.GetID()).ToString())); + resultObj.push_back(Pair("pubkeymasternode", CBitcoinAddress(mnb.pubKeyMasternode.GetID()).ToString())); + resultObj.push_back(Pair("vchsig", EncodeBase64(&mnb.sig[0], mnb.sig.size()))); + resultObj.push_back(Pair("sigtime", mnb.sigTime)); + resultObj.push_back(Pair("protocolversion", mnb.protocolVersion)); + resultObj.push_back(Pair("nlastdsq", mnb.nLastDsq)); + + UniValue lastPingObj(UniValue::VOBJ); + lastPingObj.push_back(Pair("vin", mnb.lastPing.vin.prevout.ToString())); + lastPingObj.push_back(Pair("blockhash", mnb.lastPing.blockHash.ToString())); + lastPingObj.push_back(Pair("sigtime", mnb.lastPing.sigTime)); + lastPingObj.push_back(Pair("vchsig", EncodeBase64(&mnb.lastPing.vchSig[0], mnb.lastPing.vchSig.size()))); + + resultObj.push_back(Pair("lastping", lastPingObj)); + + return resultObj; +} + +UniValue relaymasternodebroadcast(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "relaymasternodebroadcast \"hexstring\"\n" + "\nCommand to relay masternode broadcast messages\n" + + "\nArguments:\n" + "1. \"hexstring\" (string) The hex encoded masternode broadcast message\n" + + "\nExamples:\n" + + HelpExampleCli("relaymasternodebroadcast", "hexstring") + HelpExampleRpc("relaymasternodebroadcast", "hexstring")); + + + CMasternodeBroadcast mnb; + + if (!DecodeHexMnb(mnb, params[0].get_str())) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Masternode broadcast message decode failed"); + + if(!mnb.VerifySignature()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Masternode broadcast signature verification failed"); + + mnodeman.UpdateMasternodeList(mnb); + mnb.Relay(); + + return strprintf("Masternode broadcast sent (service %s, vin %s)", mnb.addr.ToString(), mnb.vin.ToString()); +} diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp new file mode 100644 index 0000000..44b5197 --- /dev/null +++ b/src/rpc/mining.cpp @@ -0,0 +1,738 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Copyright (c) 2014-2015 The Dash developers +// Copyright (c) 2015-2017 The PIVX developers +// Copyright (c) 2017-2019 The XDNA developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "amount.h" +#include "base58.h" +#include "chainparams.h" +#include "core_io.h" +#include "init.h" +#include "main.h" +#include "miner.h" +#include "net.h" +#include "pow.h" +#include "rpc/server.h" +#include "util.h" +#ifdef ENABLE_WALLET +#include "wallet/db.h" +#include "wallet/wallet.h" +#endif + +#include + +#include + +#include + +using namespace std; + +#ifdef ENABLE_WALLET +// Key used by getwork miners. +// Allocated in InitRPCMining, free'd in ShutdownRPCMining +static CReserveKey* pMiningKey = NULL; + +void InitRPCMining() +{ + if (!pwalletMain) + return; + + // getwork/getblocktemplate mining rewards paid here: + pMiningKey = new CReserveKey(pwalletMain); +} + +void ShutdownRPCMining() +{ + if (!pMiningKey) + return; + + delete pMiningKey; pMiningKey = NULL; +} +#else +void InitRPCMining() +{ +} +void ShutdownRPCMining() +{ +} +#endif + +/** + * Return average network hashes per second based on the last 'lookup' blocks, + * or from the last difficulty change if 'lookup' is nonpositive. + * If 'height' is nonnegative, compute the estimate at the time when a given block was found. + */ +UniValue GetNetworkHashPS(int lookup, int height) +{ + CBlockIndex *pb = chainActive.Tip(); + + if (height >= 0 && height < chainActive.Height()) + pb = chainActive[height]; + + if (pb == NULL || !pb->nHeight) + return 0; + + // If lookup is -1, then use blocks since last difficulty change. + if (lookup <= 0) + lookup = pb->nHeight % 2016 + 1; + + // If lookup is larger than chain, then set it to chain length. + if (lookup > pb->nHeight) + lookup = pb->nHeight; + + CBlockIndex* pb0 = pb; + int64_t minTime = pb0->GetBlockTime(); + int64_t maxTime = minTime; + for (int i = 0; i < lookup; i++) { + pb0 = pb0->pprev; + int64_t time = pb0->GetBlockTime(); + minTime = std::min(time, minTime); + maxTime = std::max(time, maxTime); + } + + // In case there's a situation where minTime == maxTime, we don't want a divide by zero exception. + if (minTime == maxTime) + return 0; + + uint256 workDiff = pb->nChainWork - pb0->nChainWork; + int64_t timeDiff = maxTime - minTime; + + return (int64_t)(workDiff.getdouble() / timeDiff); +} + +UniValue getnetworkhashps(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "getnetworkhashps ( blocks height )\n" + "\nReturns the estimated network hashes per second based on the last n blocks.\n" + "Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\n" + "Pass in [height] to estimate the network speed at the time when a certain block was found.\n" + "\nArguments:\n" + "1. blocks (numeric, optional, default=120) The number of blocks, or -1 for blocks since last difficulty change.\n" + "2. height (numeric, optional, default=-1) To estimate at the time of the given height.\n" + "\nResult:\n" + "x (numeric) Hashes per second estimated\n" + "\nExamples:\n" + + HelpExampleCli("getnetworkhashps", "") + HelpExampleRpc("getnetworkhashps", "")); + + return GetNetworkHashPS(params.size() > 0 ? params[0].get_int() : 120, params.size() > 1 ? params[1].get_int() : -1); +} + +#ifdef ENABLE_WALLET +UniValue getgenerate(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getgenerate\n" + "\nReturn if the server is set to generate coins or not. The default is false.\n" + "It is set with the command line argument -gen (or xdna.conf setting gen)\n" + "It can also be set with the setgenerate call.\n" + "\nResult\n" + "true|false (boolean) If the server is set to generate coins or not\n" + "\nExamples:\n" + + HelpExampleCli("getgenerate", "") + HelpExampleRpc("getgenerate", "")); + + return GetBoolArg("-gen", false); +} + + +UniValue setgenerate(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "setgenerate generate ( genproclimit )\n" + "\nSet 'generate' true or false to turn generation on or off.\n" + "Generation is limited to 'genproclimit' processors, -1 is unlimited.\n" + "See the getgenerate call for the current setting.\n" + "\nArguments:\n" + "1. generate (boolean, required) Set to true to turn on generation, false to turn off.\n" + "2. genproclimit (numeric, optional) Set the processor limit for when generation is on. Can be -1 for unlimited.\n" + " Note: in -regtest mode, genproclimit controls how many blocks are generated immediately.\n" + "\nResult\n" + "[ blockhashes ] (array, -regtest only) hashes of blocks generated\n" + "\nExamples:\n" + "\nSet the generation on with a limit of one processor\n" + + HelpExampleCli("setgenerate", "true 1") + + "\nCheck the setting\n" + HelpExampleCli("getgenerate", "") + + "\nTurn off generation\n" + HelpExampleCli("setgenerate", "false") + + "\nUsing json rpc\n" + HelpExampleRpc("setgenerate", "true, 1")); + + if (pwalletMain == NULL) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)"); + + bool fGenerate = true; + if (params.size() > 0) + fGenerate = params[0].get_bool(); + + int nGenProcLimit = -1; + if (params.size() > 1) { + nGenProcLimit = params[1].get_int(); + if (nGenProcLimit == 0) + fGenerate = false; + } + + // -regtest mode: don't return until nGenProcLimit blocks are generated + if (fGenerate && Params().MineBlocksOnDemand()) { + int nHeightStart = 0; + int nHeightEnd = 0; + int nHeight = 0; + int nGenerate = (nGenProcLimit > 0 ? nGenProcLimit : 1); + CReserveKey reservekey(pwalletMain); + + { // Don't keep cs_main locked + LOCK(cs_main); + nHeightStart = chainActive.Height(); + nHeight = nHeightStart; + nHeightEnd = nHeightStart + nGenerate; + } + unsigned int nExtraNonce = 0; + UniValue blockHashes(UniValue::VARR); + while (nHeight < nHeightEnd) { + unique_ptr pblocktemplate(CreateNewBlockWithKey(reservekey, pwalletMain, false)); + if (!pblocktemplate.get()) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet keypool empty"); + CBlock* pblock = &pblocktemplate->block; + { + LOCK(cs_main); + IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce); + } + while (!CheckProofOfWork(pblock->GetHash(), pblock->nBits)) { + // Yes, there is a chance every nonce could fail to satisfy the -regtest + // target -- 1 in 2^(2^32). That ain't gonna happen. + ++pblock->nNonce; + LogPrintf("new nonce %d",pblock->nNonce); + } + CValidationState state; + if (!ProcessNewBlock(state, NULL, pblock)) + throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted"); + ++nHeight; + blockHashes.push_back(pblock->GetHash().GetHex()); + } + return blockHashes; + } else // Not -regtest: start generate thread, return immediately + { + mapArgs["-gen"] = (fGenerate ? "1" : "0"); + mapArgs["-genproclimit"] = itostr(nGenProcLimit); + GenerateBitcoins(fGenerate, pwalletMain, nGenProcLimit); + } + + return NullUniValue; +} + +UniValue gethashespersec(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "gethashespersec\n" + "\nReturns a recent hashes per second performance measurement while generating.\n" + "See the getgenerate and setgenerate calls to turn generation on and off.\n" + "\nResult:\n" + "n (numeric) The recent hashes per second when generation is on (will return 0 if generation is off)\n" + "\nExamples:\n" + + HelpExampleCli("gethashespersec", "") + HelpExampleRpc("gethashespersec", "")); + + if (GetTimeMillis() - nHPSTimerStart > 8000) + return (int64_t)0; + return (int64_t)dHashesPerSec; +} +#endif + + +UniValue getmininginfo(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getmininginfo\n" + "\nReturns a json object containing mining-related information." + "\nResult:\n" + "{\n" + " \"blocks\": nnn, (numeric) The current block\n" + " \"currentblocksize\": nnn, (numeric) The last block size\n" + " \"currentblocktx\": nnn, (numeric) The last block transaction\n" + " \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n" + " \"errors\": \"...\" (string) Current errors\n" + " \"generate\": true|false (boolean) If the generation is on or off (see getgenerate or setgenerate calls)\n" + " \"genproclimit\": n (numeric) The processor limit for generation. -1 if no generation. (see getgenerate or setgenerate calls)\n" + " \"hashespersec\": n (numeric) The hashes per second of the generation, or 0 if no generation.\n" + " \"pooledtx\": n (numeric) The size of the mem pool\n" + " \"testnet\": true|false (boolean) If using testnet or not\n" + " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getmininginfo", "") + HelpExampleRpc("getmininginfo", "")); + + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("blocks", (int)chainActive.Height())); + obj.push_back(Pair("currentblocksize", (uint64_t)nLastBlockSize)); + obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx)); + obj.push_back(Pair("difficulty", (double)GetDifficulty())); + obj.push_back(Pair("errors", GetWarnings("statusbar"))); + obj.push_back(Pair("genproclimit", (int)GetArg("-genproclimit", -1))); + obj.push_back(Pair("networkhashps", getnetworkhashps(params, false))); + obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); + obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC())); + obj.push_back(Pair("chain", Params().NetworkIDString())); +#ifdef ENABLE_WALLET + obj.push_back(Pair("generate", getgenerate(params, false))); + obj.push_back(Pair("hashespersec", gethashespersec(params, false))); +#endif + return obj; +} + + +// NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts +UniValue prioritisetransaction(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 3) + throw runtime_error( + "prioritisetransaction \n" + "Accepts the transaction into mined blocks at a higher (or lower) priority\n" + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction id.\n" + "2. priority delta (numeric, required) The priority to add or subtract.\n" + " The transaction selection algorithm considers the tx as it would have a higher priority.\n" + " (priority of a transaction is calculated: coinage * value_in_duffs / txsize) \n" + "3. fee delta (numeric, required) The fee value (in duffs) to add (or subtract, if negative).\n" + " The fee is not actually paid, only the algorithm for selecting transactions into a block\n" + " considers the transaction as it would have paid a higher (or lower) fee.\n" + "\nResult\n" + "true (boolean) Returns true\n" + "\nExamples:\n" + + HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 10000") + HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 10000")); + + uint256 hash = ParseHashStr(params[0].get_str(), "txid"); + + CAmount nAmount = params[2].get_int64(); + + mempool.PrioritiseTransaction(hash, params[0].get_str(), params[1].get_real(), nAmount); + return true; +} + + +// NOTE: Assumes a conclusive result; if result is inconclusive, it must be handled by caller +static UniValue BIP22ValidationResult(const CValidationState& state) +{ + if (state.IsValid()) + return NullUniValue; + + std::string strRejectReason = state.GetRejectReason(); + if (state.IsError()) + throw JSONRPCError(RPC_VERIFY_ERROR, strRejectReason); + if (state.IsInvalid()) { + if (strRejectReason.empty()) + return "rejected"; + return strRejectReason; + } + // Should be impossible + return "valid?"; +} + +UniValue getblocktemplate(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getblocktemplate ( \"jsonrequestobject\" )\n" + "\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n" + "It returns data needed to construct a block to work on.\n" + "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n" + + "\nArguments:\n" + "1. \"jsonrequestobject\" (string, optional) A json object in the following spec\n" + " {\n" + " \"mode\":\"template\" (string, optional) This must be set to \"template\" or omitted\n" + " \"capabilities\":[ (array, optional) A list of strings\n" + " \"support\" (string) client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'\n" + " ,...\n" + " ]\n" + " }\n" + "\n" + + "\nResult:\n" + "{\n" + " \"version\" : n, (numeric) The block version\n" + " \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n" + " \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n" + " {\n" + " \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n" + " \"hash\" : \"xxxx\", (string) hash/id encoded in little-endian hexadecimal\n" + " \"depends\" : [ (array) array of numbers \n" + " n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n" + " ,...\n" + " ],\n" + " \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in duffs); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n" + " \"sigops\" : n, (numeric) total number of SigOps, as counted for purposes of block limits; if key is not present, sigop count is unknown and clients MUST NOT assume there aren't any\n" + " \"required\" : true|false (boolean) if provided and true, this transaction must be in the final block\n" + " }\n" + " ,...\n" + " ],\n" + " \"coinbaseaux\" : { (json object) data that should be included in the coinbase's scriptSig content\n" + " \"flags\" : \"flags\" (string) \n" + " },\n" + " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in duffs)\n" + " \"coinbasetxn\" : { ... }, (json object) information for coinbase transaction\n" + " \"target\" : \"xxxx\", (string) The hash target\n" + " \"mintime\" : xxx, (numeric) The minimum timestamp appropriate for next block time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"mutable\" : [ (array of string) list of ways the block template may be changed \n" + " \"value\" (string) A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'\n" + " ,...\n" + " ],\n" + " \"noncerange\" : \"00000000ffffffff\", (string) A range of valid nonces\n" + " \"sigoplimit\" : n, (numeric) limit of sigops in blocks\n" + " \"sizelimit\" : n, (numeric) limit of block size\n" + " \"curtime\" : ttt, (numeric) current timestamp in seconds since epoch (Jan 1 1970 GMT)\n" + " \"bits\" : \"xxx\", (string) compressed target of next block\n" + " \"height\" : n (numeric) The height of the next block\n" + " \"payee\" : \"xxx\", (string) required payee for the next block\n" + " \"payee_amount\" : n, (numeric) required amount to pay\n" + " \"masternode_payments\" : true|false, (boolean) true, if masternode payments are enabled\n" + " \"enforce_masternode_payments\" : true|false (boolean) true, if masternode payments are enforced\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("getblocktemplate", "") + HelpExampleRpc("getblocktemplate", "")); + + std::string strMode = "template"; + UniValue lpval = NullUniValue; + if (params.size() > 0) { + const UniValue& oparam = params[0].get_obj(); + const UniValue& modeval = find_value(oparam, "mode"); + if (modeval.isStr()) + strMode = modeval.get_str(); + else if (modeval.isNull()) { + /* Do nothing */ + } else + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); + lpval = find_value(oparam, "longpollid"); + + if (strMode == "proposal") { + const UniValue& dataval = find_value(oparam, "data"); + if (!dataval.isStr()) + throw JSONRPCError(RPC_TYPE_ERROR, "Missing data String key for proposal"); + + CBlock block; + if (!DecodeHexBlk(block, dataval.get_str())) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); + + uint256 hash = block.GetHash(); + BlockMap::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) { + CBlockIndex* pindex = mi->second; + if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) + return "duplicate"; + if (pindex->nStatus & BLOCK_FAILED_MASK) + return "duplicate-invalid"; + return "duplicate-inconclusive"; + } + + CBlockIndex* const pindexPrev = chainActive.Tip(); + // TestBlockValidity only supports blocks built on the current Tip + if (block.hashPrevBlock != pindexPrev->GetBlockHash()) + return "inconclusive-not-best-prevblk"; + CValidationState state; + TestBlockValidity(state, block, pindexPrev, false, true); + return BIP22ValidationResult(state); + } + } + + if (strMode != "template") + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); + + if (vNodes.empty()) + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "XDNA is not connected!"); + + if (IsInitialBlockDownload()) + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "XDNA is downloading blocks..."); + + static unsigned int nTransactionsUpdatedLast; + + if (!lpval.isNull()) { + // Wait to respond until either the best block changes, OR a minute has passed and there are more transactions + uint256 hashWatchedChain; + std::chrono::steady_clock::time_point checktxtime; + unsigned int nTransactionsUpdatedLastLP; + + if (lpval.isStr()) { + // Format: + std::string lpstr = lpval.get_str(); + + hashWatchedChain.SetHex(lpstr.substr(0, 64)); + nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64)); + } else { + // NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier + hashWatchedChain = chainActive.Tip()->GetBlockHash(); + nTransactionsUpdatedLastLP = nTransactionsUpdatedLast; + } + + // Release the wallet and main lock while waiting + LEAVE_CRITICAL_SECTION(cs_main); + { + checktxtime = std::chrono::steady_clock::now() + std::chrono::minutes(1); + + WaitableLock lock(csBestBlock); + while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning()) { + if (cvBlockChange.wait_until(lock, checktxtime) == std::cv_status::timeout) + { + // Timeout: Check transactions for update + if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP) + break; + checktxtime += std::chrono::seconds(10); + } + } + } + ENTER_CRITICAL_SECTION(cs_main); + + if (!IsRPCRunning()) + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down"); + // TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners? + } + + // Update block + static CBlockIndex* pindexPrev; + static int64_t nStart; + static CBlockTemplate* pblocktemplate; + if (pindexPrev != chainActive.Tip() || + (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5)) { + // Clear pindexPrev so future calls make a new block, despite any failures from here on + pindexPrev = NULL; + + // Store the chainActive.Tip() used before CreateNewBlock, to avoid races + nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); + CBlockIndex* pindexPrevNew = chainActive.Tip(); + nStart = GetTime(); + + // Create new block + if (pblocktemplate) { + delete pblocktemplate; + pblocktemplate = NULL; + } + CScript scriptDummy = CScript() << OP_TRUE; + pblocktemplate = CreateNewBlock(scriptDummy, pwalletMain, false); + if (!pblocktemplate) + throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); + + // Need to update only after we know CreateNewBlock succeeded + pindexPrev = pindexPrevNew; + } + CBlock* pblock = &pblocktemplate->block; // pointer for convenience + + // Update nTime + UpdateTime(pblock, pindexPrev); + pblock->nNonce = 0; + + UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal"); + + UniValue transactions(UniValue::VARR); + map setTxIndex; + int i = 0; + BOOST_FOREACH (CTransaction& tx, pblock->vtx) { + uint256 txHash = tx.GetHash(); + setTxIndex[txHash] = i++; + + if (tx.IsCoinBase()) + continue; + + UniValue entry(UniValue::VOBJ); + + entry.push_back(Pair("data", EncodeHexTx(tx))); + + entry.push_back(Pair("hash", txHash.GetHex())); + + UniValue deps(UniValue::VARR); + BOOST_FOREACH (const CTxIn& in, tx.vin) { + if (setTxIndex.count(in.prevout.hash)) + deps.push_back(setTxIndex[in.prevout.hash]); + } + entry.push_back(Pair("depends", deps)); + + int index_in_template = i - 1; + entry.push_back(Pair("fee", pblocktemplate->vTxFees[index_in_template])); + entry.push_back(Pair("sigops", pblocktemplate->vTxSigOps[index_in_template])); + + transactions.push_back(entry); + } + + UniValue aux(UniValue::VOBJ); + aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end()))); + + uint256 hashTarget = uint256().SetCompact(pblock->nBits); + + static UniValue aMutable(UniValue::VARR); + if (aMutable.empty()) { + aMutable.push_back("time"); + aMutable.push_back("transactions"); + aMutable.push_back("prevblock"); + } + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("capabilities", aCaps)); + result.push_back(Pair("version", pblock->nVersion)); + result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex())); + result.push_back(Pair("transactions", transactions)); + result.push_back(Pair("coinbaseaux", aux)); + result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].GetValueOut())); + result.push_back(Pair("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast))); + result.push_back(Pair("target", hashTarget.GetHex())); + result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast() + 1)); + result.push_back(Pair("mutable", aMutable)); + result.push_back(Pair("noncerange", "00000000ffffffff")); +// result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS)); +// result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE)); + result.push_back(Pair("curtime", pblock->GetBlockTime())); + result.push_back(Pair("bits", strprintf("%08x", pblock->nBits))); + result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight + 1))); + + + if (pblock->payee != CScript()) { + CTxDestination address1; + ExtractDestination(pblock->payee, address1); + CBitcoinAddress address2(address1); + result.push_back(Pair("payee", address2.ToString().c_str())); + result.push_back(Pair("payee_amount", (int64_t)pblock->vtx[0].vout[1].nValue)); + } else { + result.push_back(Pair("payee", "")); + result.push_back(Pair("payee_amount", "")); + } + + result.push_back(Pair("masternode_payments", pblock->nTime > Params().StartMasternodePayments())); + result.push_back(Pair("enforce_masternode_payments", true)); + + return result; +} + +class submitblock_StateCatcher : public CValidationInterface +{ +public: + uint256 hash; + bool found; + CValidationState state; + + submitblock_StateCatcher(const uint256& hashIn) : hash(hashIn), found(false), state(){}; + +protected: + virtual void BlockChecked(const CBlock& block, const CValidationState& stateIn) + { + if (block.GetHash() != hash) + return; + found = true; + state = stateIn; + }; +}; + +UniValue submitblock(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "submitblock \"hexdata\" ( \"jsonparametersobject\" )\n" + "\nAttempts to submit new block to network.\n" + "The 'jsonparametersobject' parameter is currently ignored.\n" + "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n" + + "\nArguments\n" + "1. \"hexdata\" (string, required) the hex-encoded block data to submit\n" + "2. \"jsonparametersobject\" (string, optional) object of optional parameters\n" + " {\n" + " \"workid\" : \"id\" (string, optional) if the server provided a workid, it MUST be included with submissions\n" + " }\n" + "\nResult:\n" + "\nExamples:\n" + + HelpExampleCli("submitblock", "\"mydata\"") + HelpExampleRpc("submitblock", "\"mydata\"")); + + CBlock block; + if (!DecodeHexBlk(block, params[0].get_str())) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); + + uint256 hash = block.GetHash(); + bool fBlockPresent = false; + { + LOCK(cs_main); + BlockMap::iterator mi = mapBlockIndex.find(hash); + if (mi != mapBlockIndex.end()) { + CBlockIndex* pindex = mi->second; + if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) + return "duplicate"; + if (pindex->nStatus & BLOCK_FAILED_MASK) + return "duplicate-invalid"; + // Otherwise, we might only have the header - process the block before returning + fBlockPresent = true; + } + } + + CValidationState state; + submitblock_StateCatcher sc(block.GetHash()); + RegisterValidationInterface(&sc); + bool fAccepted = ProcessNewBlock(state, NULL, &block); + UnregisterValidationInterface(&sc); + if (fBlockPresent) { + if (fAccepted && !sc.found) + return "duplicate-inconclusive"; + return "duplicate"; + } + if (fAccepted) { + if (!sc.found) + return "inconclusive"; + state = sc.state; + } + return BIP22ValidationResult(state); +} + +UniValue estimatefee(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "estimatefee nblocks\n" + "\nEstimates the approximate fee per kilobyte\n" + "needed for a transaction to begin confirmation\n" + "within nblocks blocks.\n" + "\nArguments:\n" + "1. nblocks (numeric)\n" + "\nResult:\n" + "n : (numeric) estimated fee-per-kilobyte\n" + "\n" + "-1.0 is returned if not enough transactions and\n" + "blocks have been observed to make an estimate.\n" + "\nExample:\n" + + HelpExampleCli("estimatefee", "6")); + + RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); + + int nBlocks = params[0].get_int(); + if (nBlocks < 1) + nBlocks = 1; + + CFeeRate feeRate = mempool.estimateFee(nBlocks); + if (feeRate == CFeeRate(0)) + return -1.0; + + return ValueFromAmount(feeRate.GetFeePerK()); +} + +UniValue estimatepriority(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "estimatepriority nblocks\n" + "\nEstimates the approximate priority\n" + "a zero-fee transaction needs to begin confirmation\n" + "within nblocks blocks.\n" + "\nArguments:\n" + "1. nblocks (numeric)\n" + "\nResult:\n" + "n : (numeric) estimated priority\n" + "\n" + "-1.0 is returned if not enough transactions and\n" + "blocks have been observed to make an estimate.\n" + "\nExample:\n" + + HelpExampleCli("estimatepriority", "6")); + + RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); + + int nBlocks = params[0].get_int(); + if (nBlocks < 1) + nBlocks = 1; + + return mempool.estimatePriority(nBlocks); +} diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp new file mode 100644 index 0000000..874ee5e --- /dev/null +++ b/src/rpc/misc.cpp @@ -0,0 +1,519 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Copyright (c) 2014-2015 The Dash developers +// Copyright (c) 2015-2017 The PIVX developers +// Copyright (c) 2017-2018 The Bulwark developers +// Copyright (c) 2017-2019 The XDNA Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "base58.h" +#include "clientversion.h" +#include "init.h" +#include "main.h" +#include "masternode-sync.h" +#include "net.h" +#include "netbase.h" +#include "rpc/server.h" +#include "spork.h" +#include "timedata.h" +#include "util.h" +#ifdef ENABLE_WALLET +#include "wallet/wallet.h" +#include "wallet/walletdb.h" +#endif + +#include + +#include + +#include + +using namespace boost; +using namespace boost::assign; +using namespace std; + +/** + * @note Do not add or change anything in the information returned by this + * method. `getinfo` exists for backwards-compatibility only. It combines + * information from wildly different sources in the program, which is a mess, + * and is thus planned to be deprecated eventually. + * + * Based on the source of the information, new information should be added to: + * - `getblockchaininfo`, + * - `getnetworkinfo` or + * - `getwalletinfo` + * + * Or alternatively, create a specific query method for the information. + **/ +UniValue getinfo(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getinfo\n" + "Returns an object containing various state info.\n" + "\nResult:\n" + "{\n" + " \"version\": xxxxx, (numeric) the server version\n" + " \"protocolversion\": xxxxx, (numeric) the protocol version\n" + " \"walletversion\": xxxxx, (numeric) the wallet version\n" + " \"balance\": xxxxxxx, (numeric) the total xdna balance of the wallet\n" + " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" + " \"timeoffset\": xxxxx, (numeric) the time offset\n" + " \"connections\": xxxxx, (numeric) the number of connections\n" + " \"proxy\": \"host:port\", (string, optional) the proxy used by the server\n" + " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" + " \"testnet\": true|false, (boolean) if the server is using testnet or not\n" + " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n" + " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" + " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" + " \"paytxfee\": x.xxxx, (numeric) the transaction fee set in xdna/kb\n" + " \"relayfee\": x.xxxx, (numeric) minimum relay fee for non-free transactions in xdna/kb\n" + " \"staking status\": true|false, (boolean) if the wallet is staking or not\n" + " \"errors\": \"...\" (string) any error messages\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getinfo", "") + HelpExampleRpc("getinfo", "")); + + proxyType proxy; + GetProxy(NET_IPV4, proxy); + + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("version", CLIENT_VERSION)); + obj.push_back(Pair("protocolversion", PROTOCOL_VERSION)); +#ifdef ENABLE_WALLET + if (pwalletMain) { + obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); + obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); + if (!fLiteMode) + obj.push_back(Pair("obfuscation_balance", ValueFromAmount(pwalletMain->GetAnonymizedBalance()))); + } +#endif + obj.push_back(Pair("blocks", (int)chainActive.Height())); + obj.push_back(Pair("timeoffset", GetTimeOffset())); + obj.push_back(Pair("connections", (int)vNodes.size())); + obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string()))); + obj.push_back(Pair("difficulty", (double)GetDifficulty())); + obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC())); +#ifdef ENABLE_WALLET + if (pwalletMain) { + obj.push_back(Pair("keypoololdest", pwalletMain->GetOldestKeyPoolTime())); + obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); + } + if (pwalletMain && pwalletMain->IsCrypted()) + obj.push_back(Pair("unlocked_until", nWalletUnlockTime)); + obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK()))); +#endif + obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()))); + bool nStaking = false; + if (mapHashedBlocks.count(chainActive.Tip()->nHeight)) + nStaking = true; + else if (mapHashedBlocks.count(chainActive.Tip()->nHeight - 1) && nLastCoinStakeSearchInterval) + nStaking = true; + obj.push_back(Pair("staking status", (nStaking ? "Staking Active" : "Staking Not Active"))); + obj.push_back(Pair("errors", GetWarnings("statusbar"))); + return obj; +} + +UniValue mnsync(const UniValue& params, bool fHelp) +{ + std::string strMode; + if (params.size() == 1) + strMode = params[0].get_str(); + + if (fHelp || params.size() != 1 || (strMode != "status" && strMode != "reset")) { + throw runtime_error( + "mnsync \"status|reset\"\n" + "\nReturns the sync status or resets sync.\n" + + "\nArguments:\n" + "1. \"mode\" (string, required) either 'status' or 'reset'\n" + + "\nResult ('status' mode):\n" + "{\n" + " \"IsBlockchainSynced\": true|false, (boolean) 'true' if blockchain is synced\n" + " \"lastMasternodeList\": xxxx, (numeric) Timestamp of last MN list message\n" + " \"lastMasternodeWinner\": xxxx, (numeric) Timestamp of last MN winner message\n" + " \"lastFailure\": xxxx, (numeric) Timestamp of last failed sync\n" + " \"nCountFailures\": n, (numeric) Number of failed syncs (total)\n" + " \"sumMasternodeList\": n, (numeric) Number of MN list messages (total)\n" + " \"sumMasternodeWinner\": n, (numeric) Number of MN winner messages (total)\n" + " \"countMasternodeList\": n, (numeric) Number of MN list messages (local)\n" + " \"countMasternodeWinner\": n, (numeric) Number of MN winner messages (local)\n" + " \"RequestedMasternodeAssets\": n, (numeric) Status code of last sync phase\n" + " \"RequestedMasternodeAttempt\": n, (numeric) Status code of last sync attempt\n" + "}\n" + + "\nResult ('reset' mode):\n" + "\"status\" (string) 'success'\n" + "\nExamples:\n" + + HelpExampleCli("mnsync", "\"status\"") + HelpExampleRpc("mnsync", "\"status\"")); + } + + if (strMode == "status") { + UniValue obj(UniValue::VOBJ); + + obj.push_back(Pair("IsBlockchainSynced", masternodeSync.IsBlockchainSynced())); + obj.push_back(Pair("lastMasternodeList", masternodeSync.lastMasternodeList)); + obj.push_back(Pair("lastMasternodeWinner", masternodeSync.lastMasternodeWinner)); + obj.push_back(Pair("lastFailure", masternodeSync.lastFailure)); + obj.push_back(Pair("nCountFailures", masternodeSync.nCountFailures)); + obj.push_back(Pair("sumMasternodeList", masternodeSync.sumMasternodeList)); + obj.push_back(Pair("sumMasternodeWinner", masternodeSync.sumMasternodeWinner)); + obj.push_back(Pair("countMasternodeList", masternodeSync.countMasternodeList)); + obj.push_back(Pair("countMasternodeWinner", masternodeSync.countMasternodeWinner)); + obj.push_back(Pair("RequestedMasternodeAssets", masternodeSync.RequestedMasternodeAssets)); + obj.push_back(Pair("RequestedMasternodeAttempt", masternodeSync.RequestedMasternodeAttempt)); + + return obj; + } + + if (strMode == "reset") { + masternodeSync.Reset(); + return "success"; + } + return "failure"; +} + +#ifdef ENABLE_WALLET +class DescribeAddressVisitor : public boost::static_visitor +{ +private: + isminetype mine; + +public: + DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {} + + UniValue operator()(const CNoDestination &dest) const { return UniValue(UniValue::VOBJ); } + + UniValue operator()(const CKeyID &keyID) const { + UniValue obj(UniValue::VOBJ); + CPubKey vchPubKey; + obj.push_back(Pair("isscript", false)); + if (mine == ISMINE_SPENDABLE) { + pwalletMain->GetPubKey(keyID, vchPubKey); + obj.push_back(Pair("pubkey", HexStr(vchPubKey))); + obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed())); + } + return obj; + } + + UniValue operator()(const CScriptID &scriptID) const { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("isscript", true)); + if (mine != ISMINE_NO) { + CScript subscript; + pwalletMain->GetCScript(scriptID, subscript); + std::vector addresses; + txnouttype whichType; + int nRequired; + ExtractDestinations(subscript, whichType, addresses, nRequired); + obj.push_back(Pair("script", GetTxnOutputType(whichType))); + obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); + UniValue a(UniValue::VARR); + BOOST_FOREACH (const CTxDestination& addr, addresses) + a.push_back(CBitcoinAddress(addr).ToString()); + obj.push_back(Pair("addresses", a)); + if (whichType == TX_MULTISIG) + obj.push_back(Pair("sigsrequired", nRequired)); + } + return obj; + } +}; +#endif + +/* + Used for updating/reading spork settings on the network +*/ +UniValue spork(const UniValue& params, bool fHelp) +{ + if (params.size() == 1 && params[0].get_str() == "show") { + UniValue ret(UniValue::VOBJ); + for (int nSporkID = SPORK_START; nSporkID <= SPORK_END; nSporkID++) { + if (sporkManager.GetSporkNameByID(nSporkID) != "Unknown") + ret.push_back(Pair(sporkManager.GetSporkNameByID(nSporkID), GetSporkValue(nSporkID))); + } + return ret; + } else if (params.size() == 1 && params[0].get_str() == "active") { + UniValue ret(UniValue::VOBJ); + for (int nSporkID = SPORK_START; nSporkID <= SPORK_END; nSporkID++) { + if (sporkManager.GetSporkNameByID(nSporkID) != "Unknown") + ret.push_back(Pair(sporkManager.GetSporkNameByID(nSporkID), IsSporkActive(nSporkID))); + } + return ret; + } else if (params.size() == 2) { + int nSporkID = sporkManager.GetSporkIDByName(params[0].get_str()); + if (nSporkID == -1) { + return "Invalid spork name"; + } + + // SPORK VALUE + int64_t nValue = params[1].get_int(); + + //broadcast new spork + if (sporkManager.UpdateSpork(nSporkID, nValue)) { + return "success"; + } else { + return "failure"; + } + } + + throw runtime_error( + "spork []\n" + " is the corresponding spork name, or 'show' to show all current spork settings, active to show which sporks are active" + " is a epoch datetime to enable or disable spork" + + HelpRequiringPassphrase()); +} + +UniValue validateaddress(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "validateaddress \"xdnaaddress\"\n" + "\nReturn information about the given xdna address.\n" + "\nArguments:\n" + "1. \"xdnaaddress\" (string, required) The xdna address to validate\n" + "\nResult:\n" + "{\n" + " \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n" + " \"address\" : \"xdnaaddress\", (string) The xdna address validated\n" + " \"ismine\" : true|false, (boolean) If the address is yours or not\n" + " \"isscript\" : true|false, (boolean) If the key is a script\n" + " \"pubkey\" : \"publickeyhex\", (string) The hex value of the raw public key\n" + " \"iscompressed\" : true|false, (boolean) If the address is compressed\n" + " \"account\" : \"account\" (string) The account associated with the address, \"\" is the default account\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") + HelpExampleRpc("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")); + + CBitcoinAddress address(params[0].get_str()); + bool isValid = address.IsValid(); + + UniValue ret(UniValue::VOBJ); + ret.push_back(Pair("isvalid", isValid)); + if (isValid) { + CTxDestination dest = address.Get(); + string currentAddress = address.ToString(); + ret.push_back(Pair("address", currentAddress)); +#ifdef ENABLE_WALLET + isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : ISMINE_NO; + ret.push_back(Pair("ismine", (mine & ISMINE_SPENDABLE) ? true : false)); + if (mine != ISMINE_NO) { + ret.push_back(Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true : false)); + UniValue detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest); + ret.pushKVs(detail); + } + if (pwalletMain && pwalletMain->mapAddressBook.count(dest)) + ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest].name)); +#endif + } + return ret; +} + +/** + * Used by addmultisigaddress / createmultisig: + */ +CScript _createmultisig_redeemScript(const UniValue& params) +{ + int nRequired = params[0].get_int(); + const UniValue& keys = params[1].get_array(); + + // Gather public keys + if (nRequired < 1) + throw runtime_error("a multisignature address must require at least one key to redeem"); + if ((int)keys.size() < nRequired) + throw runtime_error( + strprintf("not enough keys supplied " + "(got %u keys, but need at least %d to redeem)", + keys.size(), nRequired)); + if (keys.size() > 16) + throw runtime_error("Number of addresses involved in the multisignature address creation > 16\nReduce the number"); + std::vector pubkeys; + pubkeys.resize(keys.size()); + for (unsigned int i = 0; i < keys.size(); i++) { + const std::string& ks = keys[i].get_str(); +#ifdef ENABLE_WALLET + // Case 1: XDNA address and we have full public key: + CBitcoinAddress address(ks); + if (pwalletMain && address.IsValid()) { + CKeyID keyID; + if (!address.GetKeyID(keyID)) + throw runtime_error( + strprintf("%s does not refer to a key", ks)); + CPubKey vchPubKey; + if (!pwalletMain->GetPubKey(keyID, vchPubKey)) + throw runtime_error( + strprintf("no full public key for address %s", ks)); + if (!vchPubKey.IsFullyValid()) + throw runtime_error(" Invalid public key: " + ks); + pubkeys[i] = vchPubKey; + } + + // Case 2: hex public key + else +#endif + if (IsHex(ks)) { + CPubKey vchPubKey(ParseHex(ks)); + if (!vchPubKey.IsFullyValid()) + throw runtime_error(" Invalid public key: " + ks); + pubkeys[i] = vchPubKey; + } else { + throw runtime_error(" Invalid public key: " + ks); + } + } + CScript result = GetScriptForMultisig(nRequired, pubkeys); + + if (result.size() > MAX_SCRIPT_ELEMENT_SIZE) + throw runtime_error( + strprintf("redeemScript exceeds size limit: %d > %d", result.size(), MAX_SCRIPT_ELEMENT_SIZE)); + + return result; +} + +UniValue createmultisig(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 2) { + string msg = "createmultisig nrequired [\"key\",...]\n" + "\nCreates a multi-signature address with n signature of m keys required.\n" + "It returns a json object with the address and redeemScript.\n" + + "\nArguments:\n" + "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n" + "2. \"keys\" (string, required) A json array of keys which are xdna addresses or hex-encoded public keys\n" + " [\n" + " \"key\" (string) xdna address or hex-encoded public key\n" + " ,...\n" + " ]\n" + + "\nResult:\n" + "{\n" + " \"address\":\"multisigaddress\", (string) The value of the new multisig address.\n" + " \"redeemScript\":\"script\" (string) The string value of the hex-encoded redemption script.\n" + "}\n" + + "\nExamples:\n" + "\nCreate a multisig address from 2 addresses\n" + + HelpExampleCli("createmultisig", "2 \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + + "\nAs a json rpc call\n" + HelpExampleRpc("createmultisig", "2, \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\""); + throw runtime_error(msg); + } + + // Construct using pay-to-script-hash: + CScript inner = _createmultisig_redeemScript(params); + CScriptID innerID(inner); + CBitcoinAddress address(innerID); + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("address", address.ToString())); + result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end()))); + + return result; +} + +UniValue verifymessage(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 3) + throw runtime_error( + "verifymessage \"xdnaaddress\" \"signature\" \"message\"\n" + "\nVerify a signed message\n" + "\nArguments:\n" + "1. \"xdnaaddress\" (string, required) The xdna address to use for the signature.\n" + "2. \"signature\" (string, required) The signature provided by the signer in base 64 encoding (see signmessage).\n" + "3. \"message\" (string, required) The message that was signed.\n" + "\nResult:\n" + "true|false (boolean) If the signature is verified or not.\n" + "\nExamples:\n" + "\nUnlock the wallet for 30 seconds\n" + + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + + "\nCreate the signature\n" + HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"my message\"") + + "\nVerify the signature\n" + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"signature\" \"my message\"") + + "\nAs json rpc\n" + HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", \"signature\", \"my message\"")); + + string strAddress = params[0].get_str(); + string strSign = params[1].get_str(); + string strMessage = params[2].get_str(); + + CBitcoinAddress addr(strAddress); + if (!addr.IsValid()) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); + + CKeyID keyID; + if (!addr.GetKeyID(keyID)) + throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); + + bool fInvalid = false; + vector vchSig = DecodeBase64(strSign.c_str(), &fInvalid); + + if (fInvalid) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding"); + + CHashWriter ss(SER_GETHASH, 0); + ss << strMessageMagic; + ss << strMessage; + + CPubKey pubkey; + if (!pubkey.RecoverCompact(ss.GetHash(), vchSig)) + return false; + + return (pubkey.GetID() == keyID); +} + +UniValue setmocktime(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "setmocktime timestamp\n" + "\nSet the local time to given timestamp (-regtest only)\n" + "\nArguments:\n" + "1. timestamp (integer, required) Unix seconds-since-epoch timestamp\n" + " Pass 0 to go back to using the system time."); + + if (!Params().MineBlocksOnDemand()) + throw runtime_error("setmocktime for regression testing (-regtest mode) only"); + + RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); + SetMockTime(params[0].get_int64()); + + return NullUniValue; +} + +#ifdef ENABLE_WALLET +UniValue getstakingstatus(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getstakingstatus\n" + "Returns an object containing various staking information.\n" + "\nResult:\n" + "{\n" + " \"validtime\": true|false, (boolean) if the chain tip is within staking phases\n" + " \"haveconnections\": true|false, (boolean) if network connections are present\n" + " \"walletunlocked\": true|false, (boolean) if the wallet is unlocked\n" + " \"mintablecoins\": true|false, (boolean) if the wallet has mintable coins\n" + " \"enoughcoins\": true|false, (boolean) if available coins are greater than reserve balance\n" + " \"mnsync\": true|false, (boolean) if masternode data is synced\n" + " \"staking status\": true|false, (boolean) if the wallet is staking or not\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getstakingstatus", "") + HelpExampleRpc("getstakingstatus", "")); + + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("validtime", chainActive.Tip()->nTime > 1471482000)); + obj.push_back(Pair("haveconnections", !vNodes.empty())); + if (pwalletMain) { + obj.push_back(Pair("walletunlocked", !pwalletMain->IsLocked())); + obj.push_back(Pair("mintablecoins", pwalletMain->MintableCoins())); + obj.push_back(Pair("enoughcoins", nReserveBalance <= pwalletMain->GetBalance())); + } + obj.push_back(Pair("mnsync", masternodeSync.IsSynced())); + + bool nStaking = false; + if (mapHashedBlocks.count(chainActive.Tip()->nHeight)) + nStaking = true; + else if (mapHashedBlocks.count(chainActive.Tip()->nHeight - 1) && nLastCoinStakeSearchInterval) + nStaking = true; + obj.push_back(Pair("staking status", nStaking)); + + return obj; +} +#endif // ENABLE_WALLET diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp new file mode 100644 index 0000000..3afd58f --- /dev/null +++ b/src/rpc/net.cpp @@ -0,0 +1,564 @@ +// Copyright (c) 2009-2014 The Bitcoin developers +// Copyright (c) 2014-2015 The Dash developers +// Copyright (c) 2015-2017 The PIVX developers +// Copyright (c) 2017-2019 The XDNA Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "rpc/server.h" + +#include "clientversion.h" +#include "main.h" +#include "net.h" +#include "netbase.h" +#include "protocol.h" +#include "sync.h" +#include "timedata.h" +#include "ui_interface.h" +#include "util.h" +#include "version.h" + +#include + +#include + +using namespace std; + +UniValue getconnectioncount(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getconnectioncount\n" + "\nReturns the number of connections to other nodes.\n" + "\nbResult:\n" + "n (numeric) The connection count\n" + "\nExamples:\n" + + HelpExampleCli("getconnectioncount", "") + HelpExampleRpc("getconnectioncount", "")); + + LOCK2(cs_main, cs_vNodes); + + return (int)vNodes.size(); +} + +UniValue ping(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "ping\n" + "\nRequests that a ping be sent to all other nodes, to measure ping time.\n" + "Results provided in getpeerinfo, pingtime and pingwait fields are decimal seconds.\n" + "Ping command is handled in queue with all other commands, so it measures processing backlog, not just network ping.\n" + "\nExamples:\n" + + HelpExampleCli("ping", "") + HelpExampleRpc("ping", "")); + + // Request that each node send a ping during next message processing pass + LOCK2(cs_main, cs_vNodes); + + for (CNode* pNode : vNodes) { + pNode->fPingQueued = true; + } + + return NullUniValue; +} + +static void CopyNodeStats(std::vector& vstats) +{ + vstats.clear(); + + LOCK(cs_vNodes); + vstats.reserve(vNodes.size()); + for (CNode* pnode : vNodes) { + CNodeStats stats; + pnode->copyStats(stats); + vstats.push_back(stats); + } +} + +UniValue getpeerinfo(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getpeerinfo\n" + "\nReturns data about each connected network node as a json array of objects.\n" + "\nbResult:\n" + "[\n" + " {\n" + " \"id\": n, (numeric) Peer index\n" + " \"addr\":\"host:port\", (string) The ip address and port of the peer\n" + " \"addrlocal\":\"ip:port\", (string) local address\n" + " \"services\":\"xxxxxxxxxxxxxxxx\", (string) The services offered\n" + " \"lastsend\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) of the last send\n" + " \"lastrecv\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) of the last receive\n" + " \"bytessent\": n, (numeric) The total bytes sent\n" + " \"bytesrecv\": n, (numeric) The total bytes received\n" + " \"conntime\": ttt, (numeric) The connection time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"pingtime\": n, (numeric) ping time\n" + " \"pingwait\": n, (numeric) ping wait\n" + " \"version\": v, (numeric) The peer version, such as 7001\n" + " \"subver\": \"/XDNA Core:x.x.x.x/\", (string) The string version\n" + " \"inbound\": true|false, (boolean) Inbound (true) or Outbound (false)\n" + " \"startingheight\": n, (numeric) The starting height (block) of the peer\n" + " \"banscore\": n, (numeric) The ban score\n" + " \"synced_headers\": n, (numeric) The last header we have in common with this peer\n" + " \"synced_blocks\": n, (numeric) The last block we have in common with this peer\n" + " \"inflight\": [\n" + " n, (numeric) The heights of blocks we're currently asking from this peer\n" + " ...\n" + " ]\n" + " }\n" + " ,...\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("getpeerinfo", "") + HelpExampleRpc("getpeerinfo", "")); + + LOCK(cs_main); + + vector vstats; + CopyNodeStats(vstats); + + UniValue ret(UniValue::VARR); + + for (const CNodeStats& stats : vstats) { + UniValue obj(UniValue::VOBJ); + CNodeStateStats statestats; + bool fStateStats = GetNodeStateStats(stats.nodeid, statestats); + obj.push_back(Pair("id", stats.nodeid)); + obj.push_back(Pair("addr", stats.addrName)); + if (!(stats.addrLocal.empty())) + obj.push_back(Pair("addrlocal", stats.addrLocal)); + obj.push_back(Pair("services", strprintf("%016x", stats.nServices))); + obj.push_back(Pair("lastsend", stats.nLastSend)); + obj.push_back(Pair("lastrecv", stats.nLastRecv)); + obj.push_back(Pair("bytessent", stats.nSendBytes)); + obj.push_back(Pair("bytesrecv", stats.nRecvBytes)); + obj.push_back(Pair("conntime", stats.nTimeConnected)); + obj.push_back(Pair("timeoffset", stats.nTimeOffset)); + obj.push_back(Pair("pingtime", stats.dPingTime)); + if (stats.dPingWait > 0.0) + obj.push_back(Pair("pingwait", stats.dPingWait)); + obj.push_back(Pair("version", stats.nVersion)); + // Use the sanitized form of subver here, to avoid tricksy remote peers from + // corrupting or modifiying the JSON output by putting special characters in + // their ver message. + obj.push_back(Pair("subver", stats.cleanSubVer)); + obj.push_back(Pair("inbound", stats.fInbound)); + obj.push_back(Pair("startingheight", stats.nStartingHeight)); + if (fStateStats) { + obj.push_back(Pair("banscore", statestats.nMisbehavior)); + obj.push_back(Pair("synced_headers", statestats.nSyncHeight)); + obj.push_back(Pair("synced_blocks", statestats.nCommonHeight)); + UniValue heights(UniValue::VARR); + for (int height : statestats.vHeightInFlight) { + heights.push_back(height); + } + obj.push_back(Pair("inflight", heights)); + } + obj.push_back(Pair("whitelisted", stats.fWhitelisted)); + + ret.push_back(obj); + } + + return ret; +} + +UniValue addnode(const UniValue& params, bool fHelp) +{ + string strCommand; + if (params.size() == 2) + strCommand = params[1].get_str(); + if (fHelp || params.size() != 2 || + (strCommand != "onetry" && strCommand != "add" && strCommand != "remove")) + throw runtime_error( + "addnode \"node\" \"add|remove|onetry\"\n" + "\nAttempts add or remove a node from the addnode list.\n" + "Or try a connection to a node once.\n" + "\nArguments:\n" + "1. \"node\" (string, required) The node (see getpeerinfo for nodes)\n" + "2. \"command\" (string, required) 'add' to add a node to the list, 'remove' to remove a node from the list, 'onetry' to try a connection to the node once\n" + "\nExamples:\n" + + HelpExampleCli("addnode", "\"192.168.0.6:1945\" \"onetry\"") + HelpExampleRpc("addnode", "\"192.168.0.6:1945\", \"onetry\"")); + + string strNode = params[0].get_str(); + + if (strCommand == "onetry") { + CAddress addr; + OpenNetworkConnection(addr, NULL, strNode.c_str()); + return NullUniValue; + } + + LOCK(cs_vAddedNodes); + vector::iterator it = vAddedNodes.begin(); + for (; it != vAddedNodes.end(); it++) + if (strNode == *it) + break; + + if (strCommand == "add") { + if (it != vAddedNodes.end()) + throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Node already added"); + vAddedNodes.push_back(strNode); + } else if (strCommand == "remove") { + if (it == vAddedNodes.end()) + throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added."); + vAddedNodes.erase(it); + } + + return NullUniValue; +} + +UniValue disconnectnode(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "disconnectnode \"node\" \n" + "\nImmediately disconnects from the specified node.\n" + + "\nArguments:\n" + "1. \"node\" (string, required) The node (see getpeerinfo for nodes)\n" + + "\nExamples:\n" + + HelpExampleCli("disconnectnode", "\"192.168.0.6:40555\"") + + HelpExampleRpc("disconnectnode", "\"192.168.0.6:40555\"") + ); + + CNode* pNode = FindNode(params[0].get_str()); + if (pNode == NULL) + throw JSONRPCError(RPC_CLIENT_NODE_NOT_CONNECTED, "Node not found in connected nodes"); + + pNode->CloseSocketDisconnect(); + + return NullUniValue; +} + +UniValue getaddednodeinfo(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getaddednodeinfo dns ( \"node\" )\n" + "\nReturns information about the given added node, or all added nodes\n" + "(note that onetry addnodes are not listed here)\n" + "If dns is false, only a list of added nodes will be provided,\n" + "otherwise connected information will also be available.\n" + "\nArguments:\n" + "1. dns (boolean, required) If false, only a list of added nodes will be provided, otherwise connected information will also be available.\n" + "2. \"node\" (string, optional) If provided, return information about this specific node, otherwise all nodes are returned.\n" + "\nResult:\n" + "[\n" + " {\n" + " \"addednode\" : \"192.168.0.201\", (string) The node ip address\n" + " \"connected\" : true|false, (boolean) If connected\n" + " \"addresses\" : [\n" + " {\n" + " \"address\" : \"192.168.0.201:1945\", (string) The xdna server host and port\n" + " \"connected\" : \"outbound\" (string) connection, inbound or outbound\n" + " }\n" + " ,...\n" + " ]\n" + " }\n" + " ,...\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("getaddednodeinfo", "true") + HelpExampleCli("getaddednodeinfo", "true \"192.168.0.201\"") + HelpExampleRpc("getaddednodeinfo", "true, \"192.168.0.201\"")); + + bool fDns = params[0].get_bool(); + + list laddedNodes(0); + if (params.size() == 1) { + LOCK(cs_vAddedNodes); + for (string& strAddNode : vAddedNodes) + laddedNodes.push_back(strAddNode); + } else { + string strNode = params[1].get_str(); + LOCK(cs_vAddedNodes); + for (string& strAddNode : vAddedNodes) + if (strAddNode == strNode) { + laddedNodes.push_back(strAddNode); + break; + } + if (laddedNodes.size() == 0) + throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added."); + } + + UniValue ret(UniValue::VARR); + if (!fDns) { + for (string& strAddNode : laddedNodes) { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("addednode", strAddNode)); + ret.push_back(obj); + } + return ret; + } + + list > > laddedAddreses(0); + for (string& strAddNode : laddedNodes) { + vector vservNode(0); + if (Lookup(strAddNode.c_str(), vservNode, Params().GetDefaultPort(), fNameLookup, 0)) + laddedAddreses.push_back(make_pair(strAddNode, vservNode)); + else { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("addednode", strAddNode)); + obj.push_back(Pair("connected", false)); + UniValue addresses(UniValue::VARR); + obj.push_back(Pair("addresses", addresses)); + } + } + + LOCK(cs_vNodes); + for (list > >::iterator it = laddedAddreses.begin(); it != laddedAddreses.end(); it++) { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("addednode", it->first)); + + UniValue addresses(UniValue::VARR); + bool fConnected = false; + for (CService& addrNode : it->second) { + bool fFound = false; + UniValue node(UniValue::VOBJ); + node.push_back(Pair("address", addrNode.ToString())); + for (CNode* pnode : vNodes) + if (pnode->addr == addrNode) { + fFound = true; + fConnected = true; + node.push_back(Pair("connected", pnode->fInbound ? "inbound" : "outbound")); + break; + } + if (!fFound) + node.push_back(Pair("connected", "false")); + addresses.push_back(node); + } + obj.push_back(Pair("connected", fConnected)); + obj.push_back(Pair("addresses", addresses)); + ret.push_back(obj); + } + + return ret; +} + +UniValue getnettotals(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 0) + throw runtime_error( + "getnettotals\n" + "\nReturns information about network traffic, including bytes in, bytes out,\n" + "and current time.\n" + "\nResult:\n" + "{\n" + " \"totalbytesrecv\": n, (numeric) Total bytes received\n" + " \"totalbytessent\": n, (numeric) Total bytes sent\n" + " \"timemillis\": t (numeric) Total cpu time\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getnettotals", "") + HelpExampleRpc("getnettotals", "")); + + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("totalbytesrecv", CNode::GetTotalBytesRecv())); + obj.push_back(Pair("totalbytessent", CNode::GetTotalBytesSent())); + obj.push_back(Pair("timemillis", GetTimeMillis())); + return obj; +} + +static UniValue GetNetworksInfo() +{ + UniValue networks(UniValue::VARR); + for (int n = 0; n < NET_MAX; ++n) { + enum Network network = static_cast(n); + if (network == NET_UNROUTABLE) + continue; + proxyType proxy; + UniValue obj(UniValue::VOBJ); + GetProxy(network, proxy); + obj.push_back(Pair("name", GetNetworkName(network))); + obj.push_back(Pair("limited", IsLimited(network))); + obj.push_back(Pair("reachable", IsReachable(network))); + obj.push_back(Pair("proxy", proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string())); + obj.push_back(Pair("proxy_randomize_credentials", proxy.randomize_credentials)); + networks.push_back(obj); + } + return networks; +} + +UniValue getnetworkinfo(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getnetworkinfo\n" + "\nReturns an object containing various state info regarding P2P networking.\n" + + "\nResult:\n" + "{\n" + " \"version\": xxxxx, (numeric) the server version\n" + " \"subversion\": \"/XDNA Core:x.x.x.x/\", (string) the server subversion string\n" + " \"protocolversion\": xxxxx, (numeric) the protocol version\n" + " \"localservices\": \"xxxxxxxxxxxxxxxx\", (string) the services we offer to the network\n" + " \"timeoffset\": xxxxx, (numeric) the time offset\n" + " \"connections\": xxxxx, (numeric) the number of connections\n" + " \"networks\": [ (array) information per network\n" + " {\n" + " \"name\": \"xxx\", (string) network (ipv4, ipv6 or onion)\n" + " \"limited\": true|false, (boolean) is the network limited using -onlynet?\n" + " \"reachable\": true|false, (boolean) is the network reachable?\n" + " \"proxy\": \"host:port\" (string) the proxy that is used for this network, or empty if none\n" + " }\n" + " ,...\n" + " ],\n" + " \"relayfee\": x.xxxxxxxx, (numeric) minimum relay fee for non-free transactions in xdna/kb\n" + " \"localaddresses\": [ (array) list of local addresses\n" + " {\n" + " \"address\": \"xxxx\", (string) network address\n" + " \"port\": xxx, (numeric) network port\n" + " \"score\": xxx (numeric) relative score\n" + " }\n" + " ,...\n" + " ]\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getnetworkinfo", "") + HelpExampleRpc("getnetworkinfo", "")); + + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("version", CLIENT_VERSION)); + obj.push_back(Pair("subversion", strSubVersion)); + obj.push_back(Pair("protocolversion", PROTOCOL_VERSION)); + obj.push_back(Pair("localservices", strprintf("%016x", nLocalServices))); + obj.push_back(Pair("timeoffset", GetTimeOffset())); + obj.push_back(Pair("connections", (int)vNodes.size())); + obj.push_back(Pair("networks", GetNetworksInfo())); + obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()))); + UniValue localAddresses(UniValue::VARR); + { + LOCK(cs_mapLocalHost); + for (const std::pair &item : mapLocalHost) { + UniValue rec(UniValue::VOBJ); + rec.push_back(Pair("address", item.first.ToString())); + rec.push_back(Pair("port", item.second.nPort)); + rec.push_back(Pair("score", item.second.nScore)); + localAddresses.push_back(rec); + } + } + obj.push_back(Pair("localaddresses", localAddresses)); + return obj; +} + +UniValue setban(const UniValue& params, bool fHelp) +{ + string strCommand; + if (params.size() >= 2) + strCommand = params[1].get_str(); + if (fHelp || params.size() < 2 || + (strCommand != "add" && strCommand != "remove")) + throw runtime_error( + "setban \"ip(/netmask)\" \"add|remove\" (bantime) (absolute)\n" + "\nAttempts add or remove a IP/Subnet from the banned list.\n" + + "\nArguments:\n" + "1. \"ip(/netmask)\" (string, required) The IP/Subnet (see getpeerinfo for nodes ip) with a optional netmask (default is /32 = single ip)\n" + "2. \"command\" (string, required) 'add' to add a IP/Subnet to the list, 'remove' to remove a IP/Subnet from the list\n" + "3. \"bantime\" (numeric, optional) time in seconds how long (or until when if [absolute] is set) the ip is banned (0 or empty means using the default time of 24h which can also be overwritten by the -bantime startup argument)\n" + "4. \"absolute\" (boolean, optional) If set, the bantime must be a absolute timestamp in seconds since epoch (Jan 1 1970 GMT)\n" + + "\nExamples:\n" + + HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400") + + HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"") + + HelpExampleRpc("setban", "\"192.168.0.6\", \"add\" 86400")); + + CSubNet subNet; + CNetAddr netAddr; + bool isSubnet = false; + + if (params[0].get_str().find("/") != string::npos) + isSubnet = true; + + if (!isSubnet) + netAddr = CNetAddr(params[0].get_str()); + else + subNet = CSubNet(params[0].get_str()); + + if (! (isSubnet ? subNet.IsValid() : netAddr.IsValid()) ) + throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Invalid IP/Subnet"); + + if (strCommand == "add") + { + if (isSubnet ? CNode::IsBanned(subNet) : CNode::IsBanned(netAddr)) + throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned"); + + int64_t banTime = 0; //use standard bantime if not specified + if (params.size() >= 3 && !params[2].isNull()) + banTime = params[2].get_int64(); + + bool absolute = false; + if (params.size() == 4) + absolute = params[3].get_bool(); + + isSubnet ? CNode::Ban(subNet, BanReasonManuallyAdded, banTime, absolute) : CNode::Ban(netAddr, BanReasonManuallyAdded, banTime, absolute); + + //disconnect possible nodes + while(CNode *bannedNode = (isSubnet ? FindNode(subNet) : FindNode(netAddr))) + bannedNode->CloseSocketDisconnect(); + } + else if(strCommand == "remove") + { + if (!( isSubnet ? CNode::Unban(subNet) : CNode::Unban(netAddr) )) + throw JSONRPCError(RPC_MISC_ERROR, "Error: Unban failed"); + } + + DumpBanlist(); //store banlist to disk + uiInterface.BannedListChanged(); + + return NullUniValue; +} + +UniValue listbanned(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "listbanned\n" + "\nList all banned IPs/Subnets.\n" + + "\nResult:\n" + "[\n" + " {\n" + " \"address\": \"xxx\", (string) Network address of banned client.\n" + " \"banned_until\": nnn, (numeric) Timestamp when the ban is lifted.\n" + " \"ban_created\": nnn, (numeric) Timestamp when the ban was created.\n" + " \"ban_reason\": \"xxx\" (string) Reason for banning.\n" + " }\n" + " ,...\n" + "]\n" + + "\nExamples:\n" + + HelpExampleCli("listbanned", "") + + HelpExampleRpc("listbanned", "")); + + banmap_t banMap; + CNode::GetBanned(banMap); + + UniValue bannedAddresses(UniValue::VARR); + for (banmap_t::iterator it = banMap.begin(); it != banMap.end(); it++) + { + CBanEntry banEntry = (*it).second; + UniValue rec(UniValue::VOBJ); + rec.push_back(Pair("address", (*it).first.ToString())); + rec.push_back(Pair("banned_until", banEntry.nBanUntil)); + rec.push_back(Pair("ban_created", banEntry.nCreateTime)); + rec.push_back(Pair("ban_reason", banEntry.banReasonToString())); + + bannedAddresses.push_back(rec); + } + + return bannedAddresses; +} + +UniValue clearbanned(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "clearbanned\n" + "\nClear all banned IPs.\n" + + "\nExamples:\n" + + HelpExampleCli("clearbanned", "") + + HelpExampleRpc("clearbanned", "")); + + CNode::ClearBanned(); + DumpBanlist(); //store banlist to disk + uiInterface.BannedListChanged(); + + return NullUniValue; +} diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp new file mode 100644 index 0000000..b19a794 --- /dev/null +++ b/src/rpc/protocol.cpp @@ -0,0 +1,294 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Copyright (c) 2014-2015 The Dash developers +// Copyright (c) 2015-2017 The PIVX developers +// Copyright (c) 2017-2019 The XDNA Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "rpc/protocol.h" + +#include "clientversion.h" +#include "tinyformat.h" +#include "util.h" +#include "utilstrencodings.h" +#include "utiltime.h" +#include "version.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; +using namespace boost; +using namespace boost::asio; + +//! Number of bytes to allocate and read at most at once in post data +const size_t POST_READ_SIZE = 256 * 1024; + +/** + * HTTP protocol + * + * This ain't Apache. We're just using HTTP header for the length field + * and to be compatible with other JSON-RPC implementations. + */ + +string HTTPPost(const string& strMsg, const map& mapRequestHeaders) +{ + ostringstream s; + s << "POST / HTTP/1.1\r\n" + << "User-Agent: xdna-json-rpc/" << FormatFullVersion() << "\r\n" + << "Host: 127.0.0.1\r\n" + << "Content-Type: application/json\r\n" + << "Content-Length: " << strMsg.size() << "\r\n" + << "Connection: close\r\n" + << "Accept: application/json\r\n"; + BOOST_FOREACH (const PAIRTYPE(string, string) & item, mapRequestHeaders) + s << item.first << ": " << item.second << "\r\n"; + s << "\r\n" + << strMsg; + + return s.str(); +} + +static string rfc1123Time() +{ + return DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", GetTime()); +} + +static const char* httpStatusDescription(int nStatus) +{ + switch (nStatus) { + case HTTP_OK: + return "OK"; + case HTTP_BAD_REQUEST: + return "Bad Request"; + case HTTP_FORBIDDEN: + return "Forbidden"; + case HTTP_NOT_FOUND: + return "Not Found"; + case HTTP_INTERNAL_SERVER_ERROR: + return "Internal Server Error"; + default: + return ""; + } +} + +string HTTPError(int nStatus, bool keepalive, bool headersOnly) +{ + if (nStatus == HTTP_UNAUTHORIZED) + return strprintf("HTTP/1.0 401 Authorization Required\r\n" + "Date: %s\r\n" + "Server: xdna-json-rpc/%s\r\n" + "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 296\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Error\r\n" + "\r\n" + "\r\n" + "

    401 Unauthorized.

    \r\n" + "\r\n", + rfc1123Time(), FormatFullVersion()); + + return HTTPReply(nStatus, httpStatusDescription(nStatus), keepalive, + headersOnly, "text/plain"); +} + +string HTTPReplyHeader(int nStatus, bool keepalive, size_t contentLength, const char* contentType) +{ + return strprintf( + "HTTP/1.1 %d %s\r\n" + "Date: %s\r\n" + "Connection: %s\r\n" + "Content-Length: %u\r\n" + "Content-Type: %s\r\n" + "Server: xdna-json-rpc/%s\r\n" + "\r\n", + nStatus, + httpStatusDescription(nStatus), + rfc1123Time(), + keepalive ? "keep-alive" : "close", + contentLength, + contentType, + FormatFullVersion()); +} + +string HTTPReply(int nStatus, const string& strMsg, bool keepalive, bool headersOnly, const char* contentType) +{ + if (headersOnly) { + return HTTPReplyHeader(nStatus, keepalive, 0, contentType); + } else { + return HTTPReplyHeader(nStatus, keepalive, strMsg.size(), contentType) + strMsg; + } +} + +bool ReadHTTPRequestLine(std::basic_istream& stream, int& proto, string& http_method, string& http_uri) +{ + string str; + getline(stream, str); + + // HTTP request line is space-delimited + vector vWords; + boost::split(vWords, str, boost::is_any_of(" ")); + if (vWords.size() < 2) + return false; + + // HTTP methods permitted: GET, POST + http_method = vWords[0]; + if (http_method != "GET" && http_method != "POST") + return false; + + // HTTP URI must be an absolute path, relative to current host + http_uri = vWords[1]; + if (http_uri.size() == 0 || http_uri[0] != '/') + return false; + + // parse proto, if present + string strProto = ""; + if (vWords.size() > 2) + strProto = vWords[2]; + + proto = 0; + const char* ver = strstr(strProto.c_str(), "HTTP/1."); + if (ver != NULL) + proto = atoi(ver + 7); + + return true; +} + +int ReadHTTPStatus(std::basic_istream& stream, int& proto) +{ + string str; + getline(stream, str); + //LogPrintf("ReadHTTPStatus - getline string: %s\n",str.c_str()); + vector vWords; + boost::split(vWords, str, boost::is_any_of(" ")); + if (vWords.size() < 2) + return HTTP_INTERNAL_SERVER_ERROR; + proto = 0; + const char* ver = strstr(str.c_str(), "HTTP/1."); + if (ver != NULL) + proto = atoi(ver + 7); + return atoi(vWords[1].c_str()); +} + +int ReadHTTPHeaders(std::basic_istream& stream, map& mapHeadersRet) +{ + int nLen = 0; + while (true) { + string str; + std::getline(stream, str); + if (str.empty() || str == "\r") + break; + string::size_type nColon = str.find(":"); + if (nColon != string::npos) { + string strHeader = str.substr(0, nColon); + boost::trim(strHeader); + boost::to_lower(strHeader); + string strValue = str.substr(nColon + 1); + boost::trim(strValue); + mapHeadersRet[strHeader] = strValue; + if (strHeader == "content-length") + nLen = atoi(strValue.c_str()); + } + } + return nLen; +} + + +int ReadHTTPMessage(std::basic_istream& stream, map& mapHeadersRet, string& strMessageRet, int nProto, size_t max_size) +{ + mapHeadersRet.clear(); + strMessageRet = ""; + + // Read header + int nLen = ReadHTTPHeaders(stream, mapHeadersRet); + if (nLen < 0 || (size_t)nLen > max_size) + return HTTP_INTERNAL_SERVER_ERROR; + + // Read message + if (nLen > 0) { + vector vch; + size_t ptr = 0; + while (ptr < (size_t)nLen) { + size_t bytes_to_read = std::min((size_t)nLen - ptr, POST_READ_SIZE); + vch.resize(ptr + bytes_to_read); + stream.read(&vch[ptr], bytes_to_read); + if (!stream) // Connection lost while reading + return HTTP_INTERNAL_SERVER_ERROR; + ptr += bytes_to_read; + } + strMessageRet = string(vch.begin(), vch.end()); + } + + string sConHdr = mapHeadersRet["connection"]; + + if ((sConHdr != "close") && (sConHdr != "keep-alive")) { + if (nProto >= 1) + mapHeadersRet["connection"] = "keep-alive"; + else + mapHeadersRet["connection"] = "close"; + } + + return HTTP_OK; +} + +/** + * JSON-RPC protocol. XDNA speaks version 1.0 for maximum compatibility, + * but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were + * unspecified (HTTP errors and contents of 'error'). + * + * 1.0 spec: http://json-rpc.org/wiki/specification + * 1.2 spec: http://jsonrpc.org/historical/json-rpc-over-http.html + * http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx + */ + +string JSONRPCRequest(const string& strMethod, const UniValue& params, const UniValue& id) +{ + UniValue request(UniValue::VOBJ); + request.push_back(Pair("method", strMethod)); + request.push_back(Pair("params", params)); + request.push_back(Pair("id", id)); + return request.write() + "\n"; +} + +UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id) +{ + UniValue reply(UniValue::VOBJ); + if (!error.isNull()) + reply.push_back(Pair("result", NullUniValue)); + else + reply.push_back(Pair("result", result)); + reply.push_back(Pair("error", error)); + reply.push_back(Pair("id", id)); + return reply; +} + +string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id) +{ + UniValue reply = JSONRPCReplyObj(result, error, id); + return reply.write() + "\n"; +} + +UniValue JSONRPCError(int code, const string& message) +{ + UniValue error(UniValue::VOBJ); + error.push_back(Pair("code", code)); + error.push_back(Pair("message", message)); + return error; +} diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h new file mode 100644 index 0000000..e8149e5 --- /dev/null +++ b/src/rpc/protocol.h @@ -0,0 +1,161 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 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_RPCPROTOCOL_H +#define BITCOIN_RPCPROTOCOL_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//! HTTP status codes +enum HTTPStatusCode { + HTTP_OK = 200, + HTTP_BAD_REQUEST = 400, + HTTP_UNAUTHORIZED = 401, + HTTP_FORBIDDEN = 403, + HTTP_NOT_FOUND = 404, + HTTP_BAD_METHOD = 405, + HTTP_INTERNAL_SERVER_ERROR = 500, + HTTP_SERVICE_UNAVAILABLE = 503, +}; + +//! XDNA RPC error codes +enum RPCErrorCode { + //! Standard JSON-RPC 2.0 errors + RPC_INVALID_REQUEST = -32600, + RPC_METHOD_NOT_FOUND = -32601, + RPC_INVALID_PARAMS = -32602, + RPC_INTERNAL_ERROR = -32603, + RPC_PARSE_ERROR = -32700, + + //! General application defined errors + RPC_MISC_ERROR = -1, //! std::exception thrown in command handling + RPC_FORBIDDEN_BY_SAFE_MODE = -2, //! Server is in safe mode, and command is not allowed in safe mode + RPC_TYPE_ERROR = -3, //! Unexpected type was passed as parameter + RPC_INVALID_ADDRESS_OR_KEY = -5, //! Invalid address or key + RPC_OUT_OF_MEMORY = -7, //! Ran out of memory during operation + RPC_INVALID_PARAMETER = -8, //! Invalid, missing or duplicate parameter + RPC_DATABASE_ERROR = -20, //! Database error + RPC_DESERIALIZATION_ERROR = -22, //! Error parsing or validating structure in raw format + RPC_VERIFY_ERROR = -25, //! General error during transaction or block submission + RPC_VERIFY_REJECTED = -26, //! Transaction or block was rejected by network rules + RPC_VERIFY_ALREADY_IN_CHAIN = -27, //! Transaction already in chain + RPC_IN_WARMUP = -28, //! Client still warming up + + //! Aliases for backward compatibility + RPC_TRANSACTION_ERROR = RPC_VERIFY_ERROR, + RPC_TRANSACTION_REJECTED = RPC_VERIFY_REJECTED, + RPC_TRANSACTION_ALREADY_IN_CHAIN = RPC_VERIFY_ALREADY_IN_CHAIN, + + //! P2P client errors + RPC_CLIENT_NOT_CONNECTED = -9, //! XDNA is not connected + RPC_CLIENT_IN_INITIAL_DOWNLOAD = -10, //! Still downloading initial blocks + RPC_CLIENT_NODE_ALREADY_ADDED = -23, //! Node is already added + RPC_CLIENT_NODE_NOT_ADDED = -24, //! Node has not been added before + RPC_CLIENT_NODE_NOT_CONNECTED = -29, //! Node to disconnect not found in connected nodes + RPC_CLIENT_INVALID_IP_OR_SUBNET = -30, //! Invalid IP/Subnet + + //! Wallet errors + RPC_WALLET_ERROR = -4, //! Unspecified problem with wallet (key not found etc.) + RPC_WALLET_INSUFFICIENT_FUNDS = -6, //! Not enough funds in wallet or account + RPC_WALLET_INVALID_ACCOUNT_NAME = -11, //! Invalid account name + RPC_WALLET_KEYPOOL_RAN_OUT = -12, //! Keypool ran out, call keypoolrefill first + RPC_WALLET_UNLOCK_NEEDED = -13, //! Enter the wallet passphrase with walletpassphrase first + RPC_WALLET_PASSPHRASE_INCORRECT = -14, //! The wallet passphrase entered was incorrect + RPC_WALLET_WRONG_ENC_STATE = -15, //! Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.) + RPC_WALLET_ENCRYPTION_FAILED = -16, //! Failed to encrypt the wallet + RPC_WALLET_ALREADY_UNLOCKED = -17, //! Wallet is already unlocked +}; + +/** + * IOStream device that speaks SSL but can also speak non-SSL + */ +template +class SSLIOStreamDevice : public boost::iostreams::device +{ +public: + SSLIOStreamDevice(boost::asio::ssl::stream& streamIn, bool fUseSSLIn) : stream(streamIn) + { + fUseSSL = fUseSSLIn; + fNeedHandshake = fUseSSLIn; + } + + void handshake(boost::asio::ssl::stream_base::handshake_type role) + { + if (!fNeedHandshake) return; + fNeedHandshake = false; + stream.handshake(role); + } + std::streamsize read(char* s, std::streamsize n) + { + handshake(boost::asio::ssl::stream_base::server); // HTTPS servers read first + if (fUseSSL) return stream.read_some(boost::asio::buffer(s, n)); + return stream.next_layer().read_some(boost::asio::buffer(s, n)); + } + std::streamsize write(const char* s, std::streamsize n) + { + handshake(boost::asio::ssl::stream_base::client); // HTTPS clients write first + if (fUseSSL) return boost::asio::write(stream, boost::asio::buffer(s, n)); + return boost::asio::write(stream.next_layer(), boost::asio::buffer(s, n)); + } + bool connect(const std::string& server, const std::string& port) + { + using namespace boost::asio::ip; + tcp::resolver resolver(stream.get_io_service()); + tcp::resolver::iterator endpoint_iterator; +#if BOOST_VERSION >= 104300 + try { +#endif + // The default query (flags address_configured) tries IPv6 if + // non-localhost IPv6 configured, and IPv4 if non-localhost IPv4 + // configured. + tcp::resolver::query query(server.c_str(), port.c_str()); + endpoint_iterator = resolver.resolve(query); +#if BOOST_VERSION >= 104300 + } catch (boost::system::system_error& e) { + // If we at first don't succeed, try blanket lookup (IPv4+IPv6 independent of configured interfaces) + tcp::resolver::query query(server.c_str(), port.c_str(), resolver_query_base::flags()); + endpoint_iterator = resolver.resolve(query); + } +#endif + boost::system::error_code error = boost::asio::error::host_not_found; + tcp::resolver::iterator end; + while (error && endpoint_iterator != end) { + stream.lowest_layer().close(); + stream.lowest_layer().connect(*endpoint_iterator++, error); + } + if (error) + return false; + return true; + } + +private: + bool fNeedHandshake; + bool fUseSSL; + boost::asio::ssl::stream& stream; +}; + +std::string HTTPPost(const std::string& strMsg, const std::map& mapRequestHeaders); +std::string HTTPError(int nStatus, bool keepalive, bool headerOnly = false); +std::string HTTPReplyHeader(int nStatus, bool keepalive, size_t contentLength, const char* contentType = "application/json"); +std::string HTTPReply(int nStatus, const std::string& strMsg, bool keepalive, bool headerOnly = false, const char* contentType = "application/json"); +bool ReadHTTPRequestLine(std::basic_istream& stream, int& proto, std::string& http_method, std::string& http_uri); +int ReadHTTPStatus(std::basic_istream& stream, int& proto); +int ReadHTTPHeaders(std::basic_istream& stream, std::map& mapHeadersRet); +int ReadHTTPMessage(std::basic_istream& stream, std::map& mapHeadersRet, std::string& strMessageRet, int nProto, size_t max_size); +std::string JSONRPCRequest(const std::string& strMethod, const UniValue& params, const UniValue& id); +UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id); +std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id); +UniValue JSONRPCError(int code, const std::string& message); + +#endif // BITCOIN_RPCPROTOCOL_H diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp new file mode 100644 index 0000000..bb8f04b --- /dev/null +++ b/src/rpc/rawtransaction.cpp @@ -0,0 +1,867 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Copyright (c) 2014-2015 The Dash developers +// Copyright (c) 2015-2017 The PIVX developers +// Copyright (c) 2017-2019 The XDNA Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "base58.h" +#include "core_io.h" +#include "init.h" +#include "keystore.h" +#include "main.h" +#include "net.h" +#include "primitives/transaction.h" +#include "rpc/server.h" +#include "script/script.h" +#include "script/sign.h" +#include "script/standard.h" +#include "swifttx.h" +#include "uint256.h" +#include "utilmoneystr.h" +#ifdef ENABLE_WALLET +#include "wallet/wallet.h" +#endif + +#include + +#include + +#include + +using namespace boost; +using namespace boost::assign; +using namespace std; + +void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex) +{ + txnouttype type; + vector addresses; + int nRequired; + + out.push_back(Pair("asm", scriptPubKey.ToString())); + if (fIncludeHex) + out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); + + if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) { + out.push_back(Pair("type", GetTxnOutputType(type))); + return; + } + + out.push_back(Pair("reqSigs", nRequired)); + out.push_back(Pair("type", GetTxnOutputType(type))); + + UniValue a(UniValue::VARR); + BOOST_FOREACH (const CTxDestination& addr, addresses) + a.push_back(CBitcoinAddress(addr).ToString()); + out.push_back(Pair("addresses", a)); +} + +void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) +{ + // Call into TxToUniv() in bitcoin-common to decode the transaction hex. + // + // Blockchain contextual information (confirmations and blocktime) is not + // available to code in bitcoin-common, so we query them here and push the + // data into the returned UniValue. + TxToUniv(tx, uint256(), entry); + + if (!hashBlock.IsNull()) { + entry.push_back(Pair("blockhash", hashBlock.GetHex())); + BlockMap::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end() && (*mi).second) { + CBlockIndex* pindex = (*mi).second; + if (chainActive.Contains(pindex)) { + entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight)); + entry.push_back(Pair("time", pindex->GetBlockTime())); + entry.push_back(Pair("blocktime", pindex->GetBlockTime())); + } + else + entry.push_back(Pair("confirmations", 0)); + } + } +} + +UniValue getrawtransaction(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 3) + throw runtime_error( + "getrawtransaction \"txid\" ( verbose \"blockhash\" )\n" + + "\nNOTE: By default this function only works sometimes. This is when the tx is in the mempool\n" + "or there is an unspent output in the utxo for this transaction. To make it always work,\n" + "you need to maintain a transaction index, using the -txindex command line option.\n" + + "\nReturn the raw transaction data.\n" + "\nIf verbose is 'true', returns an Object with information about 'txid'.\n" + "If verbose is 'false' or omitted, returns a string that is serialized, hex-encoded data for 'txid'.\n" + + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction id\n" + "2. verbose (bool, optional, default=false) If false, return a string, otherwise return a json object\n" + "3. \"blockhash\" (string, optional) The block in which to look for the transaction\n" + + "\nResult (if verbose is not set or set to false):\n" + "\"data\" (string) The serialized, hex-encoded data for 'txid'\n" + + "\nResult (if verbose is set to true):\n" + "{\n" + " \"in_active_chain\": b, (bool) Whether specified block is in the active chain or not (only present with explicit \"blockhash\" argument)\n" + " \"hex\" : \"data\", (string) The serialized, hex-encoded data for 'txid'\n" + " \"txid\" : \"id\", (string) The transaction id (same as provided)\n" + " \"size\" : n, (numeric) The serialized transaction size\n" + " \"version\" : n, (numeric) The version\n" + " \"locktime\" : ttt, (numeric) The lock time\n" + " \"vin\" : [ (array of json objects)\n" + " {\n" + " \"txid\": \"id\", (string) The transaction id\n" + " \"vout\": n, (numeric) \n" + " \"scriptSig\": { (json object) The script\n" + " \"asm\": \"asm\", (string) asm\n" + " \"hex\": \"hex\" (string) hex\n" + " },\n" + " \"sequence\": n (numeric) The script sequence number\n" + " }\n" + " ,...\n" + " ],\n" + " \"vout\" : [ (array of json objects)\n" + " {\n" + " \"value\" : x.xxx, (numeric) The value in btc\n" + " \"n\" : n, (numeric) index\n" + " \"scriptPubKey\" : { (json object)\n" + " \"asm\" : \"asm\", (string) the asm\n" + " \"hex\" : \"hex\", (string) the hex\n" + " \"reqSigs\" : n, (numeric) The required sigs\n" + " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" + " \"addresses\" : [ (json array of string)\n" + " \"xdnaaddress\" (string) xdna address\n" + " ,...\n" + " ]\n" + " }\n" + " }\n" + " ,...\n" + " ],\n" + " \"blockhash\" : \"hash\", (string) the block hash\n" + " \"confirmations\" : n, (numeric) The confirmations\n" + " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"blocktime\" : ttt (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("getrawtransaction", "\"mytxid\"") + + HelpExampleCli("getrawtransaction", "\"mytxid\" true") + + HelpExampleRpc("getrawtransaction", "\"mytxid\", true") + + HelpExampleCli("getrawtransaction", "\"mytxid\" false \"myblockhash\"") + + HelpExampleCli("getrawtransaction", "\"mytxid\" true \"myblockhash\"") + ); + + LOCK(cs_main); + + bool in_active_chain = true; + uint256 hash = ParseHashV(params[0], "parameter 1"); + CBlockIndex* blockindex = nullptr; + + bool fVerbose = false; + if (!params[1].isNull()) { + fVerbose = params[1].isNum() ? (params[1].get_int() != 0) : params[1].get_bool(); + } + + if (!params[2].isNull()) { + uint256 blockhash = ParseHashV(params[2], "parameter 3"); + BlockMap::iterator it = mapBlockIndex.find(blockhash); + if (it == mapBlockIndex.end()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block hash not found"); + } + blockindex = it->second; + in_active_chain = chainActive.Contains(blockindex); + } + + CTransaction tx; + uint256 hash_block; + if (!GetTransaction(hash, tx, hash_block, true)) { + std::string errmsg; + if (blockindex) { + if (!(blockindex->nStatus & BLOCK_HAVE_DATA)) { + throw JSONRPCError(RPC_MISC_ERROR, "Block not available"); + } + errmsg = "No such transaction found in the provided block"; + } else { + errmsg = fTxIndex + ? "No such mempool or blockchain transaction" + : "No such mempool transaction. Use -txindex to enable blockchain transaction queries"; + } + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, errmsg + ". Use gettransaction for wallet transactions."); + } + + if (!fVerbose) { + return EncodeHexTx(tx); + } + + UniValue result(UniValue::VOBJ); + if (blockindex) result.push_back(Pair("in_active_chain", in_active_chain)); + TxToJSON(tx, hash_block, result); + return result; +} + +#ifdef ENABLE_WALLET +UniValue listunspent(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 4) + throw runtime_error( + "listunspent ( minconf maxconf [\"address\",...] )\n" + "\nReturns array of unspent transaction outputs\n" + "with between minconf and maxconf (inclusive) confirmations.\n" + "Optionally filter to only include txouts paid to specified addresses.\n" + "Results are an array of Objects, each of which has:\n" + "{txid, vout, scriptPubKey, amount, confirmations}\n" + + "\nArguments:\n" + "1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n" + "2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n" + "3. \"addresses\" (string) A json array of xdna addresses to filter\n" + " [\n" + " \"address\" (string) xdna address\n" + " ,...\n" + " ]\n" + "4. watchonlyconfig (numberic, optional, default=1) 1 = list regular unspent transactions, 2 = list only watchonly transactions, 3 = list all unspent transactions (including watchonly)\n" + + "\nResult\n" + "[ (array of json object)\n" + " {\n" + " \"txid\" : \"txid\", (string) the transaction id \n" + " \"vout\" : n, (numeric) the vout value\n" + " \"address\" : \"address\", (string) the xdna address\n" + " \"account\" : \"account\", (string) The associated account, or \"\" for the default account\n" + " \"scriptPubKey\" : \"key\", (string) the script key\n" + " \"amount\" : x.xxx, (numeric) the transaction amount in btc\n" + " \"confirmations\" : n (numeric) The number of confirmations\n" + " }\n" + " ,...\n" + "]\n" + + "\nExamples\n" + + HelpExampleCli("listunspent", "") + HelpExampleCli("listunspent", "6 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"")); + + RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)(UniValue::VNUM)(UniValue::VARR)(UniValue::VNUM)); + + int nMinDepth = 1; + if (params.size() > 0) + nMinDepth = params[0].get_int(); + + int nMaxDepth = 9999999; + if (params.size() > 1) + nMaxDepth = params[1].get_int(); + + set setAddress; + if (params.size() > 2) { + UniValue inputs = params[2].get_array(); + for (unsigned int inx = 0; inx < inputs.size(); inx++) { + const UniValue& input = inputs[inx]; + CBitcoinAddress address(input.get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid XDNA address: ") + input.get_str()); + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ") + input.get_str()); + setAddress.insert(address); + } + } + + int nWatchonlyConfig = 1; + if(params.size() > 3) { + nWatchonlyConfig = params[3].get_int(); + if (nWatchonlyConfig > 3 || nWatchonlyConfig < 1) + nWatchonlyConfig = 1; + } + + UniValue results(UniValue::VARR); + vector vecOutputs; + assert(pwalletMain != NULL); + LOCK2(cs_main, pwalletMain->cs_wallet); + pwalletMain->AvailableCoins(vecOutputs, false, NULL, false); + BOOST_FOREACH (const COutput& out, vecOutputs) { + if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) + continue; + + if (setAddress.size()) { + CTxDestination address; + if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) + continue; + + if (!setAddress.count(address)) + continue; + } + + CAmount nValue = out.tx->vout[out.i].nValue; + const CScript& pk = out.tx->vout[out.i].scriptPubKey; + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); + entry.push_back(Pair("vout", out.i)); + CTxDestination address; + if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { + entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); + if (pwalletMain->mapAddressBook.count(address)) + entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name)); + } + entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end()))); + if (pk.IsPayToScriptHash()) { + CTxDestination address; + if (ExtractDestination(pk, address)) { + const CScriptID& hash = boost::get(address); + CScript redeemScript; + if (pwalletMain->GetCScript(hash, redeemScript)) + entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); + } + } + entry.push_back(Pair("amount", ValueFromAmount(nValue))); + entry.push_back(Pair("confirmations", out.nDepth)); + entry.push_back(Pair("spendable", out.fSpendable)); + results.push_back(entry); + } + + return results; +} +#endif + +UniValue createrawtransaction(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 3) + throw runtime_error( + "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,...} ( locktime )\n" + "\nCreate a transaction spending the given inputs and sending to the given addresses.\n" + "Returns hex-encoded raw transaction.\n" + "Note that the transaction's inputs are not signed, and\n" + "it is not stored in the wallet or transmitted to the network.\n" + + "\nArguments:\n" + "1. \"transactions\" (string, required) A json array of json objects\n" + " [\n" + " {\n" + " \"txid\":\"id\", (string, required) The transaction id\n" + " \"vout\":n, (numeric, required) The output number\n" + " \"sequence\":n (numeric, optional) The sequence number\n" + " }\n" + " ,...\n" + " ]\n" + "2. \"addresses\" (string, required) a json object with addresses as keys and amounts as values\n" + " {\n" + " \"address\": x.xxx (numeric, required) The key is the xdna address, the value is the btc amount\n" + " ,...\n" + " }\n" + "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n" + + "\nResult:\n" + "\"transaction\" (string) hex string of the transaction\n" + + "\nExamples\n" + + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"address\\\":0.01}\"") + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"address\\\":0.01}\"")); + + LOCK(cs_main); + RPCTypeCheck(params, boost::assign::list_of(UniValue::VARR)(UniValue::VOBJ)(UniValue::VNUM)); + if (params[0].isNull() || params[1].isNull()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null"); + + UniValue inputs = params[0].get_array(); + UniValue sendTo = params[1].get_obj(); + + CMutableTransaction rawTx; + + if (params.size() > 2 && !params[2].isNull()) { + int64_t nLockTime = params[2].get_int64(); + if (nLockTime < 0 || nLockTime > std::numeric_limits::max()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range"); + rawTx.nLockTime = nLockTime; + } + + for (unsigned int idx = 0; idx < inputs.size(); idx++) { + const UniValue& input = inputs[idx]; + const UniValue& o = input.get_obj(); + + uint256 txid = ParseHashO(o, "txid"); + + const UniValue& vout_v = find_value(o, "vout"); + if (!vout_v.isNum()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); + int nOutput = vout_v.get_int(); + if (nOutput < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); + + uint32_t nSequence = (rawTx.nLockTime ? std::numeric_limits::max() - 1 : std::numeric_limits::max()); + + // set the sequence number if passed in the parameters object + const UniValue& sequenceObj = find_value(o, "sequence"); + if (sequenceObj.isNum()) { + int64_t seqNr64 = sequenceObj.get_int64(); + if (seqNr64 < 0 || seqNr64 > std::numeric_limits::max()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range"); + else + nSequence = (uint32_t)seqNr64; + } + + CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence); + + rawTx.vin.push_back(in); + } + + set setAddress; + vector addrList = sendTo.getKeys(); + BOOST_FOREACH(const string& name_, addrList) { + CBitcoinAddress address(name_); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid XDNA address: ")+name_); + + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_); + setAddress.insert(address); + + CScript scriptPubKey = GetScriptForDestination(address.Get()); + CAmount nAmount = AmountFromValue(sendTo[name_]); + + CTxOut out(nAmount, scriptPubKey); + rawTx.vout.push_back(out); + } + + return EncodeHexTx(rawTx); +} + +UniValue decoderawtransaction(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "decoderawtransaction \"hexstring\"\n" + "\nReturn a JSON object representing the serialized, hex-encoded transaction.\n" + + "\nArguments:\n" + "1. \"hex\" (string, required) The transaction hex string\n" + + "\nResult:\n" + "{\n" + " \"txid\" : \"id\", (string) The transaction id\n" + " \"size\" : n, (numeric) The transaction size\n" + " \"version\" : n, (numeric) The version\n" + " \"locktime\" : ttt, (numeric) The lock time\n" + " \"vin\" : [ (array of json objects)\n" + " {\n" + " \"txid\": \"id\", (string) The transaction id\n" + " \"vout\": n, (numeric) The output number\n" + " \"scriptSig\": { (json object) The script\n" + " \"asm\": \"asm\", (string) asm\n" + " \"hex\": \"hex\" (string) hex\n" + " },\n" + " \"sequence\": n (numeric) The script sequence number\n" + " }\n" + " ,...\n" + " ],\n" + " \"vout\" : [ (array of json objects)\n" + " {\n" + " \"value\" : x.xxx, (numeric) The value in btc\n" + " \"n\" : n, (numeric) index\n" + " \"scriptPubKey\" : { (json object)\n" + " \"asm\" : \"asm\", (string) the asm\n" + " \"hex\" : \"hex\", (string) the hex\n" + " \"reqSigs\" : n, (numeric) The required sigs\n" + " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" + " \"addresses\" : [ (json array of string)\n" + " \"12tvKAXCxZjSmdNbao16dKXC8tRWfcF5oc\" (string) xdna address\n" + " ,...\n" + " ]\n" + " }\n" + " }\n" + " ,...\n" + " ],\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("decoderawtransaction", "\"hexstring\"") + HelpExampleRpc("decoderawtransaction", "\"hexstring\"")); + + LOCK(cs_main); + RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)); + + CTransaction tx; + + if (!DecodeHexTx(tx, params[0].get_str())) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + + UniValue result(UniValue::VOBJ); + TxToJSON(tx, 0, result); + + return result; +} + +UniValue decodescript(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "decodescript \"hex\"\n" + "\nDecode a hex-encoded script.\n" + + "\nArguments:\n" + "1. \"hex\" (string) the hex encoded script\n" + + "\nResult:\n" + "{\n" + " \"asm\":\"asm\", (string) Script public key\n" + " \"hex\":\"hex\", (string) hex encoded public key\n" + " \"type\":\"type\", (string) The output type\n" + " \"reqSigs\": n, (numeric) The required signatures\n" + " \"addresses\": [ (json array of string)\n" + " \"address\" (string) xdna address\n" + " ,...\n" + " ],\n" + " \"p2sh\",\"address\" (string) script address\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("decodescript", "\"hexstring\"") + HelpExampleRpc("decodescript", "\"hexstring\"")); + + LOCK(cs_main); + RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)); + + UniValue r(UniValue::VOBJ); + CScript script; + if (params[0].get_str().size() > 0) { + vector scriptData(ParseHexV(params[0], "argument")); + script = CScript(scriptData.begin(), scriptData.end()); + } else { + // Empty scripts are valid + } + ScriptPubKeyToJSON(script, r, false); + + r.push_back(Pair("p2sh", CBitcoinAddress(CScriptID(script)).ToString())); + return r; +} + +/** Pushes a JSON object for script verification or signing errors to vErrorsRet. */ +static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage) +{ + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("txid", txin.prevout.hash.ToString())); + entry.push_back(Pair("vout", (uint64_t)txin.prevout.n)); + entry.push_back(Pair("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); + entry.push_back(Pair("sequence", (uint64_t)txin.nSequence)); + entry.push_back(Pair("error", strMessage)); + vErrorsRet.push_back(entry); +} + +UniValue signrawtransaction(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 4) + throw runtime_error( + "signrawtransaction \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype )\n" + "\nSign inputs for raw transaction (serialized, hex-encoded).\n" + "The second optional argument (may be null) is an array of previous transaction outputs that\n" + "this transaction depends on but may not yet be in the block chain.\n" + "The third optional argument (may be null) is an array of base58-encoded private\n" + "keys that, if given, will be the only keys used to sign the transaction.\n" +#ifdef ENABLE_WALLET + + HelpRequiringPassphrase() + "\n" +#endif + + "\nArguments:\n" + "1. \"hexstring\" (string, required) The transaction hex string\n" + "2. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n" + " [ (json array of json objects, or 'null' if none provided)\n" + " {\n" + " \"txid\":\"id\", (string, required) The transaction id\n" + " \"vout\":n, (numeric, required) The output number\n" + " \"scriptPubKey\": \"hex\", (string, required) script key\n" + " \"redeemScript\": \"hex\" (string, required for P2SH) redeem script\n" + " }\n" + " ,...\n" + " ]\n" + "3. \"privatekeys\" (string, optional) A json array of base58-encoded private keys for signing\n" + " [ (json array of strings, or 'null' if none provided)\n" + " \"privatekey\" (string) private key in base58-encoding\n" + " ,...\n" + " ]\n" + "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n" + " \"ALL\"\n" + " \"NONE\"\n" + " \"SINGLE\"\n" + " \"ALL|ANYONECANPAY\"\n" + " \"NONE|ANYONECANPAY\"\n" + " \"SINGLE|ANYONECANPAY\"\n" + + "\nResult:\n" + "{\n" + " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n" + " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n" + " \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n" + " {\n" + " \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n" + " \"vout\" : n, (numeric) The index of the output to spent and used as input\n" + " \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n" + " \"sequence\" : n, (numeric) Script sequence number\n" + " \"error\" : \"text\" (string) Verification or signing error related to the input\n" + " }\n" + " ,...\n" + " ]\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("signrawtransaction", "\"myhex\"") + HelpExampleRpc("signrawtransaction", "\"myhex\"")); + +#ifdef ENABLE_WALLET + LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL); +#else + LOCK(cs_main); +#endif + RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VARR)(UniValue::VARR)(UniValue::VSTR), true); + + vector txData(ParseHexV(params[0], "argument 1")); + CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); + vector txVariants; + while (!ssData.empty()) { + try { + CMutableTransaction tx; + ssData >> tx; + txVariants.push_back(tx); + } catch (const std::exception&) { + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + } + } + + if (txVariants.empty()) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction"); + + // mergedTx will end up with all the signatures; it + // starts as a clone of the rawtx: + CMutableTransaction mergedTx(txVariants[0]); + + // Fetch previous transactions (inputs): + std::map mapPrevOut; + if (Params().NetworkID() == CBaseChainParams::REGTEST) { + for (const CTxIn &txbase : mergedTx.vin) + { + CTransaction tempTx; + uint256 hashBlock; + if (GetTransaction(txbase.prevout.hash, tempTx, hashBlock, true)) { + // Copy results into mapPrevOut: + mapPrevOut[txbase.prevout] = tempTx.vout[txbase.prevout.n].scriptPubKey; + } + } + } + CCoinsView viewDummy; + CCoinsViewCache view(&viewDummy); + { + LOCK(mempool.cs); + CCoinsViewCache& viewChain = *pcoinsTip; + CCoinsViewMemPool viewMempool(&viewChain, mempool); + view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view + + BOOST_FOREACH (const CTxIn& txin, mergedTx.vin) { + const uint256& prevHash = txin.prevout.hash; + CCoins coins; + view.AccessCoins(prevHash); // this is certainly allowed to fail + } + + view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long + } + + bool fGivenKeys = false; + CBasicKeyStore tempKeystore; + if (params.size() > 2 && !params[2].isNull()) { + fGivenKeys = true; + UniValue keys = params[2].get_array(); + for (unsigned int idx = 0; idx < keys.size(); idx++) { + UniValue k = keys[idx]; + CBitcoinSecret vchSecret; + bool fGood = vchSecret.SetString(k.get_str()); + if (!fGood) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); + CKey key = vchSecret.GetKey(); + if (!key.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range"); + tempKeystore.AddKey(key); + } + } +#ifdef ENABLE_WALLET + else if (pwalletMain) + EnsureWalletIsUnlocked(); +#endif + + // Add previous txouts given in the RPC call: + if (params.size() > 1 && !params[1].isNull()) { + UniValue prevTxs = params[1].get_array(); + for (unsigned int idx = 0; idx < prevTxs.size(); idx++) { + const UniValue& p = prevTxs[idx]; + if (!p.isObject()) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}"); + + UniValue prevOut = p.get_obj(); + + RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR)); + + uint256 txid = ParseHashO(prevOut, "txid"); + + int nOut = find_value(prevOut, "vout").get_int(); + if (nOut < 0) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive"); + + vector pkData(ParseHexO(prevOut, "scriptPubKey")); + CScript scriptPubKey(pkData.begin(), pkData.end()); + + { + CCoinsModifier coins = view.ModifyCoins(txid); + if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) { + string err("Previous output scriptPubKey mismatch:\n"); + err = err + coins->vout[nOut].scriptPubKey.ToString() + "\nvs:\n" + + scriptPubKey.ToString(); + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); + } + if ((unsigned int)nOut >= coins->vout.size()) + coins->vout.resize(nOut + 1); + coins->vout[nOut].scriptPubKey = scriptPubKey; + coins->vout[nOut].nValue = 0; // we don't know the actual output value + } + + // if redeemScript given and not using the local wallet (private keys + // given), add redeemScript to the tempKeystore so it can be signed: + if (fGivenKeys && scriptPubKey.IsPayToScriptHash()) { + RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR)("redeemScript",UniValue::VSTR)); + UniValue v = find_value(prevOut, "redeemScript"); + if (!v.isNull()) { + vector rsData(ParseHexV(v, "redeemScript")); + CScript redeemScript(rsData.begin(), rsData.end()); + tempKeystore.AddCScript(redeemScript); + } + } + } + } + +#ifdef ENABLE_WALLET + const CKeyStore& keystore = ((fGivenKeys || !pwalletMain) ? tempKeystore : *pwalletMain); +#else + const CKeyStore& keystore = tempKeystore; +#endif + + int nHashType = SIGHASH_ALL; + if (params.size() > 3 && !params[3].isNull()) { + static map mapSigHashValues = + boost::assign::map_list_of(string("ALL"), int(SIGHASH_ALL))(string("ALL|ANYONECANPAY"), int(SIGHASH_ALL | SIGHASH_ANYONECANPAY))(string("NONE"), int(SIGHASH_NONE))(string("NONE|ANYONECANPAY"), int(SIGHASH_NONE | SIGHASH_ANYONECANPAY))(string("SINGLE"), int(SIGHASH_SINGLE))(string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE | SIGHASH_ANYONECANPAY)); + string strHashType = params[3].get_str(); + if (mapSigHashValues.count(strHashType)) + nHashType = mapSigHashValues[strHashType]; + else + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param"); + } + + bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); + + // Script verification errors + UniValue vErrors(UniValue::VARR); + + // Sign what we can: + for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { + CTxIn& txin = mergedTx.vin[i]; + const CCoins* coins = view.AccessCoins(txin.prevout.hash); + if (Params().NetworkID() == CBaseChainParams::REGTEST) { + if (mapPrevOut.count(txin.prevout) == 0 && (coins == NULL || !coins->IsAvailable(txin.prevout.n))) + { + TxInErrorToJSON(txin, vErrors, "Input not found"); + continue; + } + } else { + if (coins == NULL || !coins->IsAvailable(txin.prevout.n)) { + TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); + continue; + } + } + const CScript& prevPubKey = (Params().NetworkID() == CBaseChainParams::REGTEST && mapPrevOut.count(txin.prevout) != 0 ? mapPrevOut[txin.prevout] : coins->vout[txin.prevout.n].scriptPubKey); + + txin.scriptSig.clear(); + // Only sign SIGHASH_SINGLE if there's a corresponding output: + if (!fHashSingle || (i < mergedTx.vout.size())) + SignSignature(keystore, prevPubKey, mergedTx, i, nHashType); + + // ... and merge in other signatures: + BOOST_FOREACH (const CMutableTransaction& txv, txVariants) { + txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); + } + ScriptError serror = SCRIPT_ERR_OK; + if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i), &serror)) { + TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); + } + } + bool fComplete = vErrors.empty(); + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("hex", EncodeHexTx(mergedTx))); + result.push_back(Pair("complete", fComplete)); + if (!vErrors.empty()) { + result.push_back(Pair("errors", vErrors)); + } + + return result; +} + +UniValue sendrawtransaction(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 3) + throw runtime_error( + "sendrawtransaction \"hexstring\" ( allowhighfees )\n" + "\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n" + "\nAlso see createrawtransaction and signrawtransaction calls.\n" + + "\nArguments:\n" + "1. \"hexstring\" (string, required) The hex string of the raw transaction)\n" + "2. allowhighfees (boolean, optional, default=false) Allow high fees\n" + "3. swiftx (boolean, optional, default=false) Use SwiftX to send this transaction\n" + + "\nResult:\n" + "\"hex\" (string) The transaction hash in hex\n" + + "\nExamples:\n" + "\nCreate a transaction\n" + + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") + + "Sign the transaction, and get back the hex\n" + HelpExampleCli("signrawtransaction", "\"myhex\"") + + "\nSend the transaction (signed hex)\n" + HelpExampleCli("sendrawtransaction", "\"signedhex\"") + + "\nAs a json rpc call\n" + HelpExampleRpc("sendrawtransaction", "\"signedhex\"")); + + LOCK(cs_main); + RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL)); + + // parse hex string from parameter + CTransaction tx; + if (!DecodeHexTx(tx, params[0].get_str())) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + uint256 hashTx = tx.GetHash(); + + bool fOverrideFees = false; + if (params.size() > 1) + fOverrideFees = params[1].get_bool(); + + bool fSwiftX = false; + if (params.size() > 2) + fSwiftX = params[2].get_bool(); + + CCoinsViewCache& view = *pcoinsTip; + const CCoins* existingCoins = view.AccessCoins(hashTx); + bool fHaveMempool = mempool.exists(hashTx); + bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000; + if (!fHaveMempool && !fHaveChain) { + // push to local node and sync with wallets + if (fSwiftX) { + mapTxLockReq.insert(make_pair(tx.GetHash(), tx)); + CreateNewLock(tx); + RelayTransactionLockReq(tx, true); + } + CValidationState state; + bool fMissingInputs; + if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, !fOverrideFees)) { + if (state.IsInvalid()) { + throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); + } else { + if (fMissingInputs) { + throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs"); + } + throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason()); + } + } + } else if (fHaveChain) { + throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain"); + } + RelayTransaction(tx); + + return hashTx.GetHex(); +} diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp new file mode 100644 index 0000000..16a00f0 --- /dev/null +++ b/src/rpc/server.cpp @@ -0,0 +1,1075 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Copyright (c) 2014-2015 The Dash developers +// Copyright (c) 2015-2017 The PIVX developers +// Copyright (c) 2017-2019 The XDNA Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "rpc/server.h" + +#include "base58.h" +#include "init.h" +#include "main.h" +#include "ui_interface.h" +#include "util.h" +#ifdef ENABLE_WALLET +#include "wallet/wallet.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace boost; +using namespace boost::asio; +using namespace std; + +static std::string strRPCUserColonPass; + +static bool fRPCRunning = false; +static bool fRPCInWarmup = true; +static std::string rpcWarmupStatus("RPC server started"); +static CCriticalSection cs_rpcWarmup; + +//! These are created by StartRPCThreads, destroyed in StopRPCThreads +static asio::io_service* rpc_io_service = NULL; +static map > deadlineTimers; +static ssl::context* rpc_ssl_context = NULL; +static boost::thread_group* rpc_worker_group = NULL; +static boost::asio::io_service::work* rpc_dummy_work = NULL; +static std::vector rpc_allow_subnets; //!< List of subnets to allow RPC connections from +static std::vector > rpc_acceptors; + +void RPCTypeCheck(const UniValue& params, + const list& typesExpected, + bool fAllowNull) +{ + unsigned int i = 0; + BOOST_FOREACH(UniValue::VType t, typesExpected) { + if (params.size() <= i) + break; + + const UniValue& v = params[i]; + if (!((v.type() == t) || (fAllowNull && (v.isNull())))) { + string err = strprintf("Expected type %s, got %s", + uvTypeName(t), uvTypeName(v.type())); + throw JSONRPCError(RPC_TYPE_ERROR, err); + } + i++; + } +} + +void RPCTypeCheckObj(const UniValue& o, + const map& typesExpected, + bool fAllowNull) +{ + BOOST_FOREACH(const PAIRTYPE(string, UniValue::VType)& t, typesExpected) { + const UniValue& v = find_value(o, t.first); + if (!fAllowNull && v.isNull()) + throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first)); + + if (!((v.type() == t.second) || (fAllowNull && (v.isNull())))) { + string err = strprintf("Expected type %s for %s, got %s", + uvTypeName(t.second), t.first, uvTypeName(v.type())); + throw JSONRPCError(RPC_TYPE_ERROR, err); + } + } +} + +static inline int64_t roundint64(double d) +{ + return (int64_t)(d > 0 ? d + 0.5 : d - 0.5); +} + +CAmount AmountFromValue(const UniValue& value) +{ + double dAmount = value.get_real(); + if (dAmount <= 0.0 || dAmount > 21000000.0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); + CAmount nAmount = roundint64(dAmount * COIN); + if (!MoneyRange(nAmount)) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); + return nAmount; +} + +UniValue ValueFromAmount(const CAmount& amount) +{ + return (double)amount / (double)COIN; +} + +uint256 ParseHashV(const UniValue& v, string strName) +{ + string strHex; + if (v.isStr()) + strHex = v.get_str(); + if (!IsHex(strHex)) // Note: IsHex("") is false + throw JSONRPCError(RPC_INVALID_PARAMETER, strName + " must be hexadecimal string (not '" + strHex + "')"); + uint256 result; + result.SetHex(strHex); + return result; +} +uint256 ParseHashO(const UniValue& o, string strKey) +{ + return ParseHashV(find_value(o, strKey), strKey); +} +vector ParseHexV(const UniValue& v, string strName) +{ + string strHex; + if (v.isStr()) + strHex = v.get_str(); + if (!IsHex(strHex)) + throw JSONRPCError(RPC_INVALID_PARAMETER, strName + " must be hexadecimal string (not '" + strHex + "')"); + return ParseHex(strHex); +} +vector ParseHexO(const UniValue& o, string strKey) +{ + return ParseHexV(find_value(o, strKey), strKey); +} + +int ParseInt(const UniValue& o, string strKey) +{ + const UniValue& v = find_value(o, strKey); + if (v.isNum()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, " + strKey + "is not an int"); + + return v.get_int(); +} + +bool ParseBool(const UniValue& o, string strKey) +{ + const UniValue& v = find_value(o, strKey); + if (v.isBool()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, " + strKey + "is not a bool"); + + return v.get_bool(); +} + + +/** + * Note: This interface may still be subject to change. + */ + +string CRPCTable::help(string strCommand) const +{ + string strRet; + string category; + set setDone; + vector > vCommands; + + for (map::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi) + vCommands.push_back(make_pair(mi->second->category + mi->first, mi->second)); + sort(vCommands.begin(), vCommands.end()); + + BOOST_FOREACH (const PAIRTYPE(string, const CRPCCommand*) & command, vCommands) { + const CRPCCommand* pcmd = command.second; + string strMethod = pcmd->name; + // We already filter duplicates, but these deprecated screw up the sort order + if (strMethod.find("label") != string::npos) + continue; + if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand) + continue; +#ifdef ENABLE_WALLET + if (pcmd->reqWallet && !pwalletMain) + continue; +#endif + + try { + UniValue params; + rpcfn_type pfn = pcmd->actor; + if (setDone.insert(pfn).second) + (*pfn)(params, true); + } catch (std::exception& e) { + // Help text is returned in an exception + string strHelp = string(e.what()); + if (strCommand == "") { + if (strHelp.find('\n') != string::npos) + strHelp = strHelp.substr(0, strHelp.find('\n')); + + if (category != pcmd->category) { + if (!category.empty()) + strRet += "\n"; + category = pcmd->category; + string firstLetter = category.substr(0, 1); + boost::to_upper(firstLetter); + strRet += "== " + firstLetter + category.substr(1) + " ==\n"; + } + } + strRet += strHelp + "\n"; + } + } + if (strRet == "") + strRet = strprintf("help: unknown command: %s\n", strCommand); + strRet = strRet.substr(0, strRet.size() - 1); + return strRet; +} + +UniValue help(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "help ( \"command\" )\n" + "\nList all commands, or get help for a specified command.\n" + "\nArguments:\n" + "1. \"command\" (string, optional) The command to get help on\n" + "\nResult:\n" + "\"text\" (string) The help text\n"); + + string strCommand; + if (params.size() > 0) + strCommand = params[0].get_str(); + + return tableRPC.help(strCommand); +} + + +UniValue stop(const UniValue& params, bool fHelp) +{ + // Accept the deprecated and ignored 'detach' boolean argument + if (fHelp || params.size() > 1) + throw runtime_error( + "stop\n" + "\nStop XDNA server."); + // Shutdown will take long enough that the response should get back + StartShutdown(); + return "XDNA server stopping"; +} + + +/** + * Call Table + */ +static const CRPCCommand vRPCCommands[] = + { + // category name actor (function) okSafeMode threadSafe reqWallet + // --------------------- ------------------------ ----------------------- ---------- ---------- --------- + /* Overall control/query calls */ + {"control", "getinfo", &getinfo, true, false, false}, /* uses wallet if enabled */ + {"control", "help", &help, true, true, false}, + {"control", "stop", &stop, true, true, false}, + + /* P2P networking */ + {"network", "getnetworkinfo", &getnetworkinfo, true, false, false}, + {"network", "addnode", &addnode, true, true, false}, + {"network", "getaddednodeinfo", &getaddednodeinfo, true, true, false}, + {"network", "getconnectioncount", &getconnectioncount, true, false, false}, + {"network", "getnettotals", &getnettotals, true, true, false}, + {"network", "getpeerinfo", &getpeerinfo, true, false, false}, + {"network", "ping", &ping, true, false, false}, + {"network", "setban", &setban, true, false, false}, + {"network", "listbanned", &listbanned, true, false, false}, + {"network", "clearbanned", &clearbanned, true, false, false}, + + /* Block chain and UTXO */ + {"blockchain", "getblockchaininfo", &getblockchaininfo, true, false, false}, + {"blockchain", "getbestblockhash", &getbestblockhash, true, false, false}, + {"blockchain", "getblockcount", &getblockcount, true, false, false}, + {"blockchain", "getblock", &getblock, true, false, false}, + {"blockchain", "getblockhash", &getblockhash, true, false, false}, + {"blockchain", "getblockheader", &getblockheader, false, false, false}, + {"blockchain", "getchaintips", &getchaintips, true, false, false}, + {"blockchain", "getdifficulty", &getdifficulty, true, false, false}, + {"blockchain", "getmempoolinfo", &getmempoolinfo, true, true, false}, + {"blockchain", "getrawmempool", &getrawmempool, true, false, false}, + {"blockchain", "gettxout", &gettxout, true, false, false}, + {"blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true, false, false}, + {"blockchain", "verifychain", &verifychain, true, false, false}, + {"blockchain", "invalidateblock", &invalidateblock, true, true, false}, + {"blockchain", "reconsiderblock", &reconsiderblock, true, true, false}, + + /* Mining */ + {"mining", "getblocktemplate", &getblocktemplate, true, false, false}, + {"mining", "getmininginfo", &getmininginfo, true, false, false}, + {"mining", "getnetworkhashps", &getnetworkhashps, true, false, false}, + {"mining", "prioritisetransaction", &prioritisetransaction, true, false, false}, + {"mining", "submitblock", &submitblock, true, true, false}, + {"mining", "reservebalance", &reservebalance, true, true, false}, + +#ifdef ENABLE_WALLET + /* Coin generation */ + {"generating", "getgenerate", &getgenerate, true, false, false}, + {"generating", "gethashespersec", &gethashespersec, true, false, false}, + {"generating", "setgenerate", &setgenerate, true, true, false}, +#endif + + /* Raw transactions */ + {"rawtransactions", "createrawtransaction", &createrawtransaction, true, false, false}, + {"rawtransactions", "decoderawtransaction", &decoderawtransaction, true, false, false}, + {"rawtransactions", "decodescript", &decodescript, true, false, false}, + {"rawtransactions", "getrawtransaction", &getrawtransaction, true, false, false}, + {"rawtransactions", "sendrawtransaction", &sendrawtransaction, false, false, false}, + {"rawtransactions", "signrawtransaction", &signrawtransaction, false, false, false}, /* uses wallet if enabled */ + + /* Utility functions */ + {"util", "createmultisig", &createmultisig, true, true, false}, + {"util", "validateaddress", &validateaddress, true, false, false}, /* uses wallet if enabled */ + {"util", "verifymessage", &verifymessage, true, false, false}, + {"util", "estimatefee", &estimatefee, true, true, false}, + {"util", "estimatepriority", &estimatepriority, true, true, false}, + + /* Not shown in help */ + {"hidden", "invalidateblock", &invalidateblock, true, true, false}, + {"hidden", "reconsiderblock", &reconsiderblock, true, true, false}, + {"hidden", "setmocktime", &setmocktime, true, false, false}, + + /* XDNA features */ + {"xdna", "listmasternodes", &listmasternodes, true, true, false}, + {"xdna", "getmasternodecount", &getmasternodecount, true, true, false}, + {"xdna", "masternodeconnect", &masternodeconnect, true, true, false}, + {"xdna", "masternodecurrent", &masternodecurrent, true, true, false}, + {"xdna", "masternodedebug", &masternodedebug, true, true, false}, + {"xdna", "startmasternode", &startmasternode, true, true, false}, + {"xdna", "createmasternodekey", &createmasternodekey, true, true, false}, + {"xdna", "getmasternodeoutputs", &getmasternodeoutputs, true, true, false}, + {"xdna", "listmasternodeconf", &listmasternodeconf, true, true, false}, + {"xdna", "getmasternodestatus", &getmasternodestatus, true, true, false}, + {"xdna", "getmasternodewinners", &getmasternodewinners, true, true, false}, + {"xdna", "getmasternodescores", &getmasternodescores, true, true, false}, + {"xdna", "mnsync", &mnsync, true, true, false}, + {"xdna", "spork", &spork, true, true, false}, + {"xdna", "getpoolinfo", &getpoolinfo, true, true, false}, +#ifdef ENABLE_WALLET + + /* Wallet */ + {"wallet", "addmultisigaddress", &addmultisigaddress, true, false, true}, + {"wallet", "autocombinerewards", &autocombinerewards, false, false, true}, + {"wallet", "backupwallet", &backupwallet, true, false, true}, + {"wallet", "dumpprivkey", &dumpprivkey, true, false, true}, + {"wallet", "dumpwallet", &dumpwallet, true, false, true}, + {"wallet", "bip38encrypt", &bip38encrypt, true, false, true}, + {"wallet", "bip38decrypt", &bip38decrypt, true, false, true}, + {"wallet", "encryptwallet", &encryptwallet, true, false, true}, + {"wallet", "getaccountaddress", &getaccountaddress, true, false, true}, + {"wallet", "getaccount", &getaccount, true, false, true}, + {"wallet", "getaddressesbyaccount", &getaddressesbyaccount, true, false, true}, + {"wallet", "getbalance", &getbalance, false, false, true}, + {"wallet", "getnewaddress", &getnewaddress, true, false, true}, + {"wallet", "getrawchangeaddress", &getrawchangeaddress, true, false, true}, + {"wallet", "getreceivedbyaccount", &getreceivedbyaccount, false, false, true}, + {"wallet", "getreceivedbyaddress", &getreceivedbyaddress, false, false, true}, + {"wallet", "getstakingstatus", &getstakingstatus, false, false, true}, + {"wallet", "getstakesplitthreshold", &getstakesplitthreshold, false, false, true}, + {"wallet", "gettransaction", &gettransaction, false, false, true}, + {"wallet", "getunconfirmedbalance", &getunconfirmedbalance, false, false, true}, + {"wallet", "getwalletinfo", &getwalletinfo, false, false, true}, + {"wallet", "importprivkey", &importprivkey, true, false, true}, + {"wallet", "importwallet", &importwallet, true, false, true}, + {"wallet", "importaddress", &importaddress, true, false, true}, + {"wallet", "keypoolrefill", &keypoolrefill, true, false, true}, + {"wallet", "listaccounts", &listaccounts, false, false, true}, + {"wallet", "listaddressgroupings", &listaddressgroupings, false, false, true}, + {"wallet", "listlockunspent", &listlockunspent, false, false, true}, + {"wallet", "listreceivedbyaccount", &listreceivedbyaccount, false, false, true}, + {"wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, false, true}, + {"wallet", "listsinceblock", &listsinceblock, false, false, true}, + {"wallet", "listtransactions", &listtransactions, false, false, true}, + {"wallet", "listunspent", &listunspent, false, false, true}, + {"wallet", "lockunspent", &lockunspent, true, false, true}, + {"wallet", "move", &movecmd, false, false, true}, + {"wallet", "multisend", &multisend, false, false, true}, + {"wallet", "sendfrom", &sendfrom, false, false, true}, + {"wallet", "sendmany", &sendmany, false, false, true}, + {"wallet", "sendtoaddress", &sendtoaddress, false, false, true}, + {"wallet", "sendtoaddressix", &sendtoaddressix, false, false, true}, + {"wallet", "setaccount", &setaccount, true, false, true}, + {"wallet", "setstakesplitthreshold", &setstakesplitthreshold, false, false, true}, + {"wallet", "settxfee", &settxfee, true, false, true}, + {"wallet", "signmessage", &signmessage, true, false, true}, + {"wallet", "walletlock", &walletlock, true, false, true}, + {"wallet", "walletpassphrasechange", &walletpassphrasechange, true, false, true}, + {"wallet", "walletpassphrase", &walletpassphrase, true, false, true}, +#endif // ENABLE_WALLET +}; + +CRPCTable::CRPCTable() +{ + unsigned int vcidx; + for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++) { + const CRPCCommand* pcmd; + + pcmd = &vRPCCommands[vcidx]; + mapCommands[pcmd->name] = pcmd; + } +} + +const CRPCCommand* CRPCTable::operator[](string name) const +{ + map::const_iterator it = mapCommands.find(name); + if (it == mapCommands.end()) + return NULL; + return (*it).second; +} + + +bool HTTPAuthorized(map& mapHeaders) +{ + string strAuth = mapHeaders["authorization"]; + if (strAuth.substr(0, 6) != "Basic ") + return false; + string strUserPass64 = strAuth.substr(6); + boost::trim(strUserPass64); + string strUserPass = DecodeBase64(strUserPass64); + return TimingResistantEqual(strUserPass, strRPCUserColonPass); +} + +void ErrorReply(std::ostream& stream, const UniValue& objError, const UniValue& id) +{ + // Send error reply from json-rpc error object + int nStatus = HTTP_INTERNAL_SERVER_ERROR; + int code = find_value(objError, "code").get_int(); + if (code == RPC_INVALID_REQUEST) + nStatus = HTTP_BAD_REQUEST; + else if (code == RPC_METHOD_NOT_FOUND) + nStatus = HTTP_NOT_FOUND; + string strReply = JSONRPCReply(NullUniValue, objError, id); + stream << HTTPReply(nStatus, strReply, false) << std::flush; +} + +CNetAddr BoostAsioToCNetAddr(boost::asio::ip::address address) +{ + CNetAddr netaddr; + // Make sure that IPv4-compatible and IPv4-mapped IPv6 addresses are treated as IPv4 addresses + if (address.is_v6() && (address.to_v6().is_v4_compatible() || address.to_v6().is_v4_mapped())) + address = address.to_v6().to_v4(); + + if (address.is_v4()) { + boost::asio::ip::address_v4::bytes_type bytes = address.to_v4().to_bytes(); + netaddr.SetRaw(NET_IPV4, &bytes[0]); + } else { + boost::asio::ip::address_v6::bytes_type bytes = address.to_v6().to_bytes(); + netaddr.SetRaw(NET_IPV6, &bytes[0]); + } + return netaddr; +} + +bool ClientAllowed(const boost::asio::ip::address& address) +{ + CNetAddr netaddr = BoostAsioToCNetAddr(address); + BOOST_FOREACH (const CSubNet& subnet, rpc_allow_subnets) + if (subnet.Match(netaddr)) + return true; + return false; +} + +template +class AcceptedConnectionImpl : public AcceptedConnection +{ +public: + AcceptedConnectionImpl( + asio::io_service& io_service, + ssl::context& context, + bool fUseSSL) : sslStream(io_service, context), + _d(sslStream, fUseSSL), + _stream(_d) + { + } + + virtual std::iostream& stream() + { + return _stream; + } + + virtual std::string peer_address_to_string() const + { + return peer.address().to_string(); + } + + virtual void close() + { + _stream.close(); + } + + typename Protocol::endpoint peer; + asio::ssl::stream sslStream; + +private: + SSLIOStreamDevice _d; + iostreams::stream > _stream; +}; + +void ServiceConnection(AcceptedConnection* conn); + +//! Forward declaration required for RPCListen +template +static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor > acceptor, + ssl::context& context, + bool fUseSSL, + boost::shared_ptr conn, + const boost::system::error_code& error); + +/** + * Sets up I/O resources to accept and handle a new connection. + */ +template +static void RPCListen(boost::shared_ptr< basic_socket_acceptor > acceptor, + ssl::context& context, + const bool fUseSSL) +{ + // Accept connection + boost::shared_ptr > conn(new AcceptedConnectionImpl(acceptor->get_io_service(), context, fUseSSL)); + + acceptor->async_accept( + conn->sslStream.lowest_layer(), + conn->peer, + boost::bind(&RPCAcceptHandler, + acceptor, + boost::ref(context), + fUseSSL, + conn, + _1)); +} + + +/** + * Accept and handle incoming connection. + */ +template +static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor > acceptor, + ssl::context& context, + const bool fUseSSL, + boost::shared_ptr conn, + const boost::system::error_code& error) +{ + // Immediately start accepting new connections, except when we're cancelled or our socket is closed. + if (error != asio::error::operation_aborted && acceptor->is_open()) + RPCListen(acceptor, context, fUseSSL); + + AcceptedConnectionImpl* tcp_conn = dynamic_cast*>(conn.get()); + + if (error) { + // TODO: Actually handle errors + LogPrintf("%s: Error: %s\n", __func__, error.message()); + } + // Restrict callers by IP. It is important to + // do this before starting client thread, to filter out + // certain DoS and misbehaving clients. + else if (tcp_conn && !ClientAllowed(tcp_conn->peer.address())) { + // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake. + if (!fUseSSL) + conn->stream() << HTTPError(HTTP_FORBIDDEN, false) << std::flush; + conn->close(); + } else { + ServiceConnection(conn.get()); + conn->close(); + } +} + +static ip::tcp::endpoint ParseEndpoint(const std::string& strEndpoint, int defaultPort) +{ + std::string addr; + int port = defaultPort; + SplitHostPort(strEndpoint, port, addr); + return ip::tcp::endpoint(asio::ip::address::from_string(addr), port); +} + +void StartRPCThreads() +{ + rpc_allow_subnets.clear(); + rpc_allow_subnets.push_back(CSubNet("127.0.0.0/8")); // always allow IPv4 local subnet + rpc_allow_subnets.push_back(CSubNet("::1")); // always allow IPv6 localhost + if (mapMultiArgs.count("-rpcallowip")) { + const vector& vAllow = mapMultiArgs["-rpcallowip"]; + BOOST_FOREACH (string strAllow, vAllow) { + CSubNet subnet(strAllow); + if (!subnet.IsValid()) { + uiInterface.ThreadSafeMessageBox( + strprintf("Invalid -rpcallowip subnet specification: %s. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24).", strAllow), + "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return; + } + rpc_allow_subnets.push_back(subnet); + } + } + std::string strAllowed; + BOOST_FOREACH (const CSubNet& subnet, rpc_allow_subnets) + strAllowed += subnet.ToString() + " "; + LogPrint("rpc", "Allowing RPC connections from: %s\n", strAllowed); + + strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; + if (((mapArgs["-rpcpassword"] == "") || + (mapArgs["-rpcuser"] == mapArgs["-rpcpassword"])) && + Params().RequireRPCPassword()) { + unsigned char rand_pwd[32]; + GetRandBytes(rand_pwd, 32); + uiInterface.ThreadSafeMessageBox(strprintf( + _("To use xdnad, or the -server option to xdna-qt, you must set an rpcpassword in the configuration file:\n" + "%s\n" + "It is recommended you use the following random password:\n" + "rpcuser=xdnarpc\n" + "rpcpassword=%s\n" + "(you do not need to remember this password)\n" + "The username and password MUST NOT be the same.\n" + "If the file does not exist, create it with owner-readable-only file permissions.\n" + "It is also recommended to set alertnotify so you are notified of problems;\n" + "for example: alertnotify=echo %%s | mail -s \"XDNA Alert\" admin@foo.com\n"), + GetConfigFile().string(), + EncodeBase58(&rand_pwd[0], &rand_pwd[0] + 32)), + "", CClientUIInterface::MSG_ERROR | CClientUIInterface::SECURE); + StartShutdown(); + return; + } + + assert(rpc_io_service == NULL); + rpc_io_service = new asio::io_service(); + rpc_ssl_context = new ssl::context(ssl::context::sslv23); + + const bool fUseSSL = GetBoolArg("-rpcssl", false); + + if (fUseSSL) { + rpc_ssl_context->set_options(ssl::context::no_sslv2 | ssl::context::no_sslv3); + + filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert")); + if (!pathCertFile.is_complete()) pathCertFile = filesystem::path(GetDataDir()) / pathCertFile; + if (filesystem::exists(pathCertFile)) + rpc_ssl_context->use_certificate_chain_file(pathCertFile.string()); + else + LogPrintf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string()); + + filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem")); + if (!pathPKFile.is_complete()) pathPKFile = filesystem::path(GetDataDir()) / pathPKFile; + if (filesystem::exists(pathPKFile)) + rpc_ssl_context->use_private_key_file(pathPKFile.string(), ssl::context::pem); + else + LogPrintf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string()); + + string strCiphers = GetArg("-rpcsslciphers", "TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH"); + SSL_CTX_set_cipher_list(rpc_ssl_context->native_handle(), strCiphers.c_str()); + } + + std::vector vEndpoints; + bool bBindAny = false; + int defaultPort = GetArg("-rpcport", BaseParams().RPCPort()); + if (!mapArgs.count("-rpcallowip")) // Default to loopback if not allowing external IPs + { + vEndpoints.push_back(ip::tcp::endpoint(asio::ip::address_v6::loopback(), defaultPort)); + vEndpoints.push_back(ip::tcp::endpoint(asio::ip::address_v4::loopback(), defaultPort)); + if (mapArgs.count("-rpcbind")) { + LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n"); + } + } else if (mapArgs.count("-rpcbind")) // Specific bind address + { + BOOST_FOREACH (const std::string& addr, mapMultiArgs["-rpcbind"]) { + try { + vEndpoints.push_back(ParseEndpoint(addr, defaultPort)); + } catch (const boost::system::system_error&) { + uiInterface.ThreadSafeMessageBox( + strprintf(_("Could not parse -rpcbind value %s as network address"), addr), + "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return; + } + } + } else { // No specific bind address specified, bind to any + vEndpoints.push_back(ip::tcp::endpoint(asio::ip::address_v6::any(), defaultPort)); + vEndpoints.push_back(ip::tcp::endpoint(asio::ip::address_v4::any(), defaultPort)); + // Prefer making the socket dual IPv6/IPv4 instead of binding + // to both addresses seperately. + bBindAny = true; + } + + bool fListening = false; + std::string strerr; + std::string straddress; + BOOST_FOREACH (const ip::tcp::endpoint& endpoint, vEndpoints) { + try { + asio::ip::address bindAddress = endpoint.address(); + straddress = bindAddress.to_string(); + LogPrintf("Binding RPC on address %s port %i (IPv4+IPv6 bind any: %i)\n", straddress, endpoint.port(), bBindAny); + boost::system::error_code v6_only_error; + boost::shared_ptr acceptor(new ip::tcp::acceptor(*rpc_io_service)); + + acceptor->open(endpoint.protocol()); + acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + + // Try making the socket dual IPv6/IPv4 when listening on the IPv6 "any" address + acceptor->set_option(boost::asio::ip::v6_only( + !bBindAny || bindAddress != asio::ip::address_v6::any()), + v6_only_error); + + acceptor->bind(endpoint); + acceptor->listen(socket_base::max_connections); + + RPCListen(acceptor, *rpc_ssl_context, fUseSSL); + + rpc_acceptors.push_back(acceptor); + fListening = true; + rpc_acceptors.push_back(acceptor); + // If dual IPv6/IPv4 bind successful, skip binding to IPv4 separately + if (bBindAny && bindAddress == asio::ip::address_v6::any() && !v6_only_error) + break; + } catch (boost::system::system_error& e) { + LogPrintf("ERROR: Binding RPC on address %s port %i failed: %s\n", straddress, endpoint.port(), e.what()); + strerr = strprintf(_("An error occurred while setting up the RPC address %s port %u for listening: %s"), straddress, endpoint.port(), e.what()); + } + } + + if (!fListening) { + uiInterface.ThreadSafeMessageBox(strerr, "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return; + } + + rpc_worker_group = new boost::thread_group(); + for (int i = 0; i < GetArg("-rpcthreads", 4); i++) + rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service)); + fRPCRunning = true; +} + +void StartDummyRPCThread() +{ + if (rpc_io_service == NULL) { + rpc_io_service = new asio::io_service(); + /* Create dummy "work" to keep the thread from exiting when no timeouts active, + * see http://www.boost.org/doc/libs/1_51_0/doc/html/boost_asio/reference/io_service.html#boost_asio.reference.io_service.stopping_the_io_service_from_running_out_of_work */ + rpc_dummy_work = new asio::io_service::work(*rpc_io_service); + rpc_worker_group = new boost::thread_group(); + rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service)); + fRPCRunning = true; + } +} + +void StopRPCThreads() +{ + if (rpc_io_service == NULL) return; + // Set this to false first, so that longpolling loops will exit when woken up + fRPCRunning = false; + + // First, cancel all timers and acceptors + // This is not done automatically by ->stop(), and in some cases the destructor of + // asio::io_service can hang if this is skipped. + boost::system::error_code ec; + BOOST_FOREACH (const boost::shared_ptr& acceptor, rpc_acceptors) { + acceptor->cancel(ec); + if (ec) + LogPrintf("%s: Warning: %s when cancelling acceptor", __func__, ec.message()); + } + rpc_acceptors.clear(); + BOOST_FOREACH (const PAIRTYPE(std::string, boost::shared_ptr) & timer, deadlineTimers) { + timer.second->cancel(ec); + if (ec) + LogPrintf("%s: Warning: %s when cancelling timer", __func__, ec.message()); + } + deadlineTimers.clear(); + + rpc_io_service->stop(); + cvBlockChange.notify_all(); + if (rpc_worker_group != NULL) + rpc_worker_group->join_all(); + delete rpc_dummy_work; + rpc_dummy_work = NULL; + delete rpc_worker_group; + rpc_worker_group = NULL; + delete rpc_ssl_context; + rpc_ssl_context = NULL; + delete rpc_io_service; + rpc_io_service = NULL; +} + +bool IsRPCRunning() +{ + return fRPCRunning; +} + +void SetRPCWarmupStatus(const std::string& newStatus) +{ + LOCK(cs_rpcWarmup); + rpcWarmupStatus = newStatus; +} + +void SetRPCWarmupFinished() +{ + LOCK(cs_rpcWarmup); + assert(fRPCInWarmup); + fRPCInWarmup = false; +} + +bool RPCIsInWarmup(std::string* outStatus) +{ + LOCK(cs_rpcWarmup); + if (outStatus) + *outStatus = rpcWarmupStatus; + return fRPCInWarmup; +} + +void RPCRunHandler(const boost::system::error_code& err, boost::function func) +{ + if (!err) + func(); +} + +void RPCRunLater(const std::string& name, boost::function func, int64_t nSeconds) +{ + assert(rpc_io_service != NULL); + + if (deadlineTimers.count(name) == 0) { + deadlineTimers.insert(make_pair(name, + boost::shared_ptr(new deadline_timer(*rpc_io_service)))); + } + deadlineTimers[name]->expires_from_now(posix_time::seconds(nSeconds)); + deadlineTimers[name]->async_wait(boost::bind(RPCRunHandler, _1, func)); +} + +class JSONRequest +{ +public: + UniValue id; + string strMethod; + UniValue params; + + JSONRequest() { id = NullUniValue; } + void parse(const UniValue& valRequest); +}; + +void JSONRequest::parse(const UniValue& valRequest) +{ + // Parse request + if (!valRequest.isObject()) + throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object"); + const UniValue& request = valRequest.get_obj(); + + // Parse id now so errors from here on will have the id + id = find_value(request, "id"); + + // Parse method + UniValue valMethod = find_value(request, "method"); + if (valMethod.isNull()) + throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method"); + if (!valMethod.isStr()) + throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string"); + strMethod = valMethod.get_str(); + if (strMethod != "getblocktemplate") + LogPrint("rpc", "ThreadRPCServer method=%s\n", SanitizeString(strMethod)); + + // Parse params + UniValue valParams = find_value(request, "params"); + if (valParams.isArray()) + params = valParams.get_array(); + else if (valParams.isNull()) + params = UniValue(UniValue::VARR); + else + throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array"); +} + + +static UniValue JSONRPCExecOne(const UniValue& req) +{ + UniValue rpc_result(UniValue::VOBJ); + + JSONRequest jreq; + try { + jreq.parse(req); + + UniValue result = tableRPC.execute(jreq.strMethod, jreq.params); + rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id); + } catch (const UniValue& objError) { + rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id); + } catch (std::exception& e) { + rpc_result = JSONRPCReplyObj(NullUniValue, + JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); + } + + return rpc_result; +} + +static string JSONRPCExecBatch(const UniValue& vReq) +{ + UniValue ret(UniValue::VARR); + for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++) + ret.push_back(JSONRPCExecOne(vReq[reqIdx])); + + return ret.write() + "\n"; +} + +static bool HTTPReq_JSONRPC(AcceptedConnection* conn, + string& strRequest, + map& mapHeaders, + bool fRun) +{ + // Check authorization + if (mapHeaders.count("authorization") == 0) { + conn->stream() << HTTPError(HTTP_UNAUTHORIZED, false) << std::flush; + return false; + } + + if (!HTTPAuthorized(mapHeaders)) { + LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string()); + /* Deter brute-forcing + If this results in a DoS the user really + shouldn't have their RPC port exposed. */ + MilliSleep(250); + + conn->stream() << HTTPError(HTTP_UNAUTHORIZED, false) << std::flush; + return false; + } + + JSONRequest jreq; + try { + // Parse request + UniValue valRequest; + if (!valRequest.read(strRequest)) + throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); + + // Return immediately if in warmup + { + LOCK(cs_rpcWarmup); + if (fRPCInWarmup) + throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus); + } + + string strReply; + + // singleton request + if (valRequest.isObject()) { + jreq.parse(valRequest); + + UniValue result = tableRPC.execute(jreq.strMethod, jreq.params); + + // Send reply + strReply = JSONRPCReply(result, NullUniValue, jreq.id); + + // array of requests + } else if (valRequest.isArray()) + strReply = JSONRPCExecBatch(valRequest.get_array()); + else + throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error"); + + conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, strReply.size()) << strReply << std::flush; + } catch (const UniValue& objError) { + ErrorReply(conn->stream(), objError, jreq.id); + return false; + } catch (std::exception& e) { + ErrorReply(conn->stream(), JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); + return false; + } + return true; +} + +void ServiceConnection(AcceptedConnection* conn) +{ + bool fRun = true; + while (fRun && !ShutdownRequested()) { + int nProto = 0; + map mapHeaders; + string strRequest, strMethod, strURI; + + // Read HTTP request line + if (!ReadHTTPRequestLine(conn->stream(), nProto, strMethod, strURI)) + break; + + // Read HTTP message headers and body + ReadHTTPMessage(conn->stream(), mapHeaders, strRequest, nProto, MAX_SIZE); + + // HTTP Keep-Alive is false; close connection immediately + if ((mapHeaders["connection"] == "close") || (!GetBoolArg("-rpckeepalive", true))) + fRun = false; + + // Process via JSON-RPC API + if (strURI == "/") { + if (!HTTPReq_JSONRPC(conn, strRequest, mapHeaders, fRun)) + break; + + // Process via HTTP REST API + } else if (strURI.substr(0, 6) == "/rest/" && GetBoolArg("-rest", false)) { + if (!HTTPReq_REST(conn, strURI, mapHeaders, fRun)) + break; + + } else { + conn->stream() << HTTPError(HTTP_NOT_FOUND, false) << std::flush; + break; + } + } +} + +UniValue CRPCTable::execute(const std::string &strMethod, const UniValue ¶ms) const +{ + // Find method + const CRPCCommand* pcmd = tableRPC[strMethod]; + if (!pcmd) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); +#ifdef ENABLE_WALLET + if (pcmd->reqWallet && !pwalletMain) + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)"); +#endif + + // Observe safe mode + string strWarning = GetWarnings("rpc"); + if (strWarning != "" && !GetBoolArg("-disablesafemode", false) && + !pcmd->okSafeMode) + throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning); + + try { + // Execute + UniValue result; + { + if (pcmd->threadSafe) + result = pcmd->actor(params, false); +#ifdef ENABLE_WALLET + else if (!pwalletMain) { + LOCK(cs_main); + result = pcmd->actor(params, false); + } else { + while (true) { + TRY_LOCK(cs_main, lockMain); + if (!lockMain) { + MilliSleep(50); + continue; + } + while (true) { + TRY_LOCK(pwalletMain->cs_wallet, lockWallet); + if (!lockMain) { + MilliSleep(50); + continue; + } + result = pcmd->actor(params, false); + break; + } + break; + } + } +#else // ENABLE_WALLET + else { + LOCK(cs_main); + result = pcmd->actor(params, false); + } +#endif // !ENABLE_WALLET + } + return result; + } catch (std::exception& e) { + throw JSONRPCError(RPC_MISC_ERROR, e.what()); + } +} + +std::vector CRPCTable::listCommands() const +{ + std::vector commandList; + typedef std::map commandMap; + + std::transform( mapCommands.begin(), mapCommands.end(), + std::back_inserter(commandList), + boost::bind(&commandMap::value_type::first,_1) ); + return commandList; +} + +std::string HelpExampleCli(string methodname, string args) +{ + return "> xdna-cli " + methodname + " " + args + "\n"; +} + +std::string HelpExampleRpc(string methodname, string args) +{ + return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", " + "\"method\": \"" + + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:1944/\n"; +} + +const CRPCTable tableRPC; diff --git a/src/rpc/server.h b/src/rpc/server.h new file mode 100644 index 0000000..c054e6c --- /dev/null +++ b/src/rpc/server.h @@ -0,0 +1,282 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Copyright (c) 2015-2017 The PIVX developers +// Copyright (c) 2017-2019 The XDNA Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_RPCSERVER_H +#define BITCOIN_RPCSERVER_H + +#include "amount.h" +#include "rpc/protocol.h" +#include "uint256.h" + +#include +#include +#include +#include + +#include + +#include + + +class CBlockIndex; +class CNetAddr; + +class AcceptedConnection +{ +public: + virtual ~AcceptedConnection() {} + + virtual std::iostream& stream() = 0; + virtual std::string peer_address_to_string() const = 0; + virtual void close() = 0; +}; + +/** Start RPC threads */ +void StartRPCThreads(); +/** + * Alternative to StartRPCThreads for the GUI, when no server is + * used. The RPC thread in this case is only used to handle timeouts. + * If real RPC threads have already been started this is a no-op. + */ +void StartDummyRPCThread(); +/** Stop RPC threads */ +void StopRPCThreads(); +/** Query whether RPC is running */ +bool IsRPCRunning(); + +/** + * Set the RPC warmup status. When this is done, all RPC calls will error out + * immediately with RPC_IN_WARMUP. + */ +void SetRPCWarmupStatus(const std::string& newStatus); +/* Mark warmup as done. RPC calls will be processed from now on. */ +void SetRPCWarmupFinished(); + +/* returns the current warmup state. */ +bool RPCIsInWarmup(std::string* statusOut); + +/** + * Type-check arguments; throws JSONRPCError if wrong type given. Does not check that + * the right number of arguments are passed, just that any passed are the correct type. + * Use like: RPCTypeCheck(params, boost::assign::list_of(str_type)(int_type)(obj_type)); + */ +void RPCTypeCheck(const UniValue& params, + const std::list& typesExpected, bool fAllowNull=false); + +/** + * Check for expected keys/value types in an Object. + * Use like: RPCTypeCheckObj(object, boost::assign::map_list_of("name", str_type)("value", int_type)); + */ +void RPCTypeCheckObj(const UniValue& o, + const std::map& typesExpected, bool fAllowNull=false); + +/** + * Run func nSeconds from now. Uses boost deadline timers. + * Overrides previous timer (if any). + */ +void RPCRunLater(const std::string& name, boost::function func, int64_t nSeconds); + +//! Convert boost::asio address to CNetAddr +extern CNetAddr BoostAsioToCNetAddr(boost::asio::ip::address address); + +typedef UniValue(*rpcfn_type)(const UniValue& params, bool fHelp); + +class CRPCCommand +{ +public: + std::string category; + std::string name; + rpcfn_type actor; + bool okSafeMode; + bool threadSafe; + bool reqWallet; +}; + +/** + * XDNA RPC command dispatcher. + */ +class CRPCTable +{ +private: + std::map mapCommands; + +public: + CRPCTable(); + const CRPCCommand* operator[](std::string name) const; + std::string help(std::string name) const; + + /** + * Execute a method. + * @param method Method to execute + * @param params UniValue Array of arguments (JSON objects) + * @returns Result of the call. + * @throws an exception (UniValue) when an error happens. + */ + UniValue execute(const std::string &method, const UniValue ¶ms) const; + + /** + * Returns a list of registered commands + * @returns List of registered commands. + */ + std::vector listCommands() const; +}; + +extern const CRPCTable tableRPC; + +/** + * Utilities: convert hex-encoded Values + * (throws error if not hex). + */ +extern uint256 ParseHashV(const UniValue& v, std::string strName); +extern uint256 ParseHashO(const UniValue& o, std::string strKey); +extern std::vector ParseHexV(const UniValue& v, std::string strName); +extern std::vector ParseHexO(const UniValue& o, std::string strKey); +extern int ParseInt(const UniValue& o, std::string strKey); +extern bool ParseBool(const UniValue& o, std::string strKey); + +extern void InitRPCMining(); +extern void ShutdownRPCMining(); + +extern int64_t nWalletUnlockTime; +extern CAmount AmountFromValue(const UniValue& value); +extern UniValue ValueFromAmount(const CAmount& amount); +extern double GetDifficulty(const CBlockIndex* blockindex = NULL); +extern std::string HelpRequiringPassphrase(); +extern std::string HelpExampleCli(std::string methodname, std::string args); +extern std::string HelpExampleRpc(std::string methodname, std::string args); + +extern void EnsureWalletIsUnlocked(); + +extern UniValue getconnectioncount(const UniValue& params, bool fHelp); // in rpcnet.cpp +extern UniValue getpeerinfo(const UniValue& params, bool fHelp); +extern UniValue ping(const UniValue& params, bool fHelp); +extern UniValue addnode(const UniValue& params, bool fHelp); +extern UniValue getaddednodeinfo(const UniValue& params, bool fHelp); +extern UniValue getnettotals(const UniValue& params, bool fHelp); +extern UniValue setban(const UniValue& params, bool fHelp); +extern UniValue listbanned(const UniValue& params, bool fHelp); +extern UniValue clearbanned(const UniValue& params, bool fHelp); + +extern UniValue dumpprivkey(const UniValue& params, bool fHelp); // in rpcdump.cpp +extern UniValue importprivkey(const UniValue& params, bool fHelp); +extern UniValue importaddress(const UniValue& params, bool fHelp); +extern UniValue dumpwallet(const UniValue& params, bool fHelp); +extern UniValue importwallet(const UniValue& params, bool fHelp); +extern UniValue bip38encrypt(const UniValue& params, bool fHelp); +extern UniValue bip38decrypt(const UniValue& params, bool fHelp); + +extern UniValue getgenerate(const UniValue& params, bool fHelp); // in rpcmining.cpp +extern UniValue setgenerate(const UniValue& params, bool fHelp); +extern UniValue getnetworkhashps(const UniValue& params, bool fHelp); +extern UniValue gethashespersec(const UniValue& params, bool fHelp); +extern UniValue getmininginfo(const UniValue& params, bool fHelp); +extern UniValue prioritisetransaction(const UniValue& params, bool fHelp); +extern UniValue getblocktemplate(const UniValue& params, bool fHelp); +extern UniValue submitblock(const UniValue& params, bool fHelp); +extern UniValue estimatefee(const UniValue& params, bool fHelp); +extern UniValue estimatepriority(const UniValue& params, bool fHelp); + +extern UniValue getnewaddress(const UniValue& params, bool fHelp); // in rpcwallet.cpp +extern UniValue getaccountaddress(const UniValue& params, bool fHelp); +extern UniValue getrawchangeaddress(const UniValue& params, bool fHelp); +extern UniValue setaccount(const UniValue& params, bool fHelp); +extern UniValue getaccount(const UniValue& params, bool fHelp); +extern UniValue getaddressesbyaccount(const UniValue& params, bool fHelp); +extern UniValue sendtoaddress(const UniValue& params, bool fHelp); +extern UniValue sendtoaddressix(const UniValue& params, bool fHelp); +extern UniValue signmessage(const UniValue& params, bool fHelp); +extern UniValue getreceivedbyaddress(const UniValue& params, bool fHelp); +extern UniValue getreceivedbyaccount(const UniValue& params, bool fHelp); +extern UniValue getbalance(const UniValue& params, bool fHelp); +extern UniValue getunconfirmedbalance(const UniValue& params, bool fHelp); +extern UniValue movecmd(const UniValue& params, bool fHelp); +extern UniValue sendfrom(const UniValue& params, bool fHelp); +extern UniValue sendmany(const UniValue& params, bool fHelp); +extern UniValue addmultisigaddress(const UniValue& params, bool fHelp); +extern UniValue listreceivedbyaddress(const UniValue& params, bool fHelp); +extern UniValue listreceivedbyaccount(const UniValue& params, bool fHelp); +extern UniValue listtransactions(const UniValue& params, bool fHelp); +extern UniValue listaddressgroupings(const UniValue& params, bool fHelp); +extern UniValue listaccounts(const UniValue& params, bool fHelp); +extern UniValue listsinceblock(const UniValue& params, bool fHelp); +extern UniValue gettransaction(const UniValue& params, bool fHelp); +extern UniValue backupwallet(const UniValue& params, bool fHelp); +extern UniValue keypoolrefill(const UniValue& params, bool fHelp); +extern UniValue walletpassphrase(const UniValue& params, bool fHelp); +extern UniValue walletpassphrasechange(const UniValue& params, bool fHelp); +extern UniValue walletlock(const UniValue& params, bool fHelp); +extern UniValue encryptwallet(const UniValue& params, bool fHelp); +extern UniValue getwalletinfo(const UniValue& params, bool fHelp); +extern UniValue getblockchaininfo(const UniValue& params, bool fHelp); +extern UniValue getnetworkinfo(const UniValue& params, bool fHelp); +extern UniValue reservebalance(const UniValue& params, bool fHelp); +extern UniValue setstakesplitthreshold(const UniValue& params, bool fHelp); +extern UniValue getstakesplitthreshold(const UniValue& params, bool fHelp); +extern UniValue multisend(const UniValue& params, bool fHelp); +extern UniValue autocombinerewards(const UniValue& params, bool fHelp); + +extern UniValue getrawtransaction(const UniValue& params, bool fHelp); // in rcprawtransaction.cpp +extern UniValue listunspent(const UniValue& params, bool fHelp); +extern UniValue lockunspent(const UniValue& params, bool fHelp); +extern UniValue listlockunspent(const UniValue& params, bool fHelp); +extern UniValue createrawtransaction(const UniValue& params, bool fHelp); +extern UniValue decoderawtransaction(const UniValue& params, bool fHelp); +extern UniValue decodescript(const UniValue& params, bool fHelp); +extern UniValue signrawtransaction(const UniValue& params, bool fHelp); +extern UniValue sendrawtransaction(const UniValue& params, bool fHelp); + +extern UniValue getblockcount(const UniValue& params, bool fHelp); // in rpcblockchain.cpp +extern UniValue getbestblockhash(const UniValue& params, bool fHelp); +extern UniValue getdifficulty(const UniValue& params, bool fHelp); +extern UniValue settxfee(const UniValue& params, bool fHelp); +extern UniValue getmempoolinfo(const UniValue& params, bool fHelp); +extern UniValue getrawmempool(const UniValue& params, bool fHelp); +extern UniValue getblockhash(const UniValue& params, bool fHelp); +extern UniValue getblock(const UniValue& params, bool fHelp); +extern UniValue getblockheader(const UniValue& params, bool fHelp); +extern UniValue getfeeinfo(const UniValue& params, bool fHelp); +extern UniValue gettxoutsetinfo(const UniValue& params, bool fHelp); +extern UniValue gettxout(const UniValue& params, bool fHelp); +extern UniValue verifychain(const UniValue& params, bool fHelp); +extern UniValue getchaintips(const UniValue& params, bool fHelp); +extern UniValue invalidateblock(const UniValue& params, bool fHelp); +extern UniValue reconsiderblock(const UniValue& params, bool fHelp); +extern UniValue getinvalid(const UniValue& params, bool fHelp); + +extern UniValue obfuscation(const UniValue& params, bool fHelp); // in rpcmasternode.cpp +extern UniValue getpoolinfo(const UniValue& params, bool fHelp); +extern UniValue masternode(const UniValue& params, bool fHelp); +extern UniValue listmasternodes(const UniValue& params, bool fHelp); +extern UniValue getmasternodecount(const UniValue& params, bool fHelp); +extern UniValue masternodeconnect(const UniValue& params, bool fHelp); +extern UniValue masternodecurrent(const UniValue& params, bool fHelp); +extern UniValue masternodedebug(const UniValue& params, bool fHelp); +extern UniValue startmasternode(const UniValue& params, bool fHelp); +extern UniValue createmasternodekey(const UniValue& params, bool fHelp); +extern UniValue getmasternodeoutputs(const UniValue& params, bool fHelp); +extern UniValue listmasternodeconf(const UniValue& params, bool fHelp); +extern UniValue getmasternodestatus(const UniValue& params, bool fHelp); +extern UniValue getmasternodewinners(const UniValue& params, bool fHelp); +extern UniValue getmasternodescores(const UniValue& params, bool fHelp); + +extern UniValue getinfo(const UniValue& params, bool fHelp); // in rpcmisc.cpp +extern UniValue mnsync(const UniValue& params, bool fHelp); +extern UniValue spork(const UniValue& params, bool fHelp); +extern UniValue validateaddress(const UniValue& params, bool fHelp); +extern UniValue createmultisig(const UniValue& params, bool fHelp); +extern UniValue verifymessage(const UniValue& params, bool fHelp); +extern UniValue setmocktime(const UniValue& params, bool fHelp); +extern UniValue getstakingstatus(const UniValue& params, bool fHelp); + +// in rest.cpp +extern bool HTTPReq_REST(AcceptedConnection* conn, + std::string& strURI, + std::map& mapHeaders, + bool fRun); +void RPCNotifyBlockChange(const uint256 nHeight); + +#endif // BITCOIN_RPCSERVER_H diff --git a/src/script/sigcache.h b/src/script/sigcache.h index 44d10cc..38b032b 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -6,7 +6,7 @@ #ifndef BITCOIN_SCRIPT_SIGCACHE_H #define BITCOIN_SCRIPT_SIGCACHE_H -#include "interpreter.h" +#include "script/interpreter.h" #include diff --git a/src/script/standard.h b/src/script/standard.h index 6bca44c..bccadaa 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -6,8 +6,8 @@ #ifndef BITCOIN_SCRIPT_STANDARD_H #define BITCOIN_SCRIPT_STANDARD_H -#include "interpreter.h" -#include "../uint256.h" +#include "script/interpreter.h" +#include "uint256.h" #include diff --git a/src/serialize.h b/src/serialize.h index 0ba97fb..4bcf9c6 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -44,11 +44,12 @@ inline T* NCONST_PTR(const T* val) return const_cast(val); } -/** - * Get begin pointer of vector (non-const version). - * @note These functions avoid the undefined case of indexing into an empty - * vector, as well as that of indexing after the end of the vector. - */ +///////////////////////////////////////////////////////////////// +// +// Templates for serializing to anything that looks like a stream, +// i.e. anything that supports .read(char*, size_t) and .write(char*, size_t) +// + template inline T* begin_ptr(std::vector& v) { @@ -73,12 +74,6 @@ inline const T* end_ptr(const std::vector& v) return v.empty() ? NULL : (&v[0] + v.size()); } -///////////////////////////////////////////////////////////////// -// -// Templates for serializing to anything that looks like a stream, -// i.e. anything that supports .read(char*, size_t) and .write(char*, size_t) -// - enum { // primary actions SER_NETWORK = (1 << 0), @@ -431,6 +426,7 @@ I ReadVarInt(Stream& is) #define FLATDATA(obj) REF(CFlatData((char*)&(obj), (char*)&(obj) + sizeof(obj))) #define VARINT(obj) REF(WrapVarInt(REF(obj))) +#define COMPACTSIZE(obj) REF(CCompactSize(REF(obj))) #define LIMITED_STRING(obj, n) REF(LimitedString(REF(obj))) /** @@ -447,8 +443,8 @@ class CFlatData template explicit CFlatData(std::vector& v) { - pbegin = (char*)begin_ptr(v); - pend = (char*)end_ptr(v); + pbegin = (char*)v.data(); + pend = (char*)(v.data() + v.size()); } char* begin() { return pbegin; } const char* begin() const { return pbegin; } @@ -500,6 +496,28 @@ class CVarInt } }; +class CCompactSize +{ +protected: + uint64_t &n; +public: + CCompactSize(uint64_t& nIn) : n(nIn) { } + + unsigned int GetSerializeSize(int, int) const { + return GetSizeOfCompactSize(n); + } + + template + void Serialize(Stream &s, int, int) const { + WriteCompactSize(s, n); + } + + template + void Unserialize(Stream& s, int, int) { + n = ReadCompactSize(s); + } +}; + template class LimitedString { @@ -910,6 +928,12 @@ class CSizeComputer return *this; } + /** Pretend _nSize bytes are written, without specifying them. */ + void seek(size_t _nSize) + { + this->nSize += _nSize; + } + template CSizeComputer& operator<<(const T& obj) { diff --git a/src/spork.cpp b/src/spork.cpp index 35a11c1..bb0f72e 100644 --- a/src/spork.cpp +++ b/src/spork.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2014-2016 The Dash developers // Copyright (c) 2016-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -11,6 +11,7 @@ #include "net.h" #include "protocol.h" #include "sync.h" +#include "sporkdb.h" #include "util.h" #include @@ -25,6 +26,36 @@ CSporkManager sporkManager; std::map mapSporks; std::map mapSporksActive; +// PIVX: on startup load spork values from previous session if they exist in the sporkDB +void LoadSporksFromDB() +{ + for (int i = SPORK_START; i <= SPORK_END; ++i) { + // Since not all spork IDs are in use, we have to exclude undefined IDs + std::string strSpork = sporkManager.GetSporkNameByID(i); + if (strSpork == "Unknown") continue; + + // attempt to read spork from sporkDB + CSporkMessage spork; + if (!pSporkDB->ReadSpork(i, spork)) { + LogPrintf("%s : no previous value for %s found in database\n", __func__, strSpork); + continue; + } + + // add spork to memory + mapSporks[spork.GetHash()] = spork; + mapSporksActive[spork.nSporkID] = spork; + std::time_t result = spork.nValue; + // If SPORK Value is greater than 1,000,000 assume it's actually a Date and then convert to a more readable format + if (spork.nValue > 1000000) { + LogPrintf("%s : loaded spork %s with value %d : %s", __func__, + sporkManager.GetSporkNameByID(spork.nSporkID), spork.nValue, + std::ctime(&result)); + } else { + LogPrintf("%s : loaded spork %s with value %d\n", __func__, + sporkManager.GetSporkNameByID(spork.nSporkID), spork.nValue); + } + } +} void ProcessSpork(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) { @@ -38,6 +69,10 @@ void ProcessSpork(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) if (chainActive.Tip() == NULL) return; + // Ignore spork messages about unknown/deleted sporks + std::string strSpork = sporkManager.GetSporkNameByID(spork.nSporkID); + if (strSpork == "Unknown") return; + uint256 hash = spork.GetHash(); if (mapSporksActive.count(spork.nSporkID)) { if (mapSporksActive[spork.nSporkID].nTimeSigned >= spork.nTimeSigned) { @@ -60,8 +95,8 @@ void ProcessSpork(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) mapSporksActive[spork.nSporkID] = spork; sporkManager.Relay(spork); - //does a task if needed - ExecuteSpork(spork.nSporkID, spork.nValue); + // PIVX: add to spork database. + pSporkDB->WriteSpork(spork.nSporkID, spork); } if (strCommand == "getsporks") { std::map::iterator it = mapSporksActive.begin(); @@ -73,27 +108,6 @@ void ProcessSpork(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) } } -// grab the spork, otherwise say it's off -bool IsSporkActive(int nSporkID) -{ - int64_t r = -1; - - if (mapSporksActive.count(nSporkID)) { - r = mapSporksActive[nSporkID].nValue; - } else { - if (nSporkID == SPORK_1_SWIFTTX) r = SPORK_1_SWIFTTX_DEFAULT; - if (nSporkID == SPORK_2_SWIFTTX_BLOCK_FILTERING) r = SPORK_2_SWIFTTX_BLOCK_FILTERING_DEFAULT; - if (nSporkID == SPORK_3_MAX_VALUE) r = SPORK_3_MAX_VALUE_DEFAULT; - if (nSporkID == SPORK_4_MASTERNODE_PAYMENT_ENFORCEMENT) r = SPORK_4_MASTERNODE_PAYMENT_ENFORCEMENT_DEFAULT; - if (nSporkID == SPORK_5_RECONSIDER_BLOCKS) r = SPORK_5_RECONSIDER_BLOCKS_DEFAULT; - if (nSporkID == SPORK_6_MN_WINNER_MINIMUM_AGE) r = SPORK_6_MN_WINNER_MINIMUM_AGE_DEFAULT; - - if (r == -1) LogPrintf("GetSpork::Unknown Spork %d\n", nSporkID); - } - if (r == -1) r = 4070908800; //return 2099-1-1 by default - - return r < GetTime(); -} // grab the value of the spork on the network, or the default int64_t GetSporkValue(int nSporkID) @@ -109,6 +123,8 @@ int64_t GetSporkValue(int nSporkID) if (nSporkID == SPORK_4_MASTERNODE_PAYMENT_ENFORCEMENT) r = SPORK_4_MASTERNODE_PAYMENT_ENFORCEMENT_DEFAULT; if (nSporkID == SPORK_5_RECONSIDER_BLOCKS) r = SPORK_5_RECONSIDER_BLOCKS_DEFAULT; if (nSporkID == SPORK_6_MN_WINNER_MINIMUM_AGE) r = SPORK_6_MN_WINNER_MINIMUM_AGE_DEFAULT; + if (nSporkID == SPORK_7_MN_REBROADCAST_ENFORCEMENT) r = SPORK_7_MN_REBROADCAST_ENFORCEMENT_DEFAULT; + if (nSporkID == SPORK_8_NEW_PROTOCOL_ENFORCEMENT) r = SPORK_8_NEW_PROTOCOL_ENFORCEMENT_DEFAULT; if (r == -1) LogPrintf("GetSpork::Unknown Spork %d\n", nSporkID); @@ -117,16 +133,15 @@ int64_t GetSporkValue(int nSporkID) return r; } -void ExecuteSpork(int nSporkID, int nValue) +// grab the spork value, and see if it's off +bool IsSporkActive(int nSporkID) { - //correct fork via spork technology - if (nSporkID == SPORK_5_RECONSIDER_BLOCKS && nValue > 0) { - LogPrintf("Spork::ExecuteSpork -- Reconsider Last %d Blocks\n", nValue); - - ReprocessBlocks(nValue); - } + int64_t r = GetSporkValue(nSporkID); + if (r == -1) return false; + return r < GetTime(); } + void ReprocessBlocks(int nBlocks) { std::map::iterator it = mapRejectedBlocks.begin(); @@ -162,14 +177,13 @@ bool CSporkManager::CheckSignature(CSporkMessage& spork) { //note: need to investigate why this is failing std::string strMessage = boost::lexical_cast(spork.nSporkID) + boost::lexical_cast(spork.nValue) + boost::lexical_cast(spork.nTimeSigned); - CPubKey pubkey(ParseHex(Params().SporkKey())); - + CPubKey pubkeynew(ParseHex(Params().SporkKey())); std::string errorMessage = ""; - if (!obfuScationSigner.VerifyMessage(pubkey, spork.vchSig, strMessage, errorMessage)) { - return false; + if (obfuScationSigner.VerifyMessage(pubkeynew, spork.vchSig, strMessage, errorMessage)) { + return true; } - return true; + return false; } bool CSporkManager::Sign(CSporkMessage& spork) @@ -181,17 +195,17 @@ bool CSporkManager::Sign(CSporkMessage& spork) std::string errorMessage = ""; if (!obfuScationSigner.SetKey(strMasterPrivKey, errorMessage, key2, pubkey2)) { - LogPrintf("CMasternodePayments::Sign - ERROR: Invalid masternodeprivkey: '%s'\n", errorMessage); + LogPrint("masternode","CMasternodePayments::Sign - ERROR: Invalid masternodeprivkey: '%s'\n", errorMessage); return false; } if (!obfuScationSigner.SignMessage(strMessage, errorMessage, spork.vchSig, key2)) { - LogPrintf("CMasternodePayments::Sign - Sign message failed"); + LogPrint("masternode","CMasternodePayments::Sign - Sign message failed"); return false; } if (!obfuScationSigner.VerifyMessage(pubkey2, spork.vchSig, strMessage, errorMessage)) { - LogPrintf("CMasternodePayments::Sign - Verify message failed"); + LogPrint("masternode","CMasternodePayments::Sign - Verify message failed"); return false; } @@ -246,6 +260,9 @@ int CSporkManager::GetSporkIDByName(std::string strName) if (strName == "SPORK_4_MASTERNODE_PAYMENT_ENFORCEMENT") return SPORK_4_MASTERNODE_PAYMENT_ENFORCEMENT; if (strName == "SPORK_5_RECONSIDER_BLOCKS") return SPORK_5_RECONSIDER_BLOCKS; if (strName == "SPORK_6_MN_WINNER_MINIMUM_AGE") return SPORK_6_MN_WINNER_MINIMUM_AGE; + if (strName == "SPORK_7_MN_REBROADCAST_ENFORCEMENT") return SPORK_7_MN_REBROADCAST_ENFORCEMENT; + if (strName == "SPORK_8_NEW_PROTOCOL_ENFORCEMENT") return SPORK_8_NEW_PROTOCOL_ENFORCEMENT; + return -1; } @@ -258,6 +275,9 @@ std::string CSporkManager::GetSporkNameByID(int id) if (id == SPORK_4_MASTERNODE_PAYMENT_ENFORCEMENT) return "SPORK_4_MASTERNODE_PAYMENT_ENFORCEMENT"; if (id == SPORK_5_RECONSIDER_BLOCKS) return "SPORK_5_RECONSIDER_BLOCKS"; if (id == SPORK_6_MN_WINNER_MINIMUM_AGE) return "SPORK_6_MN_WINNER_MINIMUM_AGE"; + if (id == SPORK_7_MN_REBROADCAST_ENFORCEMENT) return "SPORK_7_MN_REBROADCAST_ENFORCEMENT"; + if (id == SPORK_8_NEW_PROTOCOL_ENFORCEMENT) return "SPORK_8_NEW_PROTOCOL_ENFORCEMENT"; + return "Unknown"; } diff --git a/src/spork.h b/src/spork.h index 3d36828..ff0e649 100644 --- a/src/spork.h +++ b/src/spork.h @@ -1,7 +1,7 @@ // Copyright (c) 2014-2016 The Dash developers // Copyright (c) 2016-2017 The PIVX developers // Copyright (c) 2017-2018 The Bulwark developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -27,7 +27,7 @@ using namespace boost; - This would result in old clients getting confused about which spork is for what */ #define SPORK_START 10001 -#define SPORK_END 10006 +#define SPORK_END 10008 #define SPORK_1_SWIFTTX 10001 #define SPORK_2_SWIFTTX_BLOCK_FILTERING 10002 @@ -35,6 +35,8 @@ using namespace boost; #define SPORK_4_MASTERNODE_PAYMENT_ENFORCEMENT 10004 #define SPORK_5_RECONSIDER_BLOCKS 10005 #define SPORK_6_MN_WINNER_MINIMUM_AGE 10006 +#define SPORK_7_MN_REBROADCAST_ENFORCEMENT 10007 +#define SPORK_8_NEW_PROTOCOL_ENFORCEMENT 10008 #define SPORK_1_SWIFTTX_DEFAULT 978307200 //2001-1-1 #define SPORK_2_SWIFTTX_BLOCK_FILTERING_DEFAULT 1424217600 //2015-2-18 @@ -44,6 +46,8 @@ using namespace boost; #define SPORK_6_MN_WINNER_MINIMUM_AGE_DEFAULT 8000 // Age in seconds. This should be > MASTERNODE_REMOVAL_SECONDS to avoid // misconfigured new nodes in the list. // Set this to zero to emulate classic behaviour +#define SPORK_7_MN_REBROADCAST_ENFORCEMENT_DEFAULT 4529244393 +#define SPORK_8_NEW_PROTOCOL_ENFORCEMENT_DEFAULT 4529244393 class CSporkMessage; class CSporkManager; @@ -52,10 +56,10 @@ extern std::map mapSporks; extern std::map mapSporksActive; extern CSporkManager sporkManager; +void LoadSporksFromDB(); void ProcessSpork(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); int64_t GetSporkValue(int nSporkID); bool IsSporkActive(int nSporkID); -void ExecuteSpork(int nSporkID, int nValue); void ReprocessBlocks(int nBlocks); // diff --git a/src/sporkdb.cpp b/src/sporkdb.cpp new file mode 100644 index 0000000..79be1dc --- /dev/null +++ b/src/sporkdb.cpp @@ -0,0 +1,25 @@ +// Copyright (c) 2017 The PIVX developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "sporkdb.h" +#include "spork.h" + +CSporkDB::CSporkDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDBWrapper(GetDataDir() / "sporks", nCacheSize, fMemory, fWipe) {} + +bool CSporkDB::WriteSpork(const int nSporkId, const CSporkMessage& spork) +{ + LogPrintf("Wrote spork %s to database\n", sporkManager.GetSporkNameByID(nSporkId)); + return Write(nSporkId, spork); + +} + +bool CSporkDB::ReadSpork(const int nSporkId, CSporkMessage& spork) +{ + return Read(nSporkId, spork); +} + +bool CSporkDB::SporkExists(const int nSporkId) +{ + return Exists(nSporkId); +} diff --git a/src/sporkdb.h b/src/sporkdb.h new file mode 100644 index 0000000..47a6a5a --- /dev/null +++ b/src/sporkdb.h @@ -0,0 +1,28 @@ +// Copyright (c) 2017 The PIVX developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef PIVX_CSPORKDB_H +#define PIVX_CSPORKDB_H + +#include +#include "leveldbwrapper.h" +#include "spork.h" + +class CSporkDB : public CLevelDBWrapper +{ +public: + CSporkDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); + +private: + CSporkDB(const CSporkDB&); + void operator=(const CSporkDB&); + +public: + bool WriteSpork(const int nSporkId, const CSporkMessage& spork); + bool ReadSpork(const int nSporkId, CSporkMessage& spork); + bool SporkExists(const int nSporkId); +}; + + +#endif //PIVX_CSPORKDB_H diff --git a/src/swifttx.h b/src/swifttx.h index 84f1b9a..c3f8989 100644 --- a/src/swifttx.h +++ b/src/swifttx.h @@ -1,6 +1,6 @@ // Copyright (c) 2009-2012 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/sync.cpp b/src/sync.cpp index ef35c9d..d088d51 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -4,15 +4,18 @@ #include "sync.h" +#include +#include + #include "util.h" #include "utilstrencodings.h" #include -#include -#include - #ifdef DEBUG_LOCKCONTENTION +#if !defined(HAVE_THREAD_LOCAL) +static_assert(false, "thread_local is not supported"); +#endif void PrintLockContention(const char* pszName, const char* pszFile, int nLine) { LogPrintf("LOCKCONTENTION: %s\n", pszName); @@ -33,98 +36,101 @@ void PrintLockContention(const char* pszName, const char* pszFile, int nLine) // struct CLockLocation { - CLockLocation(const char* pszName, const char* pszFile, int nLine) + CLockLocation(const char* pszName, const char* pszFile, int nLine, bool fTryIn) { mutexName = pszName; sourceFile = pszFile; sourceLine = nLine; + fTry = fTryIn; } std::string ToString() const { - return mutexName + " " + sourceFile + ":" + itostr(sourceLine); + return mutexName + " " + sourceFile + ":" + itostr(sourceLine) + (fTry ? " (TRY)" : ""); } - std::string MutexName() const { return mutexName; } - private: + bool fTry; std::string mutexName; std::string sourceFile; int sourceLine; }; typedef std::vector > LockStack; +typedef std::map, LockStack> LockOrders; +typedef std::set > InvLockOrders; -static boost::mutex dd_mutex; -static std::map, LockStack> lockorders; -static boost::thread_specific_ptr lockstack; +struct LockData { + // Very ugly hack: as the global constructs and destructors run single + // threaded, we use this boolean to know whether LockData still exists, + // as DeleteLock can get called by global CCriticalSection destructors + // after LockData disappears. + bool available; + LockData() : available(true) {} + ~LockData() { available = false; } + LockOrders lockorders; + InvLockOrders invlockorders; + std::mutex dd_mutex; +} static lockdata; + +static thread_local LockStack g_lockstack; static void potential_deadlock_detected(const std::pair& mismatch, const LockStack& s1, const LockStack& s2) { LogPrintf("POTENTIAL DEADLOCK DETECTED\n"); LogPrintf("Previous lock order was:\n"); - BOOST_FOREACH (const PAIRTYPE(void*, CLockLocation) & i, s2) { - if (i.first == mismatch.first) + for (const std::pair& i : s2) { + if (i.first == mismatch.first) { LogPrintf(" (1)"); - if (i.first == mismatch.second) + } + if (i.first == mismatch.second) { LogPrintf(" (2)"); + } LogPrintf(" %s\n", i.second.ToString()); } LogPrintf("Current lock order is:\n"); - BOOST_FOREACH (const PAIRTYPE(void*, CLockLocation) & i, s1) { - if (i.first == mismatch.first) + for (const std::pair& i : s1) { + if (i.first == mismatch.first) { LogPrintf(" (1)"); - if (i.first == mismatch.second) + } + if (i.first == mismatch.second) { LogPrintf(" (2)"); + } LogPrintf(" %s\n", i.second.ToString()); } } -static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) +static void push_lock(void* c, const CLockLocation& locklocation) { - if (lockstack.get() == NULL) - lockstack.reset(new LockStack); - - LogPrint("lock", "Locking: %s\n", locklocation.ToString()); - dd_mutex.lock(); + std::lock_guard lock(lockdata.dd_mutex); - (*lockstack).push_back(std::make_pair(c, locklocation)); + g_lockstack.push_back(std::make_pair(c, locklocation)); - if (!fTry) { - BOOST_FOREACH (const PAIRTYPE(void*, CLockLocation) & i, (*lockstack)) { - if (i.first == c) - break; + for (const std::pair& i : g_lockstack) { + if (i.first == c) + break; - std::pair p1 = std::make_pair(i.first, c); - if (lockorders.count(p1)) - continue; - lockorders[p1] = (*lockstack); + std::pair p1 = std::make_pair(i.first, c); + if (lockdata.lockorders.count(p1)) + continue; + lockdata.lockorders[p1] = g_lockstack; - std::pair p2 = std::make_pair(c, i.first); - if (lockorders.count(p2)) { - potential_deadlock_detected(p1, lockorders[p2], lockorders[p1]); - break; - } - } + std::pair p2 = std::make_pair(c, i.first); + lockdata.invlockorders.insert(p2); + if (lockdata.lockorders.count(p2)) + potential_deadlock_detected(p1, lockdata.lockorders[p2], lockdata.lockorders[p1]); } - dd_mutex.unlock(); } static void pop_lock() { - if (fDebug) { - const CLockLocation& locklocation = (*lockstack).rbegin()->second; - LogPrint("lock", "Unlocked: %s\n", locklocation.ToString()); - } - dd_mutex.lock(); - (*lockstack).pop_back(); - dd_mutex.unlock(); + g_lockstack.pop_back(); } void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry) { - push_lock(cs, CLockLocation(pszName, pszFile, nLine), fTry); + push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry)); } void LeaveCritical() @@ -135,18 +141,40 @@ void LeaveCritical() std::string LocksHeld() { std::string result; - BOOST_FOREACH (const PAIRTYPE(void*, CLockLocation) & i, *lockstack) + for (const std::pair& i : g_lockstack) result += i.second.ToString() + std::string("\n"); return result; } void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) { - BOOST_FOREACH (const PAIRTYPE(void*, CLockLocation) & i, *lockstack) + for (const std::pair& i : g_lockstack) if (i.first == cs) return; fprintf(stderr, "Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str()); abort(); } +void DeleteLock(void* cs) +{ + if (!lockdata.available) { + // We're already shutting down. + return; + } + std::lock_guard lock(lockdata.dd_mutex); + std::pair item = std::make_pair(cs, nullptr); + LockOrders::iterator it = lockdata.lockorders.lower_bound(item); + while (it != lockdata.lockorders.end() && it->first.first == cs) { + std::pair invitem = std::make_pair(it->first.second, it->first.first); + lockdata.invlockorders.erase(invitem); + lockdata.lockorders.erase(it++); + } + InvLockOrders::iterator invit = lockdata.invlockorders.lower_bound(item); + while (invit != lockdata.invlockorders.end() && invit->first == cs) { + std::pair invinvitem = std::make_pair(invit->second, invit->first); + lockdata.lockorders.erase(invinvitem); + lockdata.invlockorders.erase(invit++); + } +} + #endif /* DEBUG_LOCKORDER */ diff --git a/src/sync.h b/src/sync.h index cd0aa7b..ef003a8 100644 --- a/src/sync.h +++ b/src/sync.h @@ -8,54 +8,48 @@ #include "threadsafety.h" -#include -#include -#include -#include +#include +#include +#include -//////////////////////////////////////////////// -// // -// THE SIMPLE DEFINITON, EXCLUDING DEBUG CODE // -// // -//////////////////////////////////////////////// +///////////////////////////////////////////////// +// // +// THE SIMPLE DEFINITION, EXCLUDING DEBUG CODE // +// // +///////////////////////////////////////////////// /* - - - CCriticalSection mutex; - boost::recursive_mutex mutex; + std::recursive_mutex mutex; LOCK(mutex); - boost::unique_lock criticalblock(mutex); + std::unique_lock criticalblock(mutex); LOCK2(mutex1, mutex2); - boost::unique_lock criticalblock1(mutex1); - boost::unique_lock criticalblock2(mutex2); + std::unique_lock criticalblock1(mutex1); + std::unique_lock criticalblock2(mutex2); TRY_LOCK(mutex, name); - boost::unique_lock name(mutex, boost::try_to_lock_t); + std::unique_lock name(mutex, std::try_to_lock_t); ENTER_CRITICAL_SECTION(mutex); // no RAII mutex.lock(); LEAVE_CRITICAL_SECTION(mutex); // no RAII mutex.unlock(); - - - */ - /////////////////////////////// // // // THE ACTUAL IMPLEMENTATION // // // /////////////////////////////// -// Template mixin that adds -Wthread-safety locking annotations to a -// subset of the mutex API. +/** + * Template mixin that adds -Wthread-safety locking + * annotations to a subset of the mutex API. + */ template class LOCKABLE AnnotatedMixin : public PARENT { @@ -76,40 +70,50 @@ class LOCKABLE AnnotatedMixin : public PARENT } }; -/** Wrapped boost mutex: supports recursive locking, but no waiting */ -// TODO: We should move away from using the recursive lock by default. -typedef AnnotatedMixin CCriticalSection; - -/** Wrapped boost mutex: supports waiting but not recursive locking */ -typedef AnnotatedMixin CWaitableCriticalSection; - -/** Just a typedef for boost::condition_variable, can be wrapped later if desired */ -typedef boost::condition_variable CConditionVariable; - #ifdef DEBUG_LOCKORDER void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false); void LeaveCritical(); std::string LocksHeld(); void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs); +void DeleteLock(void* cs); #else -void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) -{ -} +void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {} void static inline LeaveCritical() {} void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {} +void static inline DeleteLock(void* cs) {} #endif #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) +/** + * Wrapped mutex: supports recursive locking, but no waiting + * TODO: We should move away from using the recursive lock by default. + */ +class CCriticalSection : public AnnotatedMixin +{ +public: + ~CCriticalSection() { + DeleteLock((void*)this); + } +}; + +/** Wrapped mutex: supports waiting but not recursive locking */ +typedef AnnotatedMixin CWaitableCriticalSection; + +/** Just a typedef for std::condition_variable, can be wrapped later if desired */ +typedef std::condition_variable CConditionVariable; + +/** Just a typedef for std::unique_lock, can be wrapped later if desired */ +typedef std::unique_lock WaitableLock; + #ifdef DEBUG_LOCKCONTENTION void PrintLockContention(const char* pszName, const char* pszFile, int nLine); #endif -/** Wrapper around boost::unique_lock */ -template -class CMutexLock +/** Wrapper around std::unique_lock */ +class SCOPED_LOCKABLE CCriticalBlock { private: - boost::unique_lock lock; + std::unique_lock lock; void Enter(const char* pszName, const char* pszFile, int nLine) { @@ -134,15 +138,26 @@ class CMutexLock } public: - CMutexLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) : lock(mutexIn, boost::defer_lock) + CCriticalBlock(CCriticalSection& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : lock(mutexIn, std::defer_lock) + { + if (fTry) + TryEnter(pszName, pszFile, nLine); + else + Enter(pszName, pszFile, nLine); + } + + CCriticalBlock(CCriticalSection* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn) { + if (!pmutexIn) return; + + lock = std::unique_lock(*pmutexIn, std::defer_lock); if (fTry) TryEnter(pszName, pszFile, nLine); else Enter(pszName, pszFile, nLine); } - ~CMutexLock() + ~CCriticalBlock() UNLOCK_FUNCTION() { if (lock.owns_lock()) LeaveCritical(); @@ -154,9 +169,10 @@ class CMutexLock } }; -typedef CMutexLock CCriticalBlock; +#define PASTE(x, y) x ## y +#define PASTE2(x, y) PASTE(x, y) -#define LOCK(cs) CCriticalBlock criticalblock(cs, #cs, __FILE__, __LINE__) +#define LOCK(cs) CCriticalBlock PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__) #define LOCK2(cs1, cs2) CCriticalBlock criticalblock1(cs1, #cs1, __FILE__, __LINE__), criticalblock2(cs2, #cs2, __FILE__, __LINE__) #define TRY_LOCK(cs, name) CCriticalBlock name(cs, #cs, __FILE__, __LINE__, true) @@ -175,25 +191,23 @@ typedef CMutexLock CCriticalBlock; class CSemaphore { private: - boost::condition_variable condition; - boost::mutex mutex; + std::condition_variable condition; + std::mutex mutex; int value; public: - CSemaphore(int init) : value(init) {} + explicit CSemaphore(int init) : value(init) {} void wait() { - boost::unique_lock lock(mutex); - while (value < 1) { - condition.wait(lock); - } + std::unique_lock lock(mutex); + condition.wait(lock, [&]() { return value >= 1; }); value--; } bool try_wait() { - boost::unique_lock lock(mutex); + std::lock_guard lock(mutex); if (value < 1) return false; value--; @@ -203,7 +217,7 @@ class CSemaphore void post() { { - boost::unique_lock lock(mutex); + std::lock_guard lock(mutex); value++; } condition.notify_one(); @@ -246,13 +260,12 @@ class CSemaphoreGrant grant.Release(); grant.sem = sem; grant.fHaveGrant = fHaveGrant; - sem = NULL; fHaveGrant = false; } - CSemaphoreGrant() : sem(NULL), fHaveGrant(false) {} + CSemaphoreGrant() : sem(nullptr), fHaveGrant(false) {} - CSemaphoreGrant(CSemaphore& sema, bool fTry = false) : sem(&sema), fHaveGrant(false) + explicit CSemaphoreGrant(CSemaphore& sema, bool fTry = false) : sem(&sema), fHaveGrant(false) { if (fTry) TryAcquire(); @@ -265,7 +278,7 @@ class CSemaphoreGrant Release(); } - operator bool() + operator bool() const { return fHaveGrant; } diff --git a/src/timedata.cpp b/src/timedata.cpp index 82d4381..297462e 100644 --- a/src/timedata.cpp +++ b/src/timedata.cpp @@ -10,8 +10,6 @@ #include "util.h" #include "utilstrencodings.h" -#include - using namespace std; static CCriticalSection cs_nTimeOffset; @@ -40,10 +38,8 @@ static int64_t abs64(int64_t n) return (n >= 0 ? n : -n); } -void AddTimeData(const CNetAddr& ip, int64_t nTime) +void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample) { - int64_t nOffsetSample = nTime - GetTime(); - LOCK(cs_nTimeOffset); // Ignore duplicates static set setKnown; @@ -85,7 +81,7 @@ void AddTimeData(const CNetAddr& ip, int64_t nTime) if (!fDone) { // If nobody has a time different than ours but within 5 minutes of ours, give a warning bool fMatch = false; - BOOST_FOREACH (int64_t nOffset, vSorted) + for (int64_t nOffset : vSorted) if (nOffset != 0 && abs64(nOffset) < 5 * 60) fMatch = true; @@ -99,7 +95,7 @@ void AddTimeData(const CNetAddr& ip, int64_t nTime) } } if (fDebug) { - BOOST_FOREACH (int64_t n, vSorted) + for (int64_t n : vSorted) LogPrintf("%+d ", n); LogPrintf("| "); } diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp new file mode 100644 index 0000000..2276d5b --- /dev/null +++ b/src/torcontrol.cpp @@ -0,0 +1,782 @@ +// Copyright (c) 2015-2016 The Bitcoin Core developers +// Copyright (c) 2017 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "torcontrol.h" +#include "utilstrencodings.h" +#include "netbase.h" +#include "net.h" +#include "util.h" +#include "crypto/hmac_sha256.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/** Default control port */ +const std::string DEFAULT_TOR_CONTROL = "127.0.0.1:9051"; +/** Tor cookie size (from control-spec.txt) */ +static const int TOR_COOKIE_SIZE = 32; +/** Size of client/server nonce for SAFECOOKIE */ +static const int TOR_NONCE_SIZE = 32; +/** For computing serverHash in SAFECOOKIE */ +static const std::string TOR_SAFE_SERVERKEY = "Tor safe cookie authentication server-to-controller hash"; +/** For computing clientHash in SAFECOOKIE */ +static const std::string TOR_SAFE_CLIENTKEY = "Tor safe cookie authentication controller-to-server hash"; +/** Exponential backoff configuration - initial timeout in seconds */ +static const float RECONNECT_TIMEOUT_START = 1.0; +/** Exponential backoff configuration - growth factor */ +static const float RECONNECT_TIMEOUT_EXP = 1.5; +/** Maximum length for lines received on TorControlConnection. + * tor-control-spec.txt mentions that there is explicitly no limit defined to line length, + * this is belt-and-suspenders sanity limit to prevent memory exhaustion. + */ +static const int MAX_LINE_LENGTH = 100000; + +/****** Low-level TorControlConnection ********/ + +/** Reply from Tor, can be single or multi-line */ +class TorControlReply +{ +public: + TorControlReply() { Clear(); } + + int code; + std::vector lines; + + void Clear() + { + code = 0; + lines.clear(); + } +}; + +/** Low-level handling for Tor control connection. + * Speaks the SMTP-like protocol as defined in torspec/control-spec.txt + */ +class TorControlConnection +{ +public: + typedef boost::function ConnectionCB; + typedef boost::function ReplyHandlerCB; + + /** Create a new TorControlConnection. + */ + TorControlConnection(struct event_base *base); + ~TorControlConnection(); + + /** + * Connect to a Tor control port. + * target is address of the form host:port. + * connected is the handler that is called when connection is successfully established. + * disconnected is a handler that is called when the connection is broken. + * Return true on success. + */ + bool Connect(const std::string &target, const ConnectionCB& connected, const ConnectionCB& disconnected); + + /** + * Disconnect from Tor control port. + */ + bool Disconnect(); + + /** Send a command, register a handler for the reply. + * A trailing CRLF is automatically added. + * Return true on success. + */ + bool Command(const std::string &cmd, const ReplyHandlerCB& reply_handler); + + /** Response handlers for async replies */ + boost::signals2::signal async_handler; +private: + /** Callback when ready for use */ + boost::function connected; + /** Callback when connection lost */ + boost::function disconnected; + /** Libevent event base */ + struct event_base *base; + /** Connection to control socket */ + struct bufferevent *b_conn; + /** Message being received */ + TorControlReply message; + /** Response handlers */ + std::deque reply_handlers; + + /** Libevent handlers: internal */ + static void readcb(struct bufferevent *bev, void *ctx); + static void eventcb(struct bufferevent *bev, short what, void *ctx); +}; + +TorControlConnection::TorControlConnection(struct event_base *_base): + base(_base), b_conn(0) +{ +} + +TorControlConnection::~TorControlConnection() +{ + if (b_conn) + bufferevent_free(b_conn); +} + +void TorControlConnection::readcb(struct bufferevent *bev, void *ctx) +{ + TorControlConnection *self = (TorControlConnection*)ctx; + struct evbuffer *input = bufferevent_get_input(bev); + size_t n_read_out = 0; + char *line; + assert(input); + // If there is not a whole line to read, evbuffer_readln returns NULL + while((line = evbuffer_readln(input, &n_read_out, EVBUFFER_EOL_CRLF)) != NULL) + { + std::string s(line, n_read_out); + free(line); + if (s.size() < 4) // Short line + continue; + // (-|+| ) + self->message.code = atoi(s.substr(0,3)); + self->message.lines.push_back(s.substr(4)); + char ch = s[3]; // '-','+' or ' ' + if (ch == ' ') { + // Final line, dispatch reply and clean up + if (self->message.code >= 600) { + // Dispatch async notifications to async handler + // Synchronous and asynchronous messages are never interleaved + self->async_handler(*self, self->message); + } else { + if (!self->reply_handlers.empty()) { + // Invoke reply handler with message + self->reply_handlers.front()(*self, self->message); + self->reply_handlers.pop_front(); + } else { + LogPrint("tor", "tor: Received unexpected sync reply %i\n", self->message.code); + } + } + self->message.Clear(); + } + } + // Check for size of buffer - protect against memory exhaustion with very long lines + // Do this after evbuffer_readln to make sure all full lines have been + // removed from the buffer. Everything left is an incomplete line. + if (evbuffer_get_length(input) > MAX_LINE_LENGTH) { + LogPrintf("tor: Disconnecting because MAX_LINE_LENGTH exceeded\n"); + self->Disconnect(); + } +} + +void TorControlConnection::eventcb(struct bufferevent *bev, short what, void *ctx) +{ + TorControlConnection *self = (TorControlConnection*)ctx; + if (what & BEV_EVENT_CONNECTED) { + LogPrint("tor", "tor: Successfully connected!\n"); + self->connected(*self); + } else if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) { + if (what & BEV_EVENT_ERROR) { + LogPrint("tor", "tor: Error connecting to Tor control socket\n"); + } else { + LogPrint("tor", "tor: End of stream\n"); + } + self->Disconnect(); + self->disconnected(*self); + } +} + +bool TorControlConnection::Connect(const std::string &target, const ConnectionCB& _connected, const ConnectionCB& _disconnected) +{ + if (b_conn) + Disconnect(); + // Parse target address:port + struct sockaddr_storage connect_to_addr; + int connect_to_addrlen = sizeof(connect_to_addr); + if (evutil_parse_sockaddr_port(target.c_str(), + (struct sockaddr*)&connect_to_addr, &connect_to_addrlen)<0) { + LogPrintf("tor: Error parsing socket address %s\n", target); + return false; + } + + // Create a new socket, set up callbacks and enable notification bits + b_conn = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); + if (!b_conn) + return false; + bufferevent_setcb(b_conn, TorControlConnection::readcb, NULL, TorControlConnection::eventcb, this); + bufferevent_enable(b_conn, EV_READ|EV_WRITE); + this->connected = _connected; + this->disconnected = _disconnected; + + // Finally, connect to target + if (bufferevent_socket_connect(b_conn, (struct sockaddr*)&connect_to_addr, connect_to_addrlen) < 0) { + LogPrintf("tor: Error connecting to address %s\n", target); + return false; + } + return true; +} + +bool TorControlConnection::Disconnect() +{ + if (b_conn) + bufferevent_free(b_conn); + b_conn = 0; + return true; +} + +bool TorControlConnection::Command(const std::string &cmd, const ReplyHandlerCB& reply_handler) +{ + if (!b_conn) + return false; + struct evbuffer *buf = bufferevent_get_output(b_conn); + if (!buf) + return false; + evbuffer_add(buf, cmd.data(), cmd.size()); + evbuffer_add(buf, "\r\n", 2); + reply_handlers.push_back(reply_handler); + return true; +} + +/****** General parsing utilities ********/ + +/* Split reply line in the form 'AUTH METHODS=...' into a type + * 'AUTH' and arguments 'METHODS=...'. + * Grammar is implicitly defined in https://spec.torproject.org/control-spec by + * the server reply formats for PROTOCOLINFO (S3.21) and AUTHCHALLENGE (S3.24). + */ +static std::pair SplitTorReplyLine(const std::string &s) +{ + size_t ptr=0; + std::string type; + while (ptr < s.size() && s[ptr] != ' ') { + type.push_back(s[ptr]); + ++ptr; + } + if (ptr < s.size()) + ++ptr; // skip ' ' + return make_pair(type, s.substr(ptr)); +} + +/** Parse reply arguments in the form 'METHODS=COOKIE,SAFECOOKIE COOKIEFILE=".../control_auth_cookie"'. + * Returns a map of keys to values, or an empty map if there was an error. + * Grammar is implicitly defined in https://spec.torproject.org/control-spec by + * the server reply formats for PROTOCOLINFO (S3.21), AUTHCHALLENGE (S3.24), + * and ADD_ONION (S3.27). See also sections 2.1 and 2.3. + */ +static std::map ParseTorReplyMapping(const std::string &s) +{ + std::map mapping; + size_t ptr=0; + while (ptr < s.size()) { + std::string key, value; + while (ptr < s.size() && s[ptr] != '=' && s[ptr] != ' ') { + key.push_back(s[ptr]); + ++ptr; + } + if (ptr == s.size()) // unexpected end of line + return std::map(); + if (s[ptr] == ' ') // The remaining string is an OptArguments + break; + ++ptr; // skip '=' + if (ptr < s.size() && s[ptr] == '"') { // Quoted string + ++ptr; // skip opening '"' + bool escape_next = false; + while (ptr < s.size() && (escape_next || s[ptr] != '"')) { + // Repeated backslashes must be interpreted as pairs + escape_next = (s[ptr] == '\\' && !escape_next); + value.push_back(s[ptr]); + ++ptr; + } + if (ptr == s.size()) // unexpected end of line + return std::map(); + ++ptr; // skip closing '"' + /** + * Unescape value. Per https://spec.torproject.org/control-spec section 2.1.1: + * + * For future-proofing, controller implementors MAY use the following + * rules to be compatible with buggy Tor implementations and with + * future ones that implement the spec as intended: + * + * Read \n \t \r and \0 ... \377 as C escapes. + * Treat a backslash followed by any other character as that character. + */ + std::string escaped_value; + for (size_t i = 0; i < value.size(); ++i) { + if (value[i] == '\\') { + // This will always be valid, because if the QuotedString + // ended in an odd number of backslashes, then the parser + // would already have returned above, due to a missing + // terminating double-quote. + ++i; + if (value[i] == 'n') { + escaped_value.push_back('\n'); + } else if (value[i] == 't') { + escaped_value.push_back('\t'); + } else if (value[i] == 'r') { + escaped_value.push_back('\r'); + } else if ('0' <= value[i] && value[i] <= '7') { + size_t j; + // Octal escape sequences have a limit of three octal digits, + // but terminate at the first character that is not a valid + // octal digit if encountered sooner. + for (j = 1; j < 3 && (i+j) < value.size() && '0' <= value[i+j] && value[i+j] <= '7'; ++j) {} + // Tor restricts first digit to 0-3 for three-digit octals. + // A leading digit of 4-7 would therefore be interpreted as + // a two-digit octal. + if (j == 3 && value[i] > '3') { + j--; + } + escaped_value.push_back(strtol(value.substr(i, j).c_str(), NULL, 8)); + // Account for automatic incrementing at loop end + i += j - 1; + } else { + escaped_value.push_back(value[i]); + } + } else { + escaped_value.push_back(value[i]); + } + } + value = escaped_value; + } else { // Unquoted value. Note that values can contain '=' at will, just no spaces + while (ptr < s.size() && s[ptr] != ' ') { + value.push_back(s[ptr]); + ++ptr; + } + } + if (ptr < s.size() && s[ptr] == ' ') + ++ptr; // skip ' ' after key=value + mapping[key] = value; + } + return mapping; +} + +/** Read full contents of a file and return them in a std::string. + * Returns a pair . + * If an error occurred, status will be false, otherwise status will be true and the data will be returned in string. + * + * @param maxsize Puts a maximum size limit on the file that is read. If the file is larger than this, truncated data + * (with len > maxsize) will be returned. + */ +static std::pair ReadBinaryFile(const std::string &filename, size_t maxsize=std::numeric_limits::max()) +{ + FILE *f = fopen(filename.c_str(), "rb"); + if (f == NULL) + return std::make_pair(false,""); + std::string retval; + char buffer[128]; + size_t n; + while ((n=fread(buffer, 1, sizeof(buffer), f)) > 0) { + // Check for reading errors so we don't return any data if we couldn't + // read the entire file (or up to maxsize) + if (ferror(f)) + return std::make_pair(false,""); + retval.append(buffer, buffer+n); + if (retval.size() > maxsize) + break; + } + fclose(f); + return std::make_pair(true,retval); +} + +/** Write contents of std::string to a file. + * @return true on success. + */ +static bool WriteBinaryFile(const std::string &filename, const std::string &data) +{ + FILE *f = fopen(filename.c_str(), "wb"); + if (f == NULL) + return false; + if (fwrite(data.data(), 1, data.size(), f) != data.size()) { + fclose(f); + return false; + } + fclose(f); + return true; +} + +/****** Bitcoin specific TorController implementation ********/ + +/** Controller that connects to Tor control socket, authenticate, then create + * and maintain a ephemeral hidden service. + */ +class TorController +{ +public: + TorController(struct event_base* base, const std::string& target); + ~TorController(); + + /** Get name fo file to store private key in */ + std::string GetPrivateKeyFile(); + + /** Reconnect, after getting disconnected */ + void Reconnect(); +private: + struct event_base* base; + std::string target; + TorControlConnection conn; + std::string private_key; + std::string service_id; + bool reconnect; + struct event *reconnect_ev; + float reconnect_timeout; + CService service; + /** Cookie for SAFECOOKIE auth */ + std::vector cookie; + /** ClientNonce for SAFECOOKIE auth */ + std::vector clientNonce; + + /** Callback for ADD_ONION result */ + void add_onion_cb(TorControlConnection& conn, const TorControlReply& reply); + /** Callback for AUTHENTICATE result */ + void auth_cb(TorControlConnection& conn, const TorControlReply& reply); + /** Callback for AUTHCHALLENGE result */ + void authchallenge_cb(TorControlConnection& conn, const TorControlReply& reply); + /** Callback for PROTOCOLINFO result */ + void protocolinfo_cb(TorControlConnection& conn, const TorControlReply& reply); + /** Callback after successful connection */ + void connected_cb(TorControlConnection& conn); + /** Callback after connection lost or failed connection attempt */ + void disconnected_cb(TorControlConnection& conn); + + /** Callback for reconnect timer */ + static void reconnect_cb(evutil_socket_t fd, short what, void *arg); +}; + +TorController::TorController(struct event_base* _base, const std::string& _target): + base(_base), + target(_target), conn(base), reconnect(true), reconnect_ev(0), + reconnect_timeout(RECONNECT_TIMEOUT_START) +{ + reconnect_ev = event_new(base, -1, 0, reconnect_cb, this); + if (!reconnect_ev) + LogPrintf("tor: Failed to create event for reconnection: out of memory?\n"); + // Start connection attempts immediately + if (!conn.Connect(_target, boost::bind(&TorController::connected_cb, this, _1), + boost::bind(&TorController::disconnected_cb, this, _1) )) { + LogPrintf("tor: Initiating connection to Tor control port %s failed\n", _target); + } + // Read service private key if cached + std::pair pkf = ReadBinaryFile(GetPrivateKeyFile()); + if (pkf.first) { + LogPrint("tor", "tor: Reading cached private key from %s\n", GetPrivateKeyFile()); + private_key = pkf.second; + } +} + +TorController::~TorController() +{ + if (reconnect_ev) { + event_free(reconnect_ev); + reconnect_ev = 0; + } + if (service.IsValid()) { + RemoveLocal(service); + } +} + +void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlReply& reply) +{ + if (reply.code == 250) { + LogPrint("tor", "tor: ADD_ONION successful\n"); + for (const std::string &s : reply.lines) { + std::map m = ParseTorReplyMapping(s); + std::map::iterator i; + if ((i = m.find("ServiceID")) != m.end()) + service_id = i->second; + if ((i = m.find("PrivateKey")) != m.end()) + private_key = i->second; + } + if (service_id.empty()) { + LogPrintf("tor: Error parsing ADD_ONION parameters:\n"); + for (const std::string &s : reply.lines) { + LogPrintf(" %s\n", SanitizeString(s)); + } + return; + } + LookupNumeric(std::string(service_id+".onion").c_str(), service, GetListenPort()); + LogPrintf("tor: Got service ID %s, advertising service %s\n", service_id, service.ToString()); + if (WriteBinaryFile(GetPrivateKeyFile(), private_key)) { + LogPrint("tor", "tor: Cached service private key to %s\n", GetPrivateKeyFile()); + } else { + LogPrintf("tor: Error writing service private key to %s\n", GetPrivateKeyFile()); + } + AddLocal(service, LOCAL_MANUAL); + // ... onion requested - keep connection open + } else if (reply.code == 510) { // 510 Unrecognized command + LogPrintf("tor: Add onion failed with unrecognized command (You probably need to upgrade Tor)\n"); + } else { + LogPrintf("tor: Add onion failed; error code %d\n", reply.code); + } +} + +void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply& reply) +{ + if (reply.code == 250) { + LogPrint("tor", "tor: Authentication successful\n"); + + // Now that we know Tor is running setup the proxy for onion addresses + // if -onion isn't set to something else. + if (GetArg("-onion", "") == "") { + CService resolved; + assert(LookupNumeric("127.0.0.1", resolved, 9050)); + CService addrOnion = CService(resolved, 9050); + SetProxy(NET_TOR, addrOnion); + SetLimited(NET_TOR, false); + } + + // Finally - now create the service + if (private_key.empty()) // No private key, generate one + private_key = "NEW:RSA1024"; // Explicitly request RSA1024 - see issue #9214 + // Request hidden service, redirect port. + // Note that the 'virtual' port doesn't have to be the same as our internal port, but this is just a convenient + // choice. TODO; refactor the shutdown sequence some day. + _conn.Command(strprintf("ADD_ONION %s Port=%i,127.0.0.1:%i", private_key, GetListenPort(), GetListenPort()), + boost::bind(&TorController::add_onion_cb, this, _1, _2)); + } else { + LogPrintf("tor: Authentication failed\n"); + } +} + +/** Compute Tor SAFECOOKIE response. + * + * ServerHash is computed as: + * HMAC-SHA256("Tor safe cookie authentication server-to-controller hash", + * CookieString | ClientNonce | ServerNonce) + * (with the HMAC key as its first argument) + * + * After a controller sends a successful AUTHCHALLENGE command, the + * next command sent on the connection must be an AUTHENTICATE command, + * and the only authentication string which that AUTHENTICATE command + * will accept is: + * + * HMAC-SHA256("Tor safe cookie authentication controller-to-server hash", + * CookieString | ClientNonce | ServerNonce) + * + */ +static std::vector ComputeResponse(const std::string &key, const std::vector &cookie, const std::vector &clientNonce, const std::vector &serverNonce) +{ + CHMAC_SHA256 computeHash((const uint8_t*)key.data(), key.size()); + std::vector computedHash(CHMAC_SHA256::OUTPUT_SIZE, 0); + computeHash.Write(cookie.data(), cookie.size()); + computeHash.Write(clientNonce.data(), clientNonce.size()); + computeHash.Write(serverNonce.data(), serverNonce.size()); + computeHash.Finalize(computedHash.data()); + return computedHash; +} + +void TorController::authchallenge_cb(TorControlConnection& _conn, const TorControlReply& reply) +{ + if (reply.code == 250) { + LogPrint("tor", "tor: SAFECOOKIE authentication challenge successful\n"); + std::pair l = SplitTorReplyLine(reply.lines[0]); + if (l.first == "AUTHCHALLENGE") { + std::map m = ParseTorReplyMapping(l.second); + if (m.empty()) { + LogPrintf("tor: Error parsing AUTHCHALLENGE parameters: %s\n", SanitizeString(l.second)); + return; + } + std::vector serverHash = ParseHex(m["SERVERHASH"]); + std::vector serverNonce = ParseHex(m["SERVERNONCE"]); + LogPrint("tor", "tor: AUTHCHALLENGE ServerHash %s ServerNonce %s\n", HexStr(serverHash), HexStr(serverNonce)); + if (serverNonce.size() != 32) { + LogPrintf("tor: ServerNonce is not 32 bytes, as required by spec\n"); + return; + } + + std::vector computedServerHash = ComputeResponse(TOR_SAFE_SERVERKEY, cookie, clientNonce, serverNonce); + if (computedServerHash != serverHash) { + LogPrintf("tor: ServerHash %s does not match expected ServerHash %s\n", HexStr(serverHash), HexStr(computedServerHash)); + return; + } + + std::vector computedClientHash = ComputeResponse(TOR_SAFE_CLIENTKEY, cookie, clientNonce, serverNonce); + _conn.Command("AUTHENTICATE " + HexStr(computedClientHash), boost::bind(&TorController::auth_cb, this, _1, _2)); + } else { + LogPrintf("tor: Invalid reply to AUTHCHALLENGE\n"); + } + } else { + LogPrintf("tor: SAFECOOKIE authentication challenge failed\n"); + } +} + +void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorControlReply& reply) +{ + if (reply.code == 250) { + std::set methods; + std::string cookiefile; + /* + * 250-AUTH METHODS=COOKIE,SAFECOOKIE COOKIEFILE="/home/x/.tor/control_auth_cookie" + * 250-AUTH METHODS=NULL + * 250-AUTH METHODS=HASHEDPASSWORD + */ + for (const std::string &s : reply.lines) { + std::pair l = SplitTorReplyLine(s); + if (l.first == "AUTH") { + std::map m = ParseTorReplyMapping(l.second); + std::map::iterator i; + if ((i = m.find("METHODS")) != m.end()) + boost::split(methods, i->second, boost::is_any_of(",")); + if ((i = m.find("COOKIEFILE")) != m.end()) + cookiefile = i->second; + } else if (l.first == "VERSION") { + std::map m = ParseTorReplyMapping(l.second); + std::map::iterator i; + if ((i = m.find("Tor")) != m.end()) { + LogPrint("tor", "tor: Connected to Tor version %s\n", i->second); + } + } + } + for (const std::string &s : methods) { + LogPrint("tor", "tor: Supported authentication method: %s\n", s); + } + // Prefer NULL, otherwise SAFECOOKIE. If a password is provided, use HASHEDPASSWORD + /* Authentication: + * cookie: hex-encoded ~/.tor/control_auth_cookie + * password: "password" + */ + std::string torpassword = GetArg("-torpassword", ""); + if (!torpassword.empty()) { + if (methods.count("HASHEDPASSWORD")) { + LogPrint("tor", "tor: Using HASHEDPASSWORD authentication\n"); + boost::replace_all(torpassword, "\"", "\\\""); + _conn.Command("AUTHENTICATE \"" + torpassword + "\"", boost::bind(&TorController::auth_cb, this, _1, _2)); + } else { + LogPrintf("tor: Password provided with -torpassword, but HASHEDPASSWORD authentication is not available\n"); + } + } else if (methods.count("NULL")) { + LogPrint("tor", "tor: Using NULL authentication\n"); + _conn.Command("AUTHENTICATE", boost::bind(&TorController::auth_cb, this, _1, _2)); + } else if (methods.count("SAFECOOKIE")) { + // Cookie: hexdump -e '32/1 "%02x""\n"' ~/.tor/control_auth_cookie + LogPrint("tor", "tor: Using SAFECOOKIE authentication, reading cookie authentication from %s\n", cookiefile); + std::pair status_cookie = ReadBinaryFile(cookiefile, TOR_COOKIE_SIZE); + if (status_cookie.first && status_cookie.second.size() == TOR_COOKIE_SIZE) { + // _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), boost::bind(&TorController::auth_cb, this, _1, _2)); + cookie = std::vector(status_cookie.second.begin(), status_cookie.second.end()); + clientNonce = std::vector(TOR_NONCE_SIZE, 0); + GetRandBytes(&clientNonce[0], TOR_NONCE_SIZE); + _conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(clientNonce), boost::bind(&TorController::authchallenge_cb, this, _1, _2)); + } else { + if (status_cookie.first) { + LogPrintf("tor: Authentication cookie %s is not exactly %i bytes, as is required by the spec\n", cookiefile, TOR_COOKIE_SIZE); + } else { + LogPrintf("tor: Authentication cookie %s could not be opened (check permissions)\n", cookiefile); + } + } + } else if (methods.count("HASHEDPASSWORD")) { + LogPrintf("tor: The only supported authentication mechanism left is password, but no password provided with -torpassword\n"); + } else { + LogPrintf("tor: No supported authentication method\n"); + } + } else { + LogPrintf("tor: Requesting protocol info failed\n"); + } +} + +void TorController::connected_cb(TorControlConnection& _conn) +{ + reconnect_timeout = RECONNECT_TIMEOUT_START; + // First send a PROTOCOLINFO command to figure out what authentication is expected + if (!_conn.Command("PROTOCOLINFO 1", boost::bind(&TorController::protocolinfo_cb, this, _1, _2))) + LogPrintf("tor: Error sending initial protocolinfo command\n"); +} + +void TorController::disconnected_cb(TorControlConnection& _conn) +{ + // Stop advertising service when disconnected + if (service.IsValid()) + RemoveLocal(service); + service = CService(); + if (!reconnect) + return; + + LogPrint("tor", "tor: Not connected to Tor control port %s, trying to reconnect\n", target); + + // Single-shot timer for reconnect. Use exponential backoff. + struct timeval time = MillisToTimeval(int64_t(reconnect_timeout * 1000.0)); + if (reconnect_ev) + event_add(reconnect_ev, &time); + reconnect_timeout *= RECONNECT_TIMEOUT_EXP; +} + +void TorController::Reconnect() +{ + /* Try to reconnect and reestablish if we get booted - for example, Tor + * may be restarting. + */ + if (!conn.Connect(target, boost::bind(&TorController::connected_cb, this, _1), + boost::bind(&TorController::disconnected_cb, this, _1) )) { + LogPrintf("tor: Re-initiating connection to Tor control port %s failed\n", target); + } +} + +std::string TorController::GetPrivateKeyFile() +{ + return (GetDataDir() / "onion_private_key").string(); +} + +void TorController::reconnect_cb(evutil_socket_t fd, short what, void *arg) +{ + TorController *self = (TorController*)arg; + self->Reconnect(); +} + +/****** Thread ********/ +static struct event_base *gBase; +static boost::thread torControlThread; + +static void TorControlThread() +{ + TorController ctrl(gBase, GetArg("-torcontrol", DEFAULT_TOR_CONTROL)); + + event_base_dispatch(gBase); +} + +void StartTorControl(boost::thread_group& threadGroup/*, CScheduler& scheduler*/) +{ + assert(!gBase); +#ifdef WIN32 + evthread_use_windows_threads(); +#else + evthread_use_pthreads(); +#endif + gBase = event_base_new(); + if (!gBase) { + LogPrintf("tor: Unable to create event_base\n"); + return; + } + + torControlThread = boost::thread(boost::bind(&TraceThread, "torcontrol", &TorControlThread)); +} + +void InterruptTorControl() +{ + if (gBase) { + LogPrintf("tor: Thread interrupt\n"); + event_base_loopbreak(gBase); + } +} + +void StopTorControl() +{ + // timed_join() avoids the wallet not closing during a repair-restart. For a 'normal' wallet exit + // it behaves for our cases exactly like the normal join() + if (gBase) { +#if BOOST_VERSION >= 105000 + torControlThread.try_join_for(boost::chrono::seconds(1)); +#else + torControlThread.timed_join(boost::posix_time::seconds(1)); +#endif + event_base_free(gBase); + gBase = 0; + } +} \ No newline at end of file diff --git a/src/torcontrol.h b/src/torcontrol.h new file mode 100644 index 0000000..977dfe3 --- /dev/null +++ b/src/torcontrol.h @@ -0,0 +1,24 @@ +// 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. + +/** + * Functionality for communicating with Tor. + */ +#ifndef BITCOIN_TORCONTROL_H +#define BITCOIN_TORCONTROL_H + +#include + +#include +#include +#include + +extern const std::string DEFAULT_TOR_CONTROL; +static const bool DEFAULT_LISTEN_ONION = true; + +void StartTorControl(boost::thread_group& threadGroup); +void InterruptTorControl(); +void StopTorControl(); + +#endif /* BITCOIN_TORCONTROL_H */ diff --git a/src/txdb.cpp b/src/txdb.cpp index 94a932b..10b543f 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2016-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/txdb.h b/src/txdb.h index 98b8eed..2ec32bb 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2016-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/txmempool.cpp b/src/txmempool.cpp index b8373bc..16d5a89 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2016-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -76,14 +76,14 @@ class CBlockAverage size_t FeeSamples() const { return feeSamples.size(); } size_t GetFeeSamples(std::vector& insertInto) const { - BOOST_FOREACH (const CFeeRate& f, feeSamples) + for (const CFeeRate& f : feeSamples) insertInto.push_back(f); return feeSamples.size(); } size_t PrioritySamples() const { return prioritySamples.size(); } size_t GetPrioritySamples(std::vector& insertInto) const { - BOOST_FOREACH (double d, prioritySamples) + for (double d : prioritySamples) insertInto.push_back(d); return prioritySamples.size(); } @@ -102,7 +102,7 @@ class CBlockAverage } static bool AreSane(const std::vector& vecFee, const CFeeRate& minRelayFee) { - BOOST_FOREACH (CFeeRate fee, vecFee) { + for (CFeeRate fee : vecFee) { if (!AreSane(fee, minRelayFee)) return false; } @@ -114,7 +114,7 @@ class CBlockAverage } static bool AreSane(const std::vector vecPriority) { - BOOST_FOREACH (double priority, vecPriority) { + for (double priority : vecPriority) { if (!AreSane(priority)) return false; } @@ -213,7 +213,7 @@ class CMinerPolicyEstimator // to confirm. std::vector > entriesByConfirmations; entriesByConfirmations.resize(history.size()); - BOOST_FOREACH (const CTxMemPoolEntry& entry, entries) { + for (const CTxMemPoolEntry& entry : entries) { // How many blocks did it take for miners to include this transaction? int delta = nBlockHeight - entry.GetHeight(); if (delta <= 0) { @@ -233,7 +233,7 @@ class CMinerPolicyEstimator std::random_shuffle(e.begin(), e.end()); e.resize(10); } - BOOST_FOREACH (const CTxMemPoolEntry* entry, e) { + for (const CTxMemPoolEntry* entry : e) { // Fees are stored and reported as BTC-per-kb: CFeeRate feeRate(entry->GetFee(), entry->GetTxSize()); double dPriority = entry->GetPriority(entry->GetHeight()); // Want priority when it went IN @@ -325,7 +325,7 @@ class CMinerPolicyEstimator { fileout << nBestSeenHeight; fileout << history.size(); - BOOST_FOREACH (const CBlockAverage& entry, history) { + for (const CBlockAverage& entry : history) { entry.Write(fileout); } } @@ -412,8 +412,8 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry& entry) { mapTx[hash] = entry; const CTransaction& tx = mapTx[hash].GetTx(); - for (unsigned int i = 0; i < tx.vin.size(); i++) - mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i); + for (unsigned int i = 0; i < tx.vin.size(); i++) + mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i); nTransactionsUpdated++; totalTxSize += entry.GetTxSize(); } @@ -454,7 +454,7 @@ void CTxMemPool::remove(const CTransaction& origTx, std::list& rem txToRemove.push_back(it->second.ptx->GetHash()); } } - BOOST_FOREACH (const CTxIn& txin, tx.vin) + for (const CTxIn& txin : tx.vin) mapNextTx.erase(txin.prevout); removed.push_back(tx); @@ -472,7 +472,7 @@ void CTxMemPool::removeCoinbaseSpends(const CCoinsViewCache* pcoins, unsigned in list transactionsToRemove; for (std::map::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { const CTransaction& tx = it->second.GetTx(); - BOOST_FOREACH (const CTxIn& txin, tx.vin) { + for (const CTxIn& txin : tx.vin) { std::map::const_iterator it2 = mapTx.find(txin.prevout.hash); if (it2 != mapTx.end()) continue; @@ -484,7 +484,7 @@ void CTxMemPool::removeCoinbaseSpends(const CCoinsViewCache* pcoins, unsigned in } } } - BOOST_FOREACH (const CTransaction& tx, transactionsToRemove) { + for (const CTransaction& tx : transactionsToRemove) { list removed; remove(tx, removed, true); } @@ -495,7 +495,7 @@ void CTxMemPool::removeConflicts(const CTransaction& tx, std::list // Remove transactions which depend on inputs of tx, recursively list result; LOCK(cs); - BOOST_FOREACH (const CTxIn& txin, tx.vin) { + for (const CTxIn& txin : tx.vin) { std::map::iterator it = mapNextTx.find(txin.prevout); if (it != mapNextTx.end()) { const CTransaction& txConflict = *it->second.ptx; @@ -513,13 +513,13 @@ void CTxMemPool::removeForBlock(const std::vector& vtx, unsigned i { LOCK(cs); std::vector entries; - BOOST_FOREACH (const CTransaction& tx, vtx) { + for (const CTransaction& tx : vtx) { uint256 hash = tx.GetHash(); if (mapTx.count(hash)) entries.push_back(mapTx[hash]); } minerPolicyEstimator->seenBlock(entries, nBlockHeight, minRelayFee); - BOOST_FOREACH (const CTransaction& tx, vtx) { + for (const CTransaction& tx : vtx) { std::list dummy; remove(tx, dummy, false); removeConflicts(tx, conflicts); @@ -555,7 +555,7 @@ void CTxMemPool::check(const CCoinsViewCache* pcoins) const checkTotal += it->second.GetTxSize(); const CTransaction& tx = it->second.GetTx(); bool fDependsWait = false; - BOOST_FOREACH (const CTxIn& txin, tx.vin) { + for (const CTxIn& txin : tx.vin) { // Check that every mempool transaction's inputs refer to available coins, or other mempool tx's. std::map::const_iterator it2 = mapTx.find(txin.prevout.hash); if (it2 != mapTx.end()) { @@ -566,7 +566,6 @@ void CTxMemPool::check(const CCoinsViewCache* pcoins) const const CCoins* coins = pcoins->AccessCoins(txin.prevout.hash); assert(coins && coins->IsAvailable(txin.prevout.n)); } - // Check whether its inputs are marked in mapNextTx. std::map::const_iterator it3 = mapNextTx.find(txin.prevout); assert(it3 != mapNextTx.end()); assert(it3->second.ptx == &tx); diff --git a/src/txmempool.h b/src/txmempool.h index 4eca282..8717d47 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2016-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/ui_interface.h b/src/ui_interface.h index e61274a..bee72b2 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -101,6 +101,12 @@ class CClientUIInterface /** New block has been accepted */ boost::signals2::signal NotifyBlockTip; + + /** New block has been accepted and is over a certain size */ + boost::signals2::signal NotifyBlockSize; + + /** Banlist did change. */ + boost::signals2::signal BannedListChanged; }; extern CClientUIInterface uiInterface; diff --git a/src/uint256.h b/src/uint256.h index b4bf5ec..700ab25 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -2,7 +2,7 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/uint512.h b/src/uint512.h index 8357213..0517703 100644 --- a/src/uint512.h +++ b/src/uint512.h @@ -1,5 +1,5 @@ // Copyright (c) 2017 The PIVX Core developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/univalue/lib/univalue.cpp b/src/univalue/lib/univalue.cpp index a418463..b9491a9 100644 --- a/src/univalue/lib/univalue.cpp +++ b/src/univalue/lib/univalue.cpp @@ -12,7 +12,7 @@ #include #include -#include "../include/univalue.h" +#include "univalue.h" namespace { diff --git a/src/util.cpp b/src/util.cpp index df1399f..57f30b7 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -3,7 +3,7 @@ // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers // Copyright (c) 2017-2018 The Bulwark developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/util.h b/src/util.h index ef9e878..1ef6bc2 100644 --- a/src/util.h +++ b/src/util.h @@ -2,7 +2,7 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index cd3fed6..d7d413b 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2016-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -22,16 +22,21 @@ using namespace std; -string SanitizeString(const string& str) +static const std::string CHARS_ALPHA_NUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + +static const std::string SAFE_CHARS[] = { - /** - * safeChars chosen to allow simple messages/URLs/email addresses, but avoid anything - * even possibly remotely dangerous like & or > - */ - static string safeChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890 .,;_-/:?@()"); - string strResult; - for (std::string::size_type i = 0; i < str.size(); i++) { - if (safeChars.find(str[i]) != std::string::npos) + CHARS_ALPHA_NUM + " .,;-_/:?@()", // SAFE_CHARS_DEFAULT + CHARS_ALPHA_NUM + " .,;-_?@", // SAFE_CHARS_UA_COMMENT + CHARS_ALPHA_NUM + ".-_", // SAFE_CHARS_FILENAME +}; + +std::string SanitizeString(const std::string& str, int rule) +{ + std::string strResult; + for (std::string::size_type i = 0; i < str.size(); i++) + { + if (SAFE_CHARS[rule].find(str[i]) != std::string::npos) strResult.push_back(str[i]); } return strResult; diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h index 46aaace..4b94ffe 100644 --- a/src/utilstrencodings.h +++ b/src/utilstrencodings.h @@ -23,7 +23,22 @@ /** This is needed because the foreach macro can't get over the comma in pair */ #define PAIRTYPE(t1, t2) std::pair -std::string SanitizeString(const std::string& str); +/** Used by SanitizeString() */ +enum SafeChars +{ + SAFE_CHARS_DEFAULT, //!< The full set of allowed chars + SAFE_CHARS_UA_COMMENT, //!< BIP-0014 subset + SAFE_CHARS_FILENAME, //!< Chars allowed in filenames +}; + +/** +* Remove unsafe chars. Safe chars chosen to allow simple messages/URLs/email +* addresses, but avoid anything even possibly remotely dangerous like & or > +* @param[in] str The string to sanitize +* @param[in] rule The set of safe chars to choose (default: least restrictive) +* @return A new string without unsafe chars +*/ +std::string SanitizeString(const std::string& str, int rule = SAFE_CHARS_DEFAULT); std::vector ParseHex(const char* psz); std::vector ParseHex(const std::string& str); signed char HexDigit(char c); @@ -66,6 +81,20 @@ bool ParseInt64(const std::string& str, int64_t *out); */ bool ParseDouble(const std::string& str, double *out); +/** + * Convert string to signed 64-bit integer with strict parse error feedback. + * @returns true if the entire string could be parsed as valid integer, + * false if not the entire string could be parsed or when overflow or underflow occurred. + */ +bool ParseInt64(const std::string& str, int64_t *out); + +/** + * Convert string to double with strict parse error feedback. + * @returns true if the entire string could be parsed as valid double, + * false if not the entire string could be parsed or when overflow or underflow occurred. + */ +bool ParseDouble(const std::string& str, double *out); + template std::string HexStr(const T itbegin, const T itend, bool fSpaces = false) { diff --git a/src/validationinterface.h b/src/validationinterface.h index 0d1bbd4..16305d7 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -61,7 +61,8 @@ struct CMainSignals { /** Notifies listeners about an inventory item being seen on the network. */ boost::signals2::signal Inventory; /** Tells listeners to broadcast their data. */ - boost::signals2::signal Broadcast; +// XX42 boost::signals2::signal Broadcast; + boost::signals2::signal Broadcast; /** Notifies listeners of a block validation result */ boost::signals2::signal BlockChecked; /** Notifies listeners that a key for mining is required (coinbase) */ diff --git a/src/version.h b/src/version.h index ab3dde5..87800fa 100644 --- a/src/version.h +++ b/src/version.h @@ -2,7 +2,7 @@ // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers // Copyright (c) 2017-2018 The Bulwark developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -13,14 +13,17 @@ * network protocol versioning */ -static const int PROTOCOL_VERSION = 70014; +static const int PROTOCOL_VERSION = 70015; //! initial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; +//! In this version, 'getheaders' was introduced. +static const int GETHEADERS_VERSION = 70077; + //! disconnect from peers older than this proto version -static const int MIN_PEER_PROTO_VERSION_BEFORE_ENFORCEMENT = 70013; -static const int MIN_PEER_PROTO_VERSION_AFTER_ENFORCEMENT = 70014; +static const int MIN_PEER_PROTO_VERSION_BEFORE_ENFORCEMENT = 70014; +static const int MIN_PEER_PROTO_VERSION_AFTER_ENFORCEMENT = 70015; //! nTime field added to CAddress, starting with this version; //! if possible, avoid requesting addresses nodes older than this @@ -29,4 +32,11 @@ static const int CADDR_TIME_VERSION = 31402; //! BIP 0031, pong message, is enabled for all versions AFTER this one static const int BIP0031_VERSION = 60000; +//! "mempool" command, enhanced "getdata" behavior starts with this version +static const int MEMPOOL_GD_VERSION = 60002; + +//! "filter*" commands are disabled without NODE_BLOOM after and including this version +static const int NO_BLOOM_VERSION = 70005; + + #endif // BITCOIN_VERSION_H diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp new file mode 100644 index 0000000..eeec283 --- /dev/null +++ b/src/wallet/db.cpp @@ -0,0 +1,454 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "db.h" + +#include "addrman.h" +#include "hash.h" +#include "protocol.h" +#include "util.h" +#include "utilstrencodings.h" + +#include + +#ifndef WIN32 +#include +#endif + +#include +#include +#include + +#include + +using namespace std; +using namespace boost; + + +unsigned int nWalletDBUpdated; + + +// +// CDB +// + +CDBEnv bitdb; + +void CDBEnv::EnvShutdown() +{ + if (!fDbEnvInit) + return; + + fDbEnvInit = false; + int ret = dbenv.close(0); + if (ret != 0) + LogPrintf("CDBEnv::EnvShutdown : Error %d shutting down database environment: %s\n", ret, DbEnv::strerror(ret)); + if (!fMockDb) + DbEnv(0).remove(strPath.c_str(), 0); +} + +CDBEnv::CDBEnv() : dbenv(DB_CXX_NO_EXCEPTIONS) +{ + fDbEnvInit = false; + fMockDb = false; +} + +CDBEnv::~CDBEnv() +{ + EnvShutdown(); +} + +void CDBEnv::Close() +{ + EnvShutdown(); +} + +bool CDBEnv::Open(const boost::filesystem::path& pathIn) +{ + if (fDbEnvInit) + return true; + + boost::this_thread::interruption_point(); + + strPath = pathIn.string(); + boost::filesystem::path pathLogDir = pathIn / "database"; + TryCreateDirectory(pathLogDir); + boost::filesystem::path pathErrorFile = pathIn / "db.log"; + LogPrintf("CDBEnv::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string()); + + unsigned int nEnvFlags = 0; + if (GetBoolArg("-privdb", true)) + nEnvFlags |= DB_PRIVATE; + + dbenv.set_lg_dir(pathLogDir.string().c_str()); + dbenv.set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet + dbenv.set_lg_bsize(0x10000); + dbenv.set_lg_max(1048576); + dbenv.set_lk_max_locks(40000); + dbenv.set_lk_max_objects(40000); + dbenv.set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug + dbenv.set_flags(DB_AUTO_COMMIT, 1); + dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1); + dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1); + int ret = dbenv.open(strPath.c_str(), + DB_CREATE | + DB_INIT_LOCK | + DB_INIT_LOG | + DB_INIT_MPOOL | + DB_INIT_TXN | + DB_THREAD | + DB_RECOVER | + nEnvFlags, + S_IRUSR | S_IWUSR); + if (ret != 0) + return error("CDBEnv::Open : Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret)); + + fDbEnvInit = true; + fMockDb = false; + return true; +} + +void CDBEnv::MakeMock() +{ + if (fDbEnvInit) + throw runtime_error("CDBEnv::MakeMock : Already initialized"); + + boost::this_thread::interruption_point(); + + LogPrint("db", "CDBEnv::MakeMock\n"); + + dbenv.set_cachesize(1, 0, 1); + dbenv.set_lg_bsize(10485760 * 4); + dbenv.set_lg_max(10485760); + dbenv.set_lk_max_locks(10000); + dbenv.set_lk_max_objects(10000); + dbenv.set_flags(DB_AUTO_COMMIT, 1); + dbenv.log_set_config(DB_LOG_IN_MEMORY, 1); + int ret = dbenv.open(NULL, + DB_CREATE | + DB_INIT_LOCK | + DB_INIT_LOG | + DB_INIT_MPOOL | + DB_INIT_TXN | + DB_THREAD | + DB_PRIVATE, + S_IRUSR | S_IWUSR); + if (ret > 0) + throw runtime_error(strprintf("CDBEnv::MakeMock : Error %d opening database environment.", ret)); + + fDbEnvInit = true; + fMockDb = true; +} + +CDBEnv::VerifyResult CDBEnv::Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile)) +{ + LOCK(cs_db); + assert(mapFileUseCount.count(strFile) == 0); + + Db db(&dbenv, 0); + int result = db.verify(strFile.c_str(), NULL, NULL, 0); + if (result == 0) + return VERIFY_OK; + else if (recoverFunc == NULL) + return RECOVER_FAIL; + + // Try to recover: + bool fRecovered = (*recoverFunc)(*this, strFile); + return (fRecovered ? RECOVER_OK : RECOVER_FAIL); +} + +bool CDBEnv::Salvage(std::string strFile, bool fAggressive, std::vector& vResult) +{ + LOCK(cs_db); + assert(mapFileUseCount.count(strFile) == 0); + + u_int32_t flags = DB_SALVAGE; + if (fAggressive) + flags |= DB_AGGRESSIVE; + + stringstream strDump; + + Db db(&dbenv, 0); + int result = db.verify(strFile.c_str(), NULL, &strDump, flags); + if (result == DB_VERIFY_BAD) { + LogPrintf("CDBEnv::Salvage : Database salvage found errors, all data may not be recoverable.\n"); + if (!fAggressive) { + LogPrintf("CDBEnv::Salvage : Rerun with aggressive mode to ignore errors and continue.\n"); + return false; + } + } + if (result != 0 && result != DB_VERIFY_BAD) { + LogPrintf("CDBEnv::Salvage : Database salvage failed with result %d.\n", result); + return false; + } + + // Format of bdb dump is ascii lines: + // header lines... + // HEADER=END + // hexadecimal key + // hexadecimal value + // ... repeated + // DATA=END + + string strLine; + while (!strDump.eof() && strLine != "HEADER=END") + getline(strDump, strLine); // Skip past header + + std::string keyHex, valueHex; + while (!strDump.eof() && keyHex != "DATA=END") { + getline(strDump, keyHex); + if (keyHex != "DATA=END") { + getline(strDump, valueHex); + vResult.push_back(make_pair(ParseHex(keyHex), ParseHex(valueHex))); + } + } + + return (result == 0); +} + + +void CDBEnv::CheckpointLSN(const std::string& strFile) +{ + dbenv.txn_checkpoint(0, 0, 0); + if (fMockDb) + return; + dbenv.lsn_reset(strFile.c_str(), 0); +} + + +CDB::CDB(const std::string& strFilename, const char* pszMode) : pdb(NULL), activeTxn(NULL) +{ + int ret; + fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); + if (strFilename.empty()) + return; + + bool fCreate = strchr(pszMode, 'c') != NULL; + unsigned int nFlags = DB_THREAD; + if (fCreate) + nFlags |= DB_CREATE; + + { + LOCK(bitdb.cs_db); + if (!bitdb.Open(GetDataDir())) + throw runtime_error("CDB : Failed to open database environment."); + + strFile = strFilename; + ++bitdb.mapFileUseCount[strFile]; + pdb = bitdb.mapDb[strFile]; + if (pdb == NULL) { + pdb = new Db(&bitdb.dbenv, 0); + + bool fMockDb = bitdb.IsMock(); + if (fMockDb) { + DbMpoolFile* mpf = pdb->get_mpf(); + ret = mpf->set_flags(DB_MPOOL_NOFILE, 1); + if (ret != 0) + throw runtime_error(strprintf("CDB : Failed to configure for no temp file backing for database %s", strFile)); + } + + ret = pdb->open(NULL, // Txn pointer + fMockDb ? NULL : strFile.c_str(), // Filename + fMockDb ? strFile.c_str() : "main", // Logical db name + DB_BTREE, // Database type + nFlags, // Flags + 0); + + if (ret != 0) { + delete pdb; + pdb = NULL; + --bitdb.mapFileUseCount[strFile]; + strFile = ""; + throw runtime_error(strprintf("CDB : Error %d, can't open database %s", ret, strFile)); + } + + if (fCreate && !Exists(string("version"))) { + bool fTmp = fReadOnly; + fReadOnly = false; + WriteVersion(CLIENT_VERSION); + fReadOnly = fTmp; + } + + bitdb.mapDb[strFile] = pdb; + } + } +} + +void CDB::Flush() +{ + if (activeTxn) + return; + + // Flush database activity from memory pool to disk log + unsigned int nMinutes = 0; + if (fReadOnly) + nMinutes = 1; + + bitdb.dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100) * 1024 : 0, nMinutes, 0); +} + +void CDB::Close() +{ + if (!pdb) + return; + if (activeTxn) + activeTxn->abort(); + activeTxn = NULL; + pdb = NULL; + + Flush(); + + { + LOCK(bitdb.cs_db); + --bitdb.mapFileUseCount[strFile]; + } +} + +void CDBEnv::CloseDb(const string& strFile) +{ + { + LOCK(cs_db); + if (mapDb[strFile] != NULL) { + // Close the database handle + Db* pdb = mapDb[strFile]; + pdb->close(0); + delete pdb; + mapDb[strFile] = NULL; + } + } +} + +bool CDBEnv::RemoveDb(const string& strFile) +{ + this->CloseDb(strFile); + + LOCK(cs_db); + int rc = dbenv.dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT); + return (rc == 0); +} + +bool CDB::Rewrite(const string& strFile, const char* pszSkip) +{ + while (true) { + { + LOCK(bitdb.cs_db); + if (!bitdb.mapFileUseCount.count(strFile) || bitdb.mapFileUseCount[strFile] == 0) { + // Flush log data to the dat file + bitdb.CloseDb(strFile); + bitdb.CheckpointLSN(strFile); + bitdb.mapFileUseCount.erase(strFile); + + bool fSuccess = true; + LogPrintf("CDB::Rewrite : Rewriting %s...\n", strFile); + string strFileRes = strFile + ".rewrite"; + { // surround usage of db with extra {} + CDB db(strFile.c_str(), "r"); + Db* pdbCopy = new Db(&bitdb.dbenv, 0); + + int ret = pdbCopy->open(NULL, // Txn pointer + strFileRes.c_str(), // Filename + "main", // Logical db name + DB_BTREE, // Database type + DB_CREATE, // Flags + 0); + if (ret > 0) { + LogPrintf("CDB::Rewrite : Can't create database file %s\n", strFileRes); + fSuccess = false; + } + + Dbc* pcursor = db.GetCursor(); + if (pcursor) + while (fSuccess) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT); + if (ret == DB_NOTFOUND) { + pcursor->close(); + break; + } else if (ret != 0) { + pcursor->close(); + fSuccess = false; + break; + } + if (pszSkip && + strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0) + continue; + if (strncmp(&ssKey[0], "\x07version", 8) == 0) { + // Update version: + ssValue.clear(); + ssValue << CLIENT_VERSION; + } + Dbt datKey(&ssKey[0], ssKey.size()); + Dbt datValue(&ssValue[0], ssValue.size()); + int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE); + if (ret2 > 0) + fSuccess = false; + } + if (fSuccess) { + db.Close(); + bitdb.CloseDb(strFile); + if (pdbCopy->close(0)) + fSuccess = false; + delete pdbCopy; + } + } + if (fSuccess) { + Db dbA(&bitdb.dbenv, 0); + if (dbA.remove(strFile.c_str(), NULL, 0)) + fSuccess = false; + Db dbB(&bitdb.dbenv, 0); + if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0)) + fSuccess = false; + } + if (!fSuccess) + LogPrintf("CDB::Rewrite : Failed to rewrite database file %s\n", strFileRes); + return fSuccess; + } + } + MilliSleep(100); + } + return false; +} + + +void CDBEnv::Flush(bool fShutdown) +{ + int64_t nStart = GetTimeMillis(); + // Flush log data to the actual data file on all files that are not in use + LogPrint("db", "CDBEnv::Flush : Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started"); + if (!fDbEnvInit) + return; + { + LOCK(cs_db); + map::iterator mi = mapFileUseCount.begin(); + while (mi != mapFileUseCount.end()) { + string strFile = (*mi).first; + int nRefCount = (*mi).second; + LogPrint("db", "CDBEnv::Flush : Flushing %s (refcount = %d)...\n", strFile, nRefCount); + if (nRefCount == 0) { + // Move log data to the dat file + CloseDb(strFile); + LogPrint("db", "CDBEnv::Flush : %s checkpoint\n", strFile); + dbenv.txn_checkpoint(0, 0, 0); + LogPrint("db", "CDBEnv::Flush : %s detach\n", strFile); + if (!fMockDb) + dbenv.lsn_reset(strFile.c_str(), 0); + LogPrint("db", "CDBEnv::Flush : %s closed\n", strFile); + mapFileUseCount.erase(mi++); + } else + mi++; + } + LogPrint("db", "CDBEnv::Flush : Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", GetTimeMillis() - nStart); + if (fShutdown) { + char** listp; + if (mapFileUseCount.empty()) { + dbenv.log_archive(&listp, DB_ARCH_REMOVE); + Close(); + if (!fMockDb) + boost::filesystem::remove_all(boost::filesystem::path(strPath) / "database"); + } + } + } +} diff --git a/src/wallet/db.h b/src/wallet/db.h new file mode 100644 index 0000000..2128c04 --- /dev/null +++ b/src/wallet/db.h @@ -0,0 +1,314 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 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_DB_H +#define BITCOIN_DB_H + +#include "clientversion.h" +#include "serialize.h" +#include "streams.h" +#include "sync.h" +#include "version.h" + +#include +#include +#include + +#include + +#include + +class CDiskBlockIndex; +class COutPoint; + +struct CBlockLocator; + +extern unsigned int nWalletDBUpdated; + +void ThreadFlushWalletDB(const std::string& strWalletFile); + + +class CDBEnv +{ +private: + bool fDbEnvInit; + bool fMockDb; + // Don't change into boost::filesystem::path, as that can result in + // shutdown problems/crashes caused by a static initialized internal pointer. + std::string strPath; + + void EnvShutdown(); + +public: + mutable CCriticalSection cs_db; + DbEnv dbenv; + std::map mapFileUseCount; + std::map mapDb; + + CDBEnv(); + ~CDBEnv(); + void MakeMock(); + bool IsMock() { return fMockDb; } + + /** + * Verify that database file strFile is OK. If it is not, + * call the callback to try to recover. + * This must be called BEFORE strFile is opened. + * Returns true if strFile is OK. + */ + enum VerifyResult { VERIFY_OK, + RECOVER_OK, + RECOVER_FAIL }; + VerifyResult Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile)); + /** + * Salvage data from a file that Verify says is bad. + * fAggressive sets the DB_AGGRESSIVE flag (see berkeley DB->verify() method documentation). + * Appends binary key/value pairs to vResult, returns true if successful. + * NOTE: reads the entire database into memory, so cannot be used + * for huge databases. + */ + typedef std::pair, std::vector > KeyValPair; + bool Salvage(std::string strFile, bool fAggressive, std::vector& vResult); + + bool Open(const boost::filesystem::path& path); + void Close(); + void Flush(bool fShutdown); + void CheckpointLSN(const std::string& strFile); + + void CloseDb(const std::string& strFile); + bool RemoveDb(const std::string& strFile); + + DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC) + { + DbTxn* ptxn = NULL; + int ret = dbenv.txn_begin(NULL, &ptxn, flags); + if (!ptxn || ret != 0) + return NULL; + return ptxn; + } +}; + +extern CDBEnv bitdb; + + +/** RAII class that provides access to a Berkeley database */ +class CDB +{ +protected: + Db* pdb; + std::string strFile; + DbTxn* activeTxn; + bool fReadOnly; + + explicit CDB(const std::string& strFilename, const char* pszMode = "r+"); + ~CDB() { Close(); } + +public: + void Flush(); + void Close(); + +private: + CDB(const CDB&); + void operator=(const CDB&); + +protected: + template + bool Read(const K& key, T& value) + { + if (!pdb) + return false; + + // Key + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], ssKey.size()); + + // Read + Dbt datValue; + datValue.set_flags(DB_DBT_MALLOC); + int ret = pdb->get(activeTxn, &datKey, &datValue, 0); + memset(datKey.get_data(), 0, datKey.get_size()); + if (datValue.get_data() == NULL) + return false; + + // Unserialize value + try { + CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION); + ssValue >> value; + } catch (const std::exception&) { + return false; + } + + // Clear and free memory + memset(datValue.get_data(), 0, datValue.get_size()); + free(datValue.get_data()); + return (ret == 0); + } + + template + bool Write(const K& key, const T& value, bool fOverwrite = true) + { + if (!pdb) + return false; + if (fReadOnly) + assert(!"Write called on database in read-only mode"); + + // Key + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], ssKey.size()); + + // Value + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + ssValue.reserve(10000); + ssValue << value; + Dbt datValue(&ssValue[0], ssValue.size()); + + // Write + int ret = pdb->put(activeTxn, &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE)); + + // Clear memory in case it was a private key + memset(datKey.get_data(), 0, datKey.get_size()); + memset(datValue.get_data(), 0, datValue.get_size()); + return (ret == 0); + } + + template + bool Erase(const K& key) + { + if (!pdb) + return false; + if (fReadOnly) + assert(!"Erase called on database in read-only mode"); + + // Key + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], ssKey.size()); + + // Erase + int ret = pdb->del(activeTxn, &datKey, 0); + + // Clear memory + memset(datKey.get_data(), 0, datKey.get_size()); + return (ret == 0 || ret == DB_NOTFOUND); + } + + template + bool Exists(const K& key) + { + if (!pdb) + return false; + + // Key + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(1000); + ssKey << key; + Dbt datKey(&ssKey[0], ssKey.size()); + + // Exists + int ret = pdb->exists(activeTxn, &datKey, 0); + + // Clear memory + memset(datKey.get_data(), 0, datKey.get_size()); + return (ret == 0); + } + + Dbc* GetCursor() + { + if (!pdb) + return NULL; + Dbc* pcursor = NULL; + int ret = pdb->cursor(NULL, &pcursor, 0); + if (ret != 0) + return NULL; + return pcursor; + } + + int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, unsigned int fFlags = DB_NEXT) + { + // Read at cursor + Dbt datKey; + if (fFlags == DB_SET || fFlags == DB_SET_RANGE || fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) { + datKey.set_data(&ssKey[0]); + datKey.set_size(ssKey.size()); + } + Dbt datValue; + if (fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) { + datValue.set_data(&ssValue[0]); + datValue.set_size(ssValue.size()); + } + datKey.set_flags(DB_DBT_MALLOC); + datValue.set_flags(DB_DBT_MALLOC); + int ret = pcursor->get(&datKey, &datValue, fFlags); + if (ret != 0) + return ret; + else if (datKey.get_data() == NULL || datValue.get_data() == NULL) + return 99999; + + // Convert to streams + ssKey.SetType(SER_DISK); + ssKey.clear(); + ssKey.write((char*)datKey.get_data(), datKey.get_size()); + ssValue.SetType(SER_DISK); + ssValue.clear(); + ssValue.write((char*)datValue.get_data(), datValue.get_size()); + + // Clear and free memory + memset(datKey.get_data(), 0, datKey.get_size()); + memset(datValue.get_data(), 0, datValue.get_size()); + free(datKey.get_data()); + free(datValue.get_data()); + return 0; + } + +public: + bool TxnBegin() + { + if (!pdb || activeTxn) + return false; + DbTxn* ptxn = bitdb.TxnBegin(); + if (!ptxn) + return false; + activeTxn = ptxn; + return true; + } + + bool TxnCommit() + { + if (!pdb || !activeTxn) + return false; + int ret = activeTxn->commit(0); + activeTxn = NULL; + return (ret == 0); + } + + bool TxnAbort() + { + if (!pdb || !activeTxn) + return false; + int ret = activeTxn->abort(); + activeTxn = NULL; + return (ret == 0); + } + + bool ReadVersion(int& nVersion) + { + nVersion = 0; + return Read(std::string("version"), nVersion); + } + + bool WriteVersion(int nVersion) + { + return Write(std::string("version"), nVersion); + } + + bool static Rewrite(const std::string& strFile, const char* pszSkip = NULL); +}; + +#endif // BITCOIN_DB_H diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp new file mode 100644 index 0000000..8ce9dc5 --- /dev/null +++ b/src/wallet/rpcdump.cpp @@ -0,0 +1,491 @@ +// Copyright (c) 2009-2014 The Bitcoin developers +// Copyright (c) 2014-2015 The Dash developers +// Copyright (c) 2015-2017 The PIVX developers +// Copyright (c) 2017-2019 The XDNA Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "bip38.h" +#include "init.h" +#include "main.h" +#include "rpc/server.h" +#include "script/script.h" +#include "script/standard.h" +#include "sync.h" +#include "util.h" +#include "utilstrencodings.h" +#include "utiltime.h" +#include "wallet.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include + +using namespace std; + +void EnsureWalletIsUnlocked(); + +std::string static EncodeDumpTime(int64_t nTime) +{ + return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime); +} + +int64_t static DecodeDumpTime(const std::string& str) +{ + static const boost::posix_time::ptime epoch = boost::posix_time::from_time_t(0); + static const std::locale loc(std::locale::classic(), + new boost::posix_time::time_input_facet("%Y-%m-%dT%H:%M:%SZ")); + std::istringstream iss(str); + iss.imbue(loc); + boost::posix_time::ptime ptime(boost::date_time::not_a_date_time); + iss >> ptime; + if (ptime.is_not_a_date_time()) + return 0; + return (ptime - epoch).total_seconds(); +} + +std::string static EncodeDumpString(const std::string& str) +{ + std::stringstream ret; + BOOST_FOREACH (unsigned char c, str) { + if (c <= 32 || c >= 128 || c == '%') { + ret << '%' << HexStr(&c, &c + 1); + } else { + ret << c; + } + } + return ret.str(); +} + +std::string DecodeDumpString(const std::string& str) +{ + std::stringstream ret; + for (unsigned int pos = 0; pos < str.length(); pos++) { + unsigned char c = str[pos]; + if (c == '%' && pos + 2 < str.length()) { + c = (((str[pos + 1] >> 6) * 9 + ((str[pos + 1] - '0') & 15)) << 4) | + ((str[pos + 2] >> 6) * 9 + ((str[pos + 2] - '0') & 15)); + pos += 2; + } + ret << c; + } + return ret.str(); +} + +UniValue importprivkey(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 3) + throw runtime_error( + "importprivkey \"xdnaprivkey\" ( \"label\" rescan )\n" + "\nAdds a private key (as returned by dumpprivkey) to your wallet.\n" + "\nArguments:\n" + "1. \"xdnaprivkey\" (string, required) The private key (see dumpprivkey)\n" + "2. \"label\" (string, optional, default=\"\") An optional label\n" + "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n" + "\nNote: This call can take minutes to complete if rescan is true.\n" + "\nExamples:\n" + "\nDump a private key\n" + + HelpExampleCli("dumpprivkey", "\"myaddress\"") + + "\nImport the private key with rescan\n" + HelpExampleCli("importprivkey", "\"mykey\"") + + "\nImport using a label and without rescan\n" + HelpExampleCli("importprivkey", "\"mykey\" \"testing\" false") + + "\nAs a JSON-RPC call\n" + HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false")); + + EnsureWalletIsUnlocked(); + + string strSecret = params[0].get_str(); + string strLabel = ""; + if (params.size() > 1) + strLabel = params[1].get_str(); + + // Whether to perform rescan after import + bool fRescan = true; + if (params.size() > 2) + fRescan = params[2].get_bool(); + + CBitcoinSecret vchSecret; + bool fGood = vchSecret.SetString(strSecret); + + if (!fGood) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding"); + + CKey key = vchSecret.GetKey(); + if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range"); + + CPubKey pubkey = key.GetPubKey(); + assert(key.VerifyPubKey(pubkey)); + CKeyID vchAddress = pubkey.GetID(); + { + pwalletMain->MarkDirty(); + pwalletMain->SetAddressBook(vchAddress, strLabel, "receive"); + + // Don't throw error in case a key is already there + if (pwalletMain->HaveKey(vchAddress)) + return NullUniValue; + + pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = 1; + + if (!pwalletMain->AddKeyPubKey(key, pubkey)) + throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); + + // whenever a key is imported, we need to scan the whole chain + pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value' + + if (fRescan) { + pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true); + } + } + + return NullUniValue; +} + +UniValue importaddress(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 3) + throw runtime_error( + "importaddress \"address\" ( \"label\" rescan )\n" + "\nAdds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend.\n" + "\nArguments:\n" + "1. \"address\" (string, required) The address\n" + "2. \"label\" (string, optional, default=\"\") An optional label\n" + "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n" + "\nNote: This call can take minutes to complete if rescan is true.\n" + "\nExamples:\n" + "\nImport an address with rescan\n" + + HelpExampleCli("importaddress", "\"myaddress\"") + + "\nImport using a label without rescan\n" + HelpExampleCli("importaddress", "\"myaddress\" \"testing\" false") + + "\nAs a JSON-RPC call\n" + HelpExampleRpc("importaddress", "\"myaddress\", \"testing\", false")); + + CScript script; + + CBitcoinAddress address(params[0].get_str()); + if (address.IsValid()) { + script = GetScriptForDestination(address.Get()); + } else if (IsHex(params[0].get_str())) { + std::vector data(ParseHex(params[0].get_str())); + script = CScript(data.begin(), data.end()); + } else { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid XDNA address or script"); + } + + string strLabel = ""; + if (params.size() > 1) + strLabel = params[1].get_str(); + + // Whether to perform rescan after import + bool fRescan = true; + if (params.size() > 2) + fRescan = params[2].get_bool(); + + { + if (::IsMine(*pwalletMain, script) == ISMINE_SPENDABLE) + throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); + + // add to address book or update label + if (address.IsValid()) + pwalletMain->SetAddressBook(address.Get(), strLabel, "receive"); + + // Don't throw error in case an address is already there + if (pwalletMain->HaveWatchOnly(script)) + return NullUniValue; + + pwalletMain->MarkDirty(); + + if (!pwalletMain->AddWatchOnly(script)) + throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); + + if (fRescan) { + pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true); + pwalletMain->ReacceptWalletTransactions(); + } + } + + return NullUniValue; +} + +UniValue importwallet(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "importwallet \"filename\"\n" + "\nImports keys from a wallet dump file (see dumpwallet).\n" + "\nArguments:\n" + "1. \"filename\" (string, required) The wallet file\n" + "\nExamples:\n" + "\nDump the wallet\n" + + HelpExampleCli("dumpwallet", "\"test\"") + + "\nImport the wallet\n" + HelpExampleCli("importwallet", "\"test\"") + + "\nImport using the json rpc call\n" + HelpExampleRpc("importwallet", "\"test\"")); + + EnsureWalletIsUnlocked(); + + ifstream file; + file.open(params[0].get_str().c_str(), std::ios::in | std::ios::ate); + if (!file.is_open()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); + + int64_t nTimeBegin = chainActive.Tip()->GetBlockTime(); + + bool fGood = true; + + int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg()); + file.seekg(0, file.beg); + + pwalletMain->ShowProgress(_("Importing..."), 0); // show progress dialog in GUI + while (file.good()) { + pwalletMain->ShowProgress("", std::max(1, std::min(99, (int)(((double)file.tellg() / (double)nFilesize) * 100)))); + std::string line; + std::getline(file, line); + if (line.empty() || line[0] == '#') + continue; + + std::vector vstr; + boost::split(vstr, line, boost::is_any_of(" ")); + if (vstr.size() < 2) + continue; + CBitcoinSecret vchSecret; + if (!vchSecret.SetString(vstr[0])) + continue; + CKey key = vchSecret.GetKey(); + CPubKey pubkey = key.GetPubKey(); + assert(key.VerifyPubKey(pubkey)); + CKeyID keyid = pubkey.GetID(); + if (pwalletMain->HaveKey(keyid)) { + LogPrintf("Skipping import of %s (key already present)\n", CBitcoinAddress(keyid).ToString()); + continue; + } + int64_t nTime = DecodeDumpTime(vstr[1]); + std::string strLabel; + bool fLabel = true; + for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) { + if (boost::algorithm::starts_with(vstr[nStr], "#")) + break; + if (vstr[nStr] == "change=1") + fLabel = false; + if (vstr[nStr] == "reserve=1") + fLabel = false; + if (boost::algorithm::starts_with(vstr[nStr], "label=")) { + strLabel = DecodeDumpString(vstr[nStr].substr(6)); + fLabel = true; + } + } + LogPrintf("Importing %s...\n", CBitcoinAddress(keyid).ToString()); + if (!pwalletMain->AddKeyPubKey(key, pubkey)) { + fGood = false; + continue; + } + pwalletMain->mapKeyMetadata[keyid].nCreateTime = nTime; + if (fLabel) + pwalletMain->SetAddressBook(keyid, strLabel, "receive"); + nTimeBegin = std::min(nTimeBegin, nTime); + } + file.close(); + pwalletMain->ShowProgress("", 100); // hide progress dialog in GUI + + CBlockIndex* pindex = chainActive.Tip(); + while (pindex && pindex->pprev && pindex->GetBlockTime() > nTimeBegin - 7200) + pindex = pindex->pprev; + + if (!pwalletMain->nTimeFirstKey || nTimeBegin < pwalletMain->nTimeFirstKey) + pwalletMain->nTimeFirstKey = nTimeBegin; + + LogPrintf("Rescanning last %i blocks\n", chainActive.Height() - pindex->nHeight + 1); + pwalletMain->ScanForWalletTransactions(pindex); + pwalletMain->MarkDirty(); + + if (!fGood) + throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys to wallet"); + + return NullUniValue; +} + +UniValue dumpprivkey(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "dumpprivkey \"xdnaaddress\"\n" + "\nReveals the private key corresponding to 'xdnaaddress'.\n" + "Then the importprivkey can be used with this output\n" + "\nArguments:\n" + "1. \"xdnaaddress\" (string, required) The xdna address for the private key\n" + "\nResult:\n" + "\"key\" (string) The private key\n" + "\nExamples:\n" + + HelpExampleCli("dumpprivkey", "\"myaddress\"") + HelpExampleCli("importprivkey", "\"mykey\"") + HelpExampleRpc("dumpprivkey", "\"myaddress\"")); + + EnsureWalletIsUnlocked(); + + string strAddress = params[0].get_str(); + CBitcoinAddress address; + if (!address.SetString(strAddress)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid XDNA address"); + CKeyID keyID; + if (!address.GetKeyID(keyID)) + throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key"); + CKey vchSecret; + if (!pwalletMain->GetKey(keyID, vchSecret)) + throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known"); + return CBitcoinSecret(vchSecret).ToString(); +} + + +UniValue dumpwallet(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "dumpwallet \"filename\"\n" + "\nDumps all wallet keys in a human-readable format.\n" + "\nArguments:\n" + "1. \"filename\" (string, required) The filename\n" + "\nExamples:\n" + + HelpExampleCli("dumpwallet", "\"test\"") + HelpExampleRpc("dumpwallet", "\"test\"")); + + EnsureWalletIsUnlocked(); + + ofstream file; + file.open(params[0].get_str().c_str()); + if (!file.is_open()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); + + std::map mapKeyBirth; + std::set setKeyPool; + pwalletMain->GetKeyBirthTimes(mapKeyBirth); + pwalletMain->GetAllReserveKeys(setKeyPool); + + // sort time/key pairs + std::vector > vKeyBirth; + for (std::map::const_iterator it = mapKeyBirth.begin(); it != mapKeyBirth.end(); it++) { + vKeyBirth.push_back(std::make_pair(it->second, it->first)); + } + mapKeyBirth.clear(); + std::sort(vKeyBirth.begin(), vKeyBirth.end()); + + // produce output + file << strprintf("# Wallet dump created by XDNA %s (%s)\n", CLIENT_BUILD, CLIENT_DATE); + file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime())); + file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString()); + file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->GetBlockTime())); + file << "\n"; + for (std::vector >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) { + const CKeyID& keyid = it->second; + std::string strTime = EncodeDumpTime(it->first); + std::string strAddr = CBitcoinAddress(keyid).ToString(); + CKey key; + if (pwalletMain->GetKey(keyid, key)) { + if (pwalletMain->mapAddressBook.count(keyid)) { + file << strprintf("%s %s label=%s # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, EncodeDumpString(pwalletMain->mapAddressBook[keyid].name), strAddr); + } else if (setKeyPool.count(keyid)) { + file << strprintf("%s %s reserve=1 # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, strAddr); + } else { + file << strprintf("%s %s change=1 # addr=%s\n", CBitcoinSecret(key).ToString(), strTime, strAddr); + } + } + } + file << "\n"; + file << "# End of dump\n"; + file.close(); + return NullUniValue; +} + +UniValue bip38encrypt(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 2) + throw runtime_error( + "bip38encrypt \"xdnaaddress\"\n" + "\nEncrypts a private key corresponding to 'xdnaaddress'.\n" + "\nArguments:\n" + "1. \"xdnaaddress\" (string, required) The xdna address for the private key (you must hold the key already)\n" + "2. \"passphrase\" (string, required) The passphrase you want the private key to be encrypted with - Valid special chars: !#$%&'()*+,-./:;<=>?`{|}~ \n" + "\nResult:\n" + "\"key\" (string) The encrypted private key\n" + "\nExamples:\n"); + + EnsureWalletIsUnlocked(); + + string strAddress = params[0].get_str(); + string strPassphrase = params[1].get_str(); + + CBitcoinAddress address; + if (!address.SetString(strAddress)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid XDNA address"); + CKeyID keyID; + if (!address.GetKeyID(keyID)) + throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key"); + CKey vchSecret; + if (!pwalletMain->GetKey(keyID, vchSecret)) + throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known"); + + uint256 privKey = vchSecret.GetPrivKey_256(); + string encryptedOut = BIP38_Encrypt(strAddress, strPassphrase, privKey, vchSecret.IsCompressed()); + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("Addess", strAddress)); + result.push_back(Pair("Encrypted Key", encryptedOut)); + + return result; +} + +UniValue bip38decrypt(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 2) + throw runtime_error( + "bip38decrypt \"xdnaaddress\"\n" + "\nDecrypts and then imports password protected private key.\n" + "\nArguments:\n" + "1. \"passphrase\" (string, required) The passphrase you want the private key to be encrypted with\n" + "2. \"encryptedkey\" (string, required) The encrypted private key\n" + + "\nResult:\n" + "\"key\" (string) The decrypted private key\n" + "\nExamples:\n"); + + EnsureWalletIsUnlocked(); + + /** Collect private key and passphrase **/ + string strPassphrase = params[0].get_str(); + string strKey = params[1].get_str(); + + uint256 privKey; + bool fCompressed; + if (!BIP38_Decrypt(strPassphrase, strKey, privKey, fCompressed)) + throw JSONRPCError(RPC_WALLET_ERROR, "Failed To Decrypt"); + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("privatekey", HexStr(privKey))); + + CKey key; + key.Set(privKey.begin(), privKey.end(), fCompressed); + + if (!key.IsValid()) + throw JSONRPCError(RPC_WALLET_ERROR, "Private Key Not Valid"); + + CPubKey pubkey = key.GetPubKey(); + pubkey.IsCompressed(); + assert(key.VerifyPubKey(pubkey)); + result.push_back(Pair("Address", CBitcoinAddress(pubkey.GetID()).ToString())); + CKeyID vchAddress = pubkey.GetID(); + { + pwalletMain->MarkDirty(); + pwalletMain->SetAddressBook(vchAddress, "", "receive"); + + // Don't throw error in case a key is already there + if (pwalletMain->HaveKey(vchAddress)) + throw JSONRPCError(RPC_WALLET_ERROR, "Key already held by wallet"); + + pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = 1; + + if (!pwalletMain->AddKeyPubKey(key, pubkey)) + throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); + + // whenever a key is imported, we need to scan the whole chain + pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value' + pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true); + } + + return result; +} diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp new file mode 100644 index 0000000..3bcb9df --- /dev/null +++ b/src/wallet/rpcwallet.cpp @@ -0,0 +1,2315 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Copyright (c) 2014-2015 The Dash developers +// Copyright (c) 2015-2017 The PIVX developers +// Copyright (c) 2017-2019 The XDNA Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "amount.h" +#include "base58.h" +#include "core_io.h" +#include "init.h" +#include "net.h" +#include "netbase.h" +#include "rpc/server.h" +#include "timedata.h" +#include "util.h" +#include "utilmoneystr.h" +#include "wallet.h" +#include "walletdb.h" + +#include + +#include + +#include + +using namespace std; +using namespace boost; +using namespace boost::assign; + +int64_t nWalletUnlockTime; +static CCriticalSection cs_nWalletUnlockTime; + +std::string HelpRequiringPassphrase() +{ + return pwalletMain && pwalletMain->IsCrypted() ? "\nRequires wallet passphrase to be set with walletpassphrase call." : ""; +} + +void EnsureWalletIsUnlocked() +{ + if (pwalletMain->IsLocked() || pwalletMain->fWalletUnlockAnonymizeOnly) + throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); +} + +void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) +{ + int confirms = wtx.GetDepthInMainChain(false); + int confirmsTotal = GetIXConfirmations(wtx.GetHash()) + confirms; + entry.push_back(Pair("confirmations", confirmsTotal)); + entry.push_back(Pair("bcconfirmations", confirms)); + if (wtx.IsCoinBase() || wtx.IsCoinStake()) + entry.push_back(Pair("generated", true)); + if (confirms > 0) { + entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex())); + entry.push_back(Pair("blockindex", wtx.nIndex)); + entry.push_back(Pair("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime())); + } + uint256 hash = wtx.GetHash(); + entry.push_back(Pair("txid", hash.GetHex())); + UniValue conflicts(UniValue::VARR); + BOOST_FOREACH (const uint256& conflict, wtx.GetConflicts()) + conflicts.push_back(conflict.GetHex()); + entry.push_back(Pair("walletconflicts", conflicts)); + entry.push_back(Pair("time", wtx.GetTxTime())); + entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived)); + BOOST_FOREACH (const PAIRTYPE(string, string) & item, wtx.mapValue) + entry.push_back(Pair(item.first, item.second)); +} + +string AccountFromValue(const UniValue& value) +{ + string strAccount = value.get_str(); + if (strAccount == "*") + throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Invalid account name"); + return strAccount; +} + +UniValue getnewaddress(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getnewaddress ( \"account\" )\n" + "\nReturns a new XDNA address for receiving payments.\n" + "If 'account' is specified (recommended), it is added to the address book \n" + "so payments received with the address will be credited to 'account'.\n" + "\nArguments:\n" + "1. \"account\" (string, optional) The account name for the address to be linked to. if not provided, the default account \"\" is used. It can also be set to the empty string \"\" to represent the default account. The account does not need to exist, it will be created if there is no account by the given name.\n" + "\nResult:\n" + "\"xdnaaddress\" (string) The new xdna address\n" + "\nExamples:\n" + + HelpExampleCli("getnewaddress", "") + HelpExampleCli("getnewaddress", "\"\"") + HelpExampleCli("getnewaddress", "\"myaccount\"") + HelpExampleRpc("getnewaddress", "\"myaccount\"")); + + // Parse the account first so we don't generate a key if there's an error + string strAccount; + if (params.size() > 0) + strAccount = AccountFromValue(params[0]); + + if (!pwalletMain->IsLocked()) + pwalletMain->TopUpKeyPool(); + + // Generate a new key that is added to wallet + CPubKey newKey; + if (!pwalletMain->GetKeyFromPool(newKey)) + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + CKeyID keyID = newKey.GetID(); + + pwalletMain->SetAddressBook(keyID, strAccount, "receive"); + + return CBitcoinAddress(keyID).ToString(); +} + + +CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew = false) +{ + CWalletDB walletdb(pwalletMain->strWalletFile); + + CAccount account; + walletdb.ReadAccount(strAccount, account); + + bool bKeyUsed = false; + + // Check if the current key has been used + if (account.vchPubKey.IsValid()) { + CScript scriptPubKey = GetScriptForDestination(account.vchPubKey.GetID()); + for (map::iterator it = pwalletMain->mapWallet.begin(); + it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid(); + ++it) { + const CWalletTx& wtx = (*it).second; + BOOST_FOREACH (const CTxOut& txout, wtx.vout) + if (txout.scriptPubKey == scriptPubKey) + bKeyUsed = true; + } + } + + // Generate a new key + if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed) { + if (!pwalletMain->GetKeyFromPool(account.vchPubKey)) + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + + pwalletMain->SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive"); + walletdb.WriteAccount(strAccount, account); + } + + return CBitcoinAddress(account.vchPubKey.GetID()); +} + +UniValue getaccountaddress(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaccountaddress \"account\"\n" + "\nReturns the current XDNA address for receiving payments to this account.\n" + "\nArguments:\n" + "1. \"account\" (string, required) The account name for the address. It can also be set to the empty string \"\" to represent the default account. The account does not need to exist, it will be created and a new address created if there is no account by the given name.\n" + "\nResult:\n" + "\"xdnaaddress\" (string) The account xdna address\n" + "\nExamples:\n" + + HelpExampleCli("getaccountaddress", "") + HelpExampleCli("getaccountaddress", "\"\"") + HelpExampleCli("getaccountaddress", "\"myaccount\"") + HelpExampleRpc("getaccountaddress", "\"myaccount\"")); + + // Parse the account first so we don't generate a key if there's an error + string strAccount = AccountFromValue(params[0]); + + UniValue ret(UniValue::VSTR); + + ret = GetAccountAddress(strAccount).ToString(); + + return ret; +} + + +UniValue getrawchangeaddress(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getrawchangeaddress\n" + "\nReturns a new XDNA address, for receiving change.\n" + "This is for use with raw transactions, NOT normal use.\n" + "\nResult:\n" + "\"address\" (string) The address\n" + "\nExamples:\n" + + HelpExampleCli("getrawchangeaddress", "") + HelpExampleRpc("getrawchangeaddress", "")); + + if (!pwalletMain->IsLocked()) + pwalletMain->TopUpKeyPool(); + + CReserveKey reservekey(pwalletMain); + CPubKey vchPubKey; + if (!reservekey.GetReservedKey(vchPubKey)) + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); + + reservekey.KeepKey(); + + CKeyID keyID = vchPubKey.GetID(); + + return CBitcoinAddress(keyID).ToString(); +} + + +UniValue setaccount(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "setaccount \"xdnaaddress\" \"account\"\n" + "\nSets the account associated with the given address.\n" + "\nArguments:\n" + "1. \"xdnaaddress\" (string, required) The xdna address to be associated with an account.\n" + "2. \"account\" (string, required) The account to assign the address to.\n" + "\nExamples:\n" + + HelpExampleCli("setaccount", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\" \"tabby\"") + HelpExampleRpc("setaccount", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\", \"tabby\"")); + + CBitcoinAddress address(params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid XDNA address"); + + + string strAccount; + if (params.size() > 1) + strAccount = AccountFromValue(params[1]); + + // Only add the account if the address is yours. + if (IsMine(*pwalletMain, address.Get())) { + // Detect when changing the account of an address that is the 'unused current key' of another account: + if (pwalletMain->mapAddressBook.count(address.Get())) { + string strOldAccount = pwalletMain->mapAddressBook[address.Get()].name; + if (address == GetAccountAddress(strOldAccount)) + GetAccountAddress(strOldAccount, true); + } + pwalletMain->SetAddressBook(address.Get(), strAccount, "receive"); + } else + throw JSONRPCError(RPC_MISC_ERROR, "setaccount can only be used with own address"); + + return NullUniValue; +} + + +UniValue getaccount(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaccount \"xdnaaddress\"\n" + "\nReturns the account associated with the given address.\n" + "\nArguments:\n" + "1. \"xdnaaddress\" (string, required) The xdna address for account lookup.\n" + "\nResult:\n" + "\"accountname\" (string) the account address\n" + "\nExamples:\n" + + HelpExampleCli("getaccount", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\"") + HelpExampleRpc("getaccount", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\"")); + + CBitcoinAddress address(params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid XDNA address"); + + string strAccount; + map::iterator mi = pwalletMain->mapAddressBook.find(address.Get()); + if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.name.empty()) + strAccount = (*mi).second.name; + return strAccount; +} + + +UniValue getaddressesbyaccount(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getaddressesbyaccount \"account\"\n" + "\nReturns the list of addresses for the given account.\n" + "\nArguments:\n" + "1. \"account\" (string, required) The account name.\n" + "\nResult:\n" + "[ (json array of string)\n" + " \"xdnaaddress\" (string) a xdna address associated with the given account\n" + " ,...\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("getaddressesbyaccount", "\"tabby\"") + HelpExampleRpc("getaddressesbyaccount", "\"tabby\"")); + + string strAccount = AccountFromValue(params[0]); + + // Find all addresses that have the given account + UniValue ret(UniValue::VARR); + BOOST_FOREACH (const PAIRTYPE(CBitcoinAddress, CAddressBookData) & item, pwalletMain->mapAddressBook) { + const CBitcoinAddress& address = item.first; + const string& strName = item.second.name; + if (strName == strAccount) + ret.push_back(address.ToString()); + } + return ret; +} + +void SendMoney(const CTxDestination& address, CAmount nValue, CWalletTx& wtxNew, bool fUseIX = false) +{ + // Check amount + if (nValue <= 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); + + if (nValue > pwalletMain->GetBalance()) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + + string strError; + if (pwalletMain->IsLocked()) { + strError = "Error: Wallet locked, unable to create transaction!"; + LogPrintf("SendMoney() : %s", strError); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + + // Parse XDNA address + CScript scriptPubKey = GetScriptForDestination(address); + + // Create and send the transaction + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired; + if (!pwalletMain->CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired, strError, NULL, ALL_COINS, fUseIX, (CAmount)0)) { + if (nValue + nFeeRequired > pwalletMain->GetBalance()) + strError = strprintf("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!", FormatMoney(nFeeRequired)); + LogPrintf("SendMoney() : %s\n", strError); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + if (!pwalletMain->CommitTransaction(wtxNew, reservekey, (!fUseIX ? "tx" : "ix"))) + throw JSONRPCError(RPC_WALLET_ERROR, "Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); +} + +UniValue sendtoaddress(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 4) + throw runtime_error( + "sendtoaddress \"xdnaaddress\" amount ( \"comment\" \"comment-to\" )\n" + "\nSend an amount to a given address. The amount is a real and is rounded to the nearest 0.00000001\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"xdnaaddress\" (string, required) The xdna address to send to.\n" + "2. \"amount\" (numeric, required) The amount in btc to send. eg 0.1\n" + "3. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" + " This is not part of the transaction, just kept in your wallet.\n" + "4. \"comment-to\" (string, optional) A comment to store the name of the person or organization \n" + " to which you're sending the transaction. This is not part of the \n" + " transaction, just kept in your wallet.\n" + "\nResult:\n" + "\"transactionid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("sendtoaddress", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\" 0.1") + HelpExampleCli("sendtoaddress", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\" 0.1 \"donation\" \"seans outpost\"") + HelpExampleRpc("sendtoaddress", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\", 0.1, \"donation\", \"seans outpost\"")); + + CBitcoinAddress address(params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid XDNA address"); + + // Amount + CAmount nAmount = AmountFromValue(params[1]); + + // Wallet comments + CWalletTx wtx; + if (params.size() > 2 && !params[2].isNull() && !params[2].get_str().empty()) + wtx.mapValue["comment"] = params[2].get_str(); + if (params.size() > 3 && !params[3].isNull() && !params[3].get_str().empty()) + wtx.mapValue["to"] = params[3].get_str(); + + EnsureWalletIsUnlocked(); + + SendMoney(address.Get(), nAmount, wtx); + + return wtx.GetHash().GetHex(); +} + +UniValue sendtoaddressix(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 4) + throw runtime_error( + "sendtoaddressix \"xdnaaddress\" amount ( \"comment\" \"comment-to\" )\n" + "\nSend an amount to a given address. The amount is a real and is rounded to the nearest 0.00000001\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"xdnaaddress\" (string, required) The xdna address to send to.\n" + "2. \"amount\" (numeric, required) The amount in btc to send. eg 0.1\n" + "3. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" + " This is not part of the transaction, just kept in your wallet.\n" + "4. \"comment-to\" (string, optional) A comment to store the name of the person or organization \n" + " to which you're sending the transaction. This is not part of the \n" + " transaction, just kept in your wallet.\n" + "\nResult:\n" + "\"transactionid\" (string) The transaction id.\n" + "\nExamples:\n" + + HelpExampleCli("sendtoaddressix", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\" 0.1") + HelpExampleCli("sendtoaddressix", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\" 0.1 \"donation\" \"seans outpost\"") + HelpExampleRpc("sendtoaddressix", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\", 0.1, \"donation\", \"seans outpost\"")); + + CBitcoinAddress address(params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid XDNA address"); + + // Amount + CAmount nAmount = AmountFromValue(params[1]); + + // Wallet comments + CWalletTx wtx; + if (params.size() > 2 && !params[2].isNull() && !params[2].get_str().empty()) + wtx.mapValue["comment"] = params[2].get_str(); + if (params.size() > 3 && !params[3].isNull() && !params[3].get_str().empty()) + wtx.mapValue["to"] = params[3].get_str(); + + EnsureWalletIsUnlocked(); + + SendMoney(address.Get(), nAmount, wtx, true); + + return wtx.GetHash().GetHex(); +} + +UniValue listaddressgroupings(const UniValue& params, bool fHelp) +{ + if (fHelp) + throw runtime_error( + "listaddressgroupings\n" + "\nLists groups of addresses which have had their common ownership\n" + "made public by common use as inputs or as the resulting change\n" + "in past transactions\n" + "\nResult:\n" + "[\n" + " [\n" + " [\n" + " \"xdnaaddress\", (string) The xdna address\n" + " amount, (numeric) The amount in btc\n" + " \"account\" (string, optional) The account\n" + " ]\n" + " ,...\n" + " ]\n" + " ,...\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("listaddressgroupings", "") + HelpExampleRpc("listaddressgroupings", "")); + + UniValue jsonGroupings(UniValue::VARR); + map balances = pwalletMain->GetAddressBalances(); + BOOST_FOREACH (set grouping, pwalletMain->GetAddressGroupings()) { + UniValue jsonGrouping(UniValue::VARR); + BOOST_FOREACH (CTxDestination address, grouping) { + UniValue addressInfo(UniValue::VARR); + addressInfo.push_back(CBitcoinAddress(address).ToString()); + addressInfo.push_back(ValueFromAmount(balances[address])); + { + LOCK(pwalletMain->cs_wallet); + if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end()) + addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second.name); + } + jsonGrouping.push_back(addressInfo); + } + jsonGroupings.push_back(jsonGrouping); + } + return jsonGroupings; +} + +UniValue signmessage(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 2) + throw runtime_error( + "signmessage \"xdnaaddress\" \"message\"\n" + "\nSign a message with the private key of an address" + + HelpRequiringPassphrase() + "\n" + "\nArguments:\n" + "1. \"xdnaaddress\" (string, required) The xdna address to use for the private key.\n" + "2. \"message\" (string, required) The message to create a signature of.\n" + "\nResult:\n" + "\"signature\" (string) The signature of the message encoded in base 64\n" + "\nExamples:\n" + "\nUnlock the wallet for 30 seconds\n" + + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + + "\nCreate the signature\n" + HelpExampleCli("signmessage", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\" \"my message\"") + + "\nVerify the signature\n" + HelpExampleCli("verifymessage", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\" \"signature\" \"my message\"") + + "\nAs json rpc\n" + HelpExampleRpc("signmessage", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\", \"my message\"")); + + EnsureWalletIsUnlocked(); + + string strAddress = params[0].get_str(); + string strMessage = params[1].get_str(); + + CBitcoinAddress addr(strAddress); + if (!addr.IsValid()) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); + + CKeyID keyID; + if (!addr.GetKeyID(keyID)) + throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); + + CKey key; + if (!pwalletMain->GetKey(keyID, key)) + throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); + + CHashWriter ss(SER_GETHASH, 0); + ss << strMessageMagic; + ss << strMessage; + + vector vchSig; + if (!key.SignCompact(ss.GetHash(), vchSig)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); + + return EncodeBase64(&vchSig[0], vchSig.size()); +} + +UniValue getreceivedbyaddress(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getreceivedbyaddress \"xdnaaddress\" ( minconf )\n" + "\nReturns the total amount received by the given xdnaaddress in transactions with at least minconf confirmations.\n" + "\nArguments:\n" + "1. \"xdnaaddress\" (string, required) The xdna address for transactions.\n" + "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" + "\nResult:\n" + "amount (numeric) The total amount in btc received at this address.\n" + "\nExamples:\n" + "\nThe amount from transactions with at least 1 confirmation\n" + + HelpExampleCli("getreceivedbyaddress", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\"") + + "\nThe amount including unconfirmed transactions, zero confirmations\n" + HelpExampleCli("getreceivedbyaddress", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\" 0") + + "\nThe amount with at least 6 confirmation, very safe\n" + HelpExampleCli("getreceivedbyaddress", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\" 6") + + "\nAs a json rpc call\n" + HelpExampleRpc("getreceivedbyaddress", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\", 6")); + + // xdna address + CBitcoinAddress address = CBitcoinAddress(params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid XDNA address"); + CScript scriptPubKey = GetScriptForDestination(address.Get()); + if (!IsMine(*pwalletMain, scriptPubKey)) + return (double)0.0; + + // Minimum confirmations + int nMinDepth = 1; + if (params.size() > 1) + nMinDepth = params[1].get_int(); + + // Tally + CAmount nAmount = 0; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || !IsFinalTx(wtx)) + continue; + + BOOST_FOREACH (const CTxOut& txout, wtx.vout) + if (txout.scriptPubKey == scriptPubKey) + if (wtx.GetDepthInMainChain() >= nMinDepth) + nAmount += txout.nValue; + } + + return ValueFromAmount(nAmount); +} + + +UniValue getreceivedbyaccount(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getreceivedbyaccount \"account\" ( minconf )\n" + "\nReturns the total amount received by addresses with in transactions with at least [minconf] confirmations.\n" + "\nArguments:\n" + "1. \"account\" (string, required) The selected account, may be the default account using \"\".\n" + "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" + "\nResult:\n" + "amount (numeric) The total amount in btc received for this account.\n" + "\nExamples:\n" + "\nAmount received by the default account with at least 1 confirmation\n" + + HelpExampleCli("getreceivedbyaccount", "\"\"") + + "\nAmount received at the tabby account including unconfirmed amounts with zero confirmations\n" + HelpExampleCli("getreceivedbyaccount", "\"tabby\" 0") + + "\nThe amount with at least 6 confirmation, very safe\n" + HelpExampleCli("getreceivedbyaccount", "\"tabby\" 6") + + "\nAs a json rpc call\n" + HelpExampleRpc("getreceivedbyaccount", "\"tabby\", 6")); + + // Minimum confirmations + int nMinDepth = 1; + if (params.size() > 1) + nMinDepth = params[1].get_int(); + + // Get the set of pub keys assigned to account + string strAccount = AccountFromValue(params[0]); + set setAddress = pwalletMain->GetAccountAddresses(strAccount); + + // Tally + CAmount nAmount = 0; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { + const CWalletTx& wtx = (*it).second; + if (wtx.IsCoinBase() || !IsFinalTx(wtx)) + continue; + + BOOST_FOREACH (const CTxOut& txout, wtx.vout) { + CTxDestination address; + if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address)) + if (wtx.GetDepthInMainChain() >= nMinDepth) + nAmount += txout.nValue; + } + } + + return (double)nAmount / (double)COIN; +} + + +CAmount GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth, const isminefilter& filter) +{ + CAmount nBalance = 0; + + // Tally wallet transactions + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { + const CWalletTx& wtx = (*it).second; + if (!IsFinalTx(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; +} + +CAmount GetAccountBalance(const string& strAccount, int nMinDepth, const isminefilter& filter) +{ + CWalletDB walletdb(pwalletMain->strWalletFile); + return GetAccountBalance(walletdb, strAccount, nMinDepth, filter); +} + + +UniValue getbalance(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 3) + throw runtime_error( + "getbalance ( \"account\" minconf includeWatchonly )\n" + "\nIf account is not specified, returns the server's total available balance.\n" + "If account is specified, returns the balance in the account.\n" + "Note that the account \"\" is not the same as leaving the parameter out.\n" + "The server total may be different to the balance in the default \"\" account.\n" + "\nArguments:\n" + "1. \"account\" (string, optional) The selected account, or \"*\" for entire wallet. It may be the default account using \"\".\n" + "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" + "3. includeWatchonly (bool, optional, default=false) Also include balance in watchonly addresses (see 'importaddress')\n" + "\nResult:\n" + "amount (numeric) The total amount in btc received for this account.\n" + "\nExamples:\n" + "\nThe total amount in the server across all accounts\n" + + HelpExampleCli("getbalance", "") + + "\nThe total amount in the server across all accounts, with at least 5 confirmations\n" + HelpExampleCli("getbalance", "\"*\" 6") + + "\nThe total amount in the default account with at least 1 confirmation\n" + HelpExampleCli("getbalance", "\"\"") + + "\nThe total amount in the account named tabby with at least 6 confirmations\n" + HelpExampleCli("getbalance", "\"tabby\" 6") + + "\nAs a json rpc call\n" + HelpExampleRpc("getbalance", "\"tabby\", 6")); + + if (params.size() == 0) + return ValueFromAmount(pwalletMain->GetBalance()); + + int nMinDepth = 1; + if (params.size() > 1) + nMinDepth = params[1].get_int(); + isminefilter filter = ISMINE_SPENDABLE; + if (params.size() > 2) + if (params[2].get_bool()) + filter = filter | ISMINE_WATCH_ONLY; + + if (params[0].get_str() == "*") { + // Calculate total balance a different way from GetBalance() + // (GetBalance() sums up all unspent TxOuts) + // getbalance and "getbalance * 1 true" should return the same number + CAmount nBalance = 0; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { + const CWalletTx& wtx = (*it).second; + if (!IsFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) + continue; + + CAmount allFee; + string strSentAccount; + list listReceived; + list listSent; + wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); + if (wtx.GetDepthInMainChain() >= nMinDepth) { + BOOST_FOREACH (const COutputEntry& r, listReceived) + nBalance += r.amount; + } + BOOST_FOREACH (const COutputEntry& s, listSent) + nBalance -= s.amount; + nBalance -= allFee; + } + return ValueFromAmount(nBalance); + } + + string strAccount = AccountFromValue(params[0]); + + CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, filter); + + return ValueFromAmount(nBalance); +} + +UniValue getunconfirmedbalance(const UniValue ¶ms, bool fHelp) +{ + if (fHelp || params.size() > 0) + throw runtime_error( + "getunconfirmedbalance\n" + "Returns the server's total unconfirmed balance\n"); + return ValueFromAmount(pwalletMain->GetUnconfirmedBalance()); +} + + +UniValue movecmd(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 3 || params.size() > 5) + throw runtime_error( + "move \"fromaccount\" \"toaccount\" amount ( minconf \"comment\" )\n" + "\nMove a specified amount from one account in your wallet to another.\n" + "\nArguments:\n" + "1. \"fromaccount\" (string, required) The name of the account to move funds from. May be the default account using \"\".\n" + "2. \"toaccount\" (string, required) The name of the account to move funds to. May be the default account using \"\".\n" + "3. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n" + "4. \"comment\" (string, optional) An optional comment, stored in the wallet only.\n" + "\nResult:\n" + "true|false (boolean) true if successfull.\n" + "\nExamples:\n" + "\nMove 0.01 btc from the default account to the account named tabby\n" + + HelpExampleCli("move", "\"\" \"tabby\" 0.01") + + "\nMove 0.01 btc timotei to akiko with a comment and funds have 6 confirmations\n" + HelpExampleCli("move", "\"timotei\" \"akiko\" 0.01 6 \"happy birthday!\"") + + "\nAs a json rpc call\n" + HelpExampleRpc("move", "\"timotei\", \"akiko\", 0.01, 6, \"happy birthday!\"")); + + string strFrom = AccountFromValue(params[0]); + string strTo = AccountFromValue(params[1]); + CAmount nAmount = AmountFromValue(params[2]); + if (params.size() > 3) + // unused parameter, used to be nMinDepth, keep type-checking it though + (void)params[3].get_int(); + string strComment; + if (params.size() > 4) + strComment = params[4].get_str(); + + CWalletDB walletdb(pwalletMain->strWalletFile); + if (!walletdb.TxnBegin()) + throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); + + int64_t nNow = GetAdjustedTime(); + + // Debit + CAccountingEntry debit; + debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb); + debit.strAccount = strFrom; + debit.nCreditDebit = -nAmount; + debit.nTime = nNow; + debit.strOtherAccount = strTo; + debit.strComment = strComment; + walletdb.WriteAccountingEntry(debit); + + // Credit + CAccountingEntry credit; + credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb); + credit.strAccount = strTo; + credit.nCreditDebit = nAmount; + credit.nTime = nNow; + credit.strOtherAccount = strFrom; + credit.strComment = strComment; + walletdb.WriteAccountingEntry(credit); + + if (!walletdb.TxnCommit()) + throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); + + return true; +} + + +UniValue sendfrom(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 3 || params.size() > 6) + throw runtime_error( + "sendfrom \"fromaccount\" \"toxdnaaddress\" amount ( minconf \"comment\" \"comment-to\" )\n" + "\nSent an amount from an account to a xdna address.\n" + "The amount is a real and is rounded to the nearest 0.00000001." + + HelpRequiringPassphrase() + "\n" + "\nArguments:\n" + "1. \"fromaccount\" (string, required) The name of the account to send funds from. May be the default account using \"\".\n" + "2. \"toxdnaaddress\" (string, required) The xdna address to send funds to.\n" + "3. amount (numeric, required) The amount in btc. (transaction fee is added on top).\n" + "4. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n" + "5. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" + " This is not part of the transaction, just kept in your wallet.\n" + "6. \"comment-to\" (string, optional) An optional comment to store the name of the person or organization \n" + " to which you're sending the transaction. This is not part of the transaction, \n" + " it is just kept in your wallet.\n" + "\nResult:\n" + "\"transactionid\" (string) The transaction id.\n" + "\nExamples:\n" + "\nSend 0.01 btc from the default account to the address, must have at least 1 confirmation\n" + + HelpExampleCli("sendfrom", "\"\" \"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\" 0.01") + + "\nSend 0.01 from the tabby account to the given address, funds must have at least 6 confirmations\n" + HelpExampleCli("sendfrom", "\"tabby\" \"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\" 0.01 6 \"donation\" \"seans outpost\"") + + "\nAs a json rpc call\n" + HelpExampleRpc("sendfrom", "\"tabby\", \"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\", 0.01, 6, \"donation\", \"seans outpost\"")); + + string strAccount = AccountFromValue(params[0]); + CBitcoinAddress address(params[1].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid XDNA address"); + CAmount nAmount = AmountFromValue(params[2]); + int nMinDepth = 1; + if (params.size() > 3) + nMinDepth = params[3].get_int(); + + CWalletTx wtx; + wtx.strFromAccount = strAccount; + if (params.size() > 4 && !params[4].isNull() && !params[4].get_str().empty()) + wtx.mapValue["comment"] = params[4].get_str(); + if (params.size() > 5 && !params[5].isNull() && !params[5].get_str().empty()) + wtx.mapValue["to"] = params[5].get_str(); + + EnsureWalletIsUnlocked(); + + // Check funds + CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); + if (nAmount > nBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); + + SendMoney(address.Get(), nAmount, wtx); + + return wtx.GetHash().GetHex(); +} + + +UniValue sendmany(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 4) + throw runtime_error( + "sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" )\n" + "\nSend multiple times. Amounts are double-precision floating point numbers." + + HelpRequiringPassphrase() + "\n" + "\nArguments:\n" + "1. \"fromaccount\" (string, required) The account to send the funds from, can be \"\" for the default account\n" + "2. \"amounts\" (string, required) A json object with addresses and amounts\n" + " {\n" + " \"address\":amount (numeric) The xdna address is the key, the numeric amount in btc is the value\n" + " ,...\n" + " }\n" + "3. minconf (numeric, optional, default=1) Only use the balance confirmed at least this many times.\n" + "4. \"comment\" (string, optional) A comment\n" + "\nResult:\n" + "\"transactionid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n" + " the number of addresses.\n" + "\nExamples:\n" + "\nSend two amounts to two different addresses:\n" + + HelpExampleCli("sendmany", "\"tabby\" \"{\\\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\\\":0.01,\\\"XuQQkwA4FYkq2XERzMY2CiAZhJTEDAbtcg\\\":0.02}\"") + + "\nSend two amounts to two different addresses setting the confirmation and comment:\n" + HelpExampleCli("sendmany", "\"tabby\" \"{\\\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\\\":0.01,\\\"XuQQkwA4FYkq2XERzMY2CiAZhJTEDAbtcg\\\":0.02}\" 6 \"testing\"") + + "\nAs a json rpc call\n" + HelpExampleRpc("sendmany", "\"tabby\", \"{\\\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\\\":0.01,\\\"XuQQkwA4FYkq2XERzMY2CiAZhJTEDAbtcg\\\":0.02}\", 6, \"testing\"")); + + string strAccount = AccountFromValue(params[0]); + UniValue sendTo = params[1].get_obj(); + int nMinDepth = 1; + if (params.size() > 2) + nMinDepth = params[2].get_int(); + + CWalletTx wtx; + wtx.strFromAccount = strAccount; + if (params.size() > 3 && !params[3].isNull() && !params[3].get_str().empty()) + wtx.mapValue["comment"] = params[3].get_str(); + + set setAddress; + vector > vecSend; + + CAmount totalAmount = 0; + vector keys = sendTo.getKeys(); + for (const string& name_ : keys) { + CBitcoinAddress address(name_); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid XDNA address: ")+name_); + + if (setAddress.count(address)) + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_); + setAddress.insert(address); + + CScript scriptPubKey = GetScriptForDestination(address.Get()); + CAmount nAmount = AmountFromValue(sendTo[name_]); + totalAmount += nAmount; + + vecSend.push_back(make_pair(scriptPubKey, nAmount)); + } + + EnsureWalletIsUnlocked(); + + // Check funds + CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); + if (totalAmount > nBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); + + // Send + CReserveKey keyChange(pwalletMain); + CAmount nFeeRequired = 0; + string strFailReason; + bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, strFailReason); + if (!fCreated) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); + if (!pwalletMain->CommitTransaction(wtx, keyChange)) + throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed"); + + return wtx.GetHash().GetHex(); +} + +// Defined in rpcmisc.cpp +extern CScript _createmultisig_redeemScript(const UniValue& params); + +UniValue addmultisigaddress(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 3) { + string msg = "addmultisigaddress nrequired [\"key\",...] ( \"account\" )\n" + "\nAdd a nrequired-to-sign multisignature address to the wallet.\n" + "Each key is a XDNA address or hex-encoded public key.\n" + "If 'account' is specified, assign address to that account.\n" + + "\nArguments:\n" + "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n" + "2. \"keysobject\" (string, required) A json array of xdna addresses or hex-encoded public keys\n" + " [\n" + " \"address\" (string) xdna address or hex-encoded public key\n" + " ...,\n" + " ]\n" + "3. \"account\" (string, optional) An account to assign the addresses to.\n" + + "\nResult:\n" + "\"xdnaaddress\" (string) A xdna address associated with the keys.\n" + + "\nExamples:\n" + "\nAdd a multisig address from 2 addresses\n" + + HelpExampleCli("addmultisigaddress", "2 \"[\\\"Xt4qk9uKvQYAonVGSZNXqxeDmtjaEWgfrs\\\",\\\"XoSoWQkpgLpppPoyyzbUFh1fq2RBvW6UK1\\\"]\"") + + "\nAs json rpc call\n" + HelpExampleRpc("addmultisigaddress", "2, \"[\\\"Xt4qk9uKvQYAonVGSZNXqxeDmtjaEWgfrs\\\",\\\"XoSoWQkpgLpppPoyyzbUFh1fq2RBvW6UK1\\\"]\""); + throw runtime_error(msg); + } + + string strAccount; + if (params.size() > 2) + strAccount = AccountFromValue(params[2]); + + // Construct using pay-to-script-hash: + CScript inner = _createmultisig_redeemScript(params); + CScriptID innerID(inner); + pwalletMain->AddCScript(inner); + + pwalletMain->SetAddressBook(innerID, strAccount, "send"); + return CBitcoinAddress(innerID).ToString(); +} + + +struct tallyitem { + CAmount nAmount; + int nConf; + int nBCConf; + vector txids; + bool fIsWatchonly; + tallyitem() + { + nAmount = 0; + nConf = std::numeric_limits::max(); + nBCConf = std::numeric_limits::max(); + fIsWatchonly = false; + } +}; + +UniValue ListReceived(const UniValue& params, bool fByAccounts) +{ + // Minimum confirmations + int nMinDepth = 1; + if (params.size() > 0) + nMinDepth = params[0].get_int(); + + // Whether to include empty accounts + bool fIncludeEmpty = false; + if (params.size() > 1) + fIncludeEmpty = params[1].get_bool(); + + isminefilter filter = ISMINE_SPENDABLE; + if (params.size() > 2) + if (params[2].get_bool()) + filter = filter | ISMINE_WATCH_ONLY; + + // Tally + map mapTally; + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { + const CWalletTx& wtx = (*it).second; + + if (wtx.IsCoinBase() || !IsFinalTx(wtx)) + continue; + + int nDepth = wtx.GetDepthInMainChain(); + int nBCDepth = wtx.GetDepthInMainChain(false); + if (nDepth < nMinDepth) + continue; + + BOOST_FOREACH (const CTxOut& txout, wtx.vout) { + CTxDestination address; + if (!ExtractDestination(txout.scriptPubKey, address)) + continue; + + isminefilter mine = IsMine(*pwalletMain, address); + if (!(mine & filter)) + continue; + + tallyitem& item = mapTally[address]; + item.nAmount += txout.nValue; + item.nConf = min(item.nConf, nDepth); + item.nBCConf = min(item.nBCConf, nBCDepth); + item.txids.push_back(wtx.GetHash()); + if (mine & ISMINE_WATCH_ONLY) + item.fIsWatchonly = true; + } + } + + // Reply + UniValue ret(UniValue::VARR); + map mapAccountTally; + BOOST_FOREACH (const PAIRTYPE(CBitcoinAddress, CAddressBookData) & item, pwalletMain->mapAddressBook) { + const CBitcoinAddress& address = item.first; + const string& strAccount = item.second.name; + map::iterator it = mapTally.find(address); + if (it == mapTally.end() && !fIncludeEmpty) + continue; + + CAmount nAmount = 0; + int nConf = std::numeric_limits::max(); + int nBCConf = std::numeric_limits::max(); + bool fIsWatchonly = false; + if (it != mapTally.end()) { + nAmount = (*it).second.nAmount; + nConf = (*it).second.nConf; + nBCConf = (*it).second.nBCConf; + fIsWatchonly = (*it).second.fIsWatchonly; + } + + if (fByAccounts) { + tallyitem& item = mapAccountTally[strAccount]; + item.nAmount += nAmount; + item.nConf = min(item.nConf, nConf); + item.nBCConf = min(item.nBCConf, nBCConf); + item.fIsWatchonly = fIsWatchonly; + } else { + UniValue obj(UniValue::VOBJ); + if (fIsWatchonly) + obj.push_back(Pair("involvesWatchonly", true)); + obj.push_back(Pair("address", address.ToString())); + obj.push_back(Pair("account", strAccount)); + obj.push_back(Pair("amount", ValueFromAmount(nAmount))); + obj.push_back(Pair("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf))); + obj.push_back(Pair("bcconfirmations", (nBCConf == std::numeric_limits::max() ? 0 : nBCConf))); + UniValue transactions(UniValue::VARR); + if (it != mapTally.end()) { + BOOST_FOREACH (const uint256& item, (*it).second.txids) { + transactions.push_back(item.GetHex()); + } + } + obj.push_back(Pair("txids", transactions)); + ret.push_back(obj); + } + } + + if (fByAccounts) { + for (map::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it) { + CAmount nAmount = (*it).second.nAmount; + int nConf = (*it).second.nConf; + int nBCConf = (*it).second.nBCConf; + UniValue obj(UniValue::VOBJ); + if ((*it).second.fIsWatchonly) + obj.push_back(Pair("involvesWatchonly", true)); + obj.push_back(Pair("account", (*it).first)); + obj.push_back(Pair("amount", ValueFromAmount(nAmount))); + obj.push_back(Pair("confirmations", (nConf == std::numeric_limits::max() ? 0 : nConf))); + obj.push_back(Pair("bcconfirmations", (nBCConf == std::numeric_limits::max() ? 0 : nBCConf))); + ret.push_back(obj); + } + } + + return ret; +} + +UniValue listreceivedbyaddress(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 3) + throw runtime_error( + "listreceivedbyaddress ( minconf includeempty includeWatchonly)\n" + "\nList balances by receiving address.\n" + "\nArguments:\n" + "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" + "2. includeempty (numeric, optional, default=false) Whether to include addresses that haven't received any payments.\n" + "3. includeWatchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n" + + "\nResult:\n" + "[\n" + " {\n" + " \"involvesWatchonly\" : \"true\", (bool) Only returned if imported addresses were involved in transaction\n" + " \"address\" : \"receivingaddress\", (string) The receiving address\n" + " \"account\" : \"accountname\", (string) The account of the receiving address. The default account is \"\".\n" + " \"amount\" : x.xxx, (numeric) The total amount in btc received by the address\n" + " \"confirmations\" : n (numeric) The number of confirmations of the most recent transaction included\n" + " \"bcconfirmations\" : n (numeric) The number of blockchain confirmations of the most recent transaction included\n" + " }\n" + " ,...\n" + "]\n" + + "\nExamples:\n" + + HelpExampleCli("listreceivedbyaddress", "") + HelpExampleCli("listreceivedbyaddress", "6 true") + HelpExampleRpc("listreceivedbyaddress", "6, true, true")); + + return ListReceived(params, false); +} + +UniValue listreceivedbyaccount(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 3) + throw runtime_error( + "listreceivedbyaccount ( minconf includeempty includeWatchonly)\n" + "\nList balances by account.\n" + "\nArguments:\n" + "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" + "2. includeempty (boolean, optional, default=false) Whether to include accounts that haven't received any payments.\n" + "3. includeWatchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n" + + "\nResult:\n" + "[\n" + " {\n" + " \"involvesWatchonly\" : \"true\", (bool) Only returned if imported addresses were involved in transaction\n" + " \"account\" : \"accountname\", (string) The account name of the receiving account\n" + " \"amount\" : x.xxx, (numeric) The total amount received by addresses with this account\n" + " \"confirmations\" : n (numeric) The number of confirmations of the most recent transaction included\n" + " \"bcconfirmations\" : n (numeric) The number of blockchain confirmations of the most recent transaction included\n" + " }\n" + " ,...\n" + "]\n" + + "\nExamples:\n" + + HelpExampleCli("listreceivedbyaccount", "") + HelpExampleCli("listreceivedbyaccount", "6 true") + HelpExampleRpc("listreceivedbyaccount", "6, true, true")); + + return ListReceived(params, true); +} + +static void MaybePushAddress(UniValue & entry, const CTxDestination &dest) +{ + CBitcoinAddress addr; + if (addr.Set(dest)) + entry.push_back(Pair("address", addr.ToString())); +} + +void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter) +{ + CAmount nFee; + string strSentAccount; + list listReceived; + list listSent; + + wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, filter); + + bool fAllAccounts = (strAccount == string("*")); + bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY); + + // Sent + if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) { + BOOST_FOREACH (const COutputEntry& s, listSent) { + UniValue entry(UniValue::VOBJ); + if (involvesWatchonly || (::IsMine(*pwalletMain, s.destination) & ISMINE_WATCH_ONLY)) + entry.push_back(Pair("involvesWatchonly", true)); + entry.push_back(Pair("account", strSentAccount)); + MaybePushAddress(entry, s.destination); + std::map::const_iterator it = wtx.mapValue.find("DS"); + entry.push_back(Pair("category", (it != wtx.mapValue.end() && it->second == "1") ? "darksent" : "send")); + entry.push_back(Pair("amount", ValueFromAmount(-s.amount))); + entry.push_back(Pair("vout", s.vout)); + entry.push_back(Pair("fee", ValueFromAmount(-nFee))); + if (fLong) + WalletTxToJSON(wtx, entry); + ret.push_back(entry); + } + } + + // Received + if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) { + BOOST_FOREACH (const COutputEntry& r, listReceived) { + string account; + if (pwalletMain->mapAddressBook.count(r.destination)) + account = pwalletMain->mapAddressBook[r.destination].name; + if (fAllAccounts || (account == strAccount)) { + UniValue entry(UniValue::VOBJ); + if (involvesWatchonly || (::IsMine(*pwalletMain, r.destination) & ISMINE_WATCH_ONLY)) + entry.push_back(Pair("involvesWatchonly", true)); + entry.push_back(Pair("account", account)); + MaybePushAddress(entry, r.destination); + if (wtx.IsCoinBase()) { + if (wtx.GetDepthInMainChain() < 1) + entry.push_back(Pair("category", "orphan")); + else if (wtx.GetBlocksToMaturity() > 0) + entry.push_back(Pair("category", "immature")); + else + entry.push_back(Pair("category", "generate")); + } else { + entry.push_back(Pair("category", "receive")); + } + entry.push_back(Pair("amount", ValueFromAmount(r.amount))); + entry.push_back(Pair("vout", r.vout)); + if (fLong) + WalletTxToJSON(wtx, entry); + ret.push_back(entry); + } + } + } +} + +void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, UniValue& ret) +{ + bool fAllAccounts = (strAccount == string("*")); + + if (fAllAccounts || acentry.strAccount == strAccount) { + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("account", acentry.strAccount)); + entry.push_back(Pair("category", "move")); + entry.push_back(Pair("time", acentry.nTime)); + entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit))); + entry.push_back(Pair("otheraccount", acentry.strOtherAccount)); + entry.push_back(Pair("comment", acentry.strComment)); + ret.push_back(entry); + } +} + +UniValue listtransactions(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 4) + throw runtime_error( + "listtransactions ( \"account\" count from includeWatchonly)\n" + "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions for account 'account'.\n" + "\nArguments:\n" + "1. \"account\" (string, optional) The account name. If not included, it will list all transactions for all accounts.\n" + " If \"\" is set, it will list transactions for the default account.\n" + "2. count (numeric, optional, default=10) The number of transactions to return\n" + "3. from (numeric, optional, default=0) The number of transactions to skip\n" + "4. includeWatchonly (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')\n" + "\nResult:\n" + "[\n" + " {\n" + " \"account\":\"accountname\", (string) The account name associated with the transaction. \n" + " It will be \"\" for the default account.\n" + " \"address\":\"xdnaaddress\", (string) The xdna address of the transaction. Not present for \n" + " move transactions (category = move).\n" + " \"category\":\"send|receive|move\", (string) The transaction category. 'move' is a local (off blockchain)\n" + " transaction between accounts, and not associated with an address,\n" + " transaction id or block. 'send' and 'receive' transactions are \n" + " associated with an address, transaction id and block details\n" + " \"amount\": x.xxx, (numeric) The amount in btc. This is negative for the 'send' category, and for the\n" + " 'move' category for moves outbound. It is positive for the 'receive' category,\n" + " and for the 'move' category for inbound funds.\n" + " \"vout\" : n, (numeric) the vout value\n" + " \"fee\": x.xxx, (numeric) The amount of the fee in btc. This is negative and only available for the \n" + " 'send' category of transactions.\n" + " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and \n" + " 'receive' category of transactions.\n" + " \"bcconfirmations\": n, (numeric) The number of blockchain confirmations for the transaction. Available for 'send'\n" + " and 'receive' category of transactions.\n" + " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive'\n" + " category of transactions.\n" + " \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive'\n" + " category of transactions.\n" + " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n" + " \"time\": xxx, (numeric) The transaction time in seconds since epoch (midnight Jan 1 1970 GMT).\n" + " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (midnight Jan 1 1970 GMT). Available \n" + " for 'send' and 'receive' category of transactions.\n" + " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" + " \"otheraccount\": \"accountname\", (string) For the 'move' category of transactions, the account the funds came \n" + " from (for receiving funds, positive amounts), or went to (for sending funds,\n" + " negative amounts).\n" + " }\n" + "]\n" + + "\nExamples:\n" + "\nList the most recent 10 transactions in the systems\n" + + HelpExampleCli("listtransactions", "") + + "\nList the most recent 10 transactions for the tabby account\n" + HelpExampleCli("listtransactions", "\"tabby\"") + + "\nList transactions 100 to 120 from the tabby account\n" + HelpExampleCli("listtransactions", "\"tabby\" 20 100") + + "\nAs a json rpc call\n" + HelpExampleRpc("listtransactions", "\"tabby\", 20, 100")); + + string strAccount = "*"; + if (params.size() > 0) + strAccount = params[0].get_str(); + int nCount = 10; + if (params.size() > 1) + nCount = params[1].get_int(); + int nFrom = 0; + if (params.size() > 2) + nFrom = params[2].get_int(); + isminefilter filter = ISMINE_SPENDABLE; + if (params.size() > 3) + if (params[3].get_bool()) + filter = filter | ISMINE_WATCH_ONLY; + + if (nCount < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count"); + if (nFrom < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from"); + + UniValue ret(UniValue::VARR); + + std::list acentries; + CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount); + + // iterate backwards until we have nCount items to return: + for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) { + CWalletTx* const pwtx = (*it).second.first; + if (pwtx != 0) + ListTransactions(*pwtx, strAccount, 0, true, ret, filter); + CAccountingEntry* const pacentry = (*it).second.second; + if (pacentry != 0) + AcentryToJSON(*pacentry, strAccount, ret); + + if ((int)ret.size() >= (nCount + nFrom)) break; + } + // ret is newest to oldest + + if (nFrom > (int)ret.size()) + nFrom = ret.size(); + if ((nFrom + nCount) > (int)ret.size()) + nCount = ret.size() - nFrom; + + vector arrTmp = ret.getValues(); + + vector::iterator first = arrTmp.begin(); + std::advance(first, nFrom); + vector::iterator last = arrTmp.begin(); + std::advance(last, nFrom+nCount); + + if (last != arrTmp.end()) arrTmp.erase(last, arrTmp.end()); + if (first != arrTmp.begin()) arrTmp.erase(arrTmp.begin(), first); + + std::reverse(arrTmp.begin(), arrTmp.end()); // Return oldest to newest + + ret.clear(); + ret.setArray(); + ret.push_backV(arrTmp); + + return ret; +} + +UniValue listaccounts(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "listaccounts ( minconf includeWatchonly)\n" + "\nReturns Object that has account names as keys, account balances as values.\n" + "\nArguments:\n" + "1. minconf (numeric, optional, default=1) Only include transactions with at least this many confirmations\n" + "2. includeWatchonly (bool, optional, default=false) Include balances in watchonly addresses (see 'importaddress')\n" + "\nResult:\n" + "{ (json object where keys are account names, and values are numeric balances\n" + " \"account\": x.xxx, (numeric) The property name is the account name, and the value is the total balance for the account.\n" + " ...\n" + "}\n" + "\nExamples:\n" + "\nList account balances where there at least 1 confirmation\n" + + HelpExampleCli("listaccounts", "") + + "\nList account balances including zero confirmation transactions\n" + HelpExampleCli("listaccounts", "0") + + "\nList account balances for 6 or more confirmations\n" + HelpExampleCli("listaccounts", "6") + + "\nAs json rpc call\n" + HelpExampleRpc("listaccounts", "6")); + + int nMinDepth = 1; + if (params.size() > 0) + nMinDepth = params[0].get_int(); + isminefilter includeWatchonly = ISMINE_SPENDABLE; + if (params.size() > 1) + if (params[1].get_bool()) + includeWatchonly = includeWatchonly | ISMINE_WATCH_ONLY; + + map mapAccountBalances; + BOOST_FOREACH (const PAIRTYPE(CTxDestination, CAddressBookData) & entry, pwalletMain->mapAddressBook) { + if (IsMine(*pwalletMain, entry.first) & includeWatchonly) // This address belongs to me + mapAccountBalances[entry.second.name] = 0; + } + + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { + const CWalletTx& wtx = (*it).second; + CAmount nFee; + string strSentAccount; + list listReceived; + list listSent; + int nDepth = wtx.GetDepthInMainChain(); + if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0) + continue; + wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, includeWatchonly); + mapAccountBalances[strSentAccount] -= nFee; + BOOST_FOREACH (const COutputEntry& s, listSent) + mapAccountBalances[strSentAccount] -= s.amount; + if (nDepth >= nMinDepth) { + BOOST_FOREACH (const COutputEntry& r, listReceived) + if (pwalletMain->mapAddressBook.count(r.destination)) + mapAccountBalances[pwalletMain->mapAddressBook[r.destination].name] += r.amount; + else + mapAccountBalances[""] += r.amount; + } + } + + list acentries; + CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries); + BOOST_FOREACH (const CAccountingEntry& entry, acentries) + mapAccountBalances[entry.strAccount] += entry.nCreditDebit; + + UniValue ret(UniValue::VOBJ); + BOOST_FOREACH (const PAIRTYPE(string, CAmount) & accountBalance, mapAccountBalances) { + ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); + } + return ret; +} + +UniValue listsinceblock(const UniValue& params, bool fHelp) +{ + if (fHelp) + throw runtime_error( + "listsinceblock ( \"blockhash\" target-confirmations includeWatchonly)\n" + "\nGet all transactions in blocks since block [blockhash], or all transactions if omitted\n" + "\nArguments:\n" + "1. \"blockhash\" (string, optional) The block hash to list transactions since\n" + "2. target-confirmations: (numeric, optional) The confirmations required, must be 1 or more\n" + "3. includeWatchonly: (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')" + "\nResult:\n" + "{\n" + " \"transactions\": [\n" + " \"account\":\"accountname\", (string) The account name associated with the transaction. Will be \"\" for the default account.\n" + " \"address\":\"xdnaaddress\", (string) The xdna address of the transaction. Not present for move transactions (category = move).\n" + " \"category\":\"send|receive\", (string) The transaction category. 'send' has negative amounts, 'receive' has positive amounts.\n" + " \"amount\": x.xxx, (numeric) The amount in btc. This is negative for the 'send' category, and for the 'move' category for moves \n" + " outbound. It is positive for the 'receive' category, and for the 'move' category for inbound funds.\n" + " \"vout\" : n, (numeric) the vout value\n" + " \"fee\": x.xxx, (numeric) The amount of the fee in btc. This is negative and only available for the 'send' category of transactions.\n" + " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and 'receive' category of transactions.\n" + " \"bcconfirmations\" : n, (numeric) The number of blockchain confirmations for the transaction. Available for 'send' and 'receive' category of transactions.\n" + " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive' category of transactions.\n" + " \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive' category of transactions.\n" + " \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n" + " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n" + " \"time\": xxx, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT).\n" + " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (Jan 1 1970 GMT). Available for 'send' and 'receive' category of transactions.\n" + " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" + " \"to\": \"...\", (string) If a comment to is associated with the transaction.\n" + " ],\n" + " \"lastblock\": \"lastblockhash\" (string) The hash of the last block\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("listsinceblock", "") + HelpExampleCli("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\" 6") + HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\", 6")); + + CBlockIndex* pindex = NULL; + int target_confirms = 1; + isminefilter filter = ISMINE_SPENDABLE; + + if (params.size() > 0) { + uint256 blockId = 0; + + blockId.SetHex(params[0].get_str()); + BlockMap::iterator it = mapBlockIndex.find(blockId); + if (it != mapBlockIndex.end()) + pindex = it->second; + } + + if (params.size() > 1) { + target_confirms = params[1].get_int(); + + if (target_confirms < 1) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); + } + + if (params.size() > 2) + if (params[2].get_bool()) + filter = filter | ISMINE_WATCH_ONLY; + + int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1; + + UniValue transactions(UniValue::VARR); + + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++) { + CWalletTx tx = (*it).second; + + if (depth == -1 || tx.GetDepthInMainChain(false) < depth) + ListTransactions(tx, "*", 0, true, transactions, filter); + } + + CBlockIndex* pblockLast = chainActive[chainActive.Height() + 1 - target_confirms]; + uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : 0; + + UniValue ret(UniValue::VOBJ); + ret.push_back(Pair("transactions", transactions)); + ret.push_back(Pair("lastblock", lastblock.GetHex())); + + return ret; +} + +UniValue gettransaction(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "gettransaction \"txid\" ( includeWatchonly )\n" + "\nGet detailed information about in-wallet transaction \n" + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction id\n" + "2. \"includeWatchonly\" (bool, optional, default=false) Whether to include watchonly addresses in balance calculation and details[]\n" + "\nResult:\n" + "{\n" + " \"amount\" : x.xxx, (numeric) The transaction amount in btc\n" + " \"confirmations\" : n, (numeric) The number of confirmations\n" + " \"bcconfirmations\" : n, (numeric) The number of blockchain confirmations\n" + " \"blockhash\" : \"hash\", (string) The block hash\n" + " \"blockindex\" : xx, (numeric) The block index\n" + " \"blocktime\" : ttt, (numeric) The time in seconds since epoch (1 Jan 1970 GMT)\n" + " \"txid\" : \"transactionid\", (string) The transaction id.\n" + " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (1 Jan 1970 GMT)\n" + " \"timereceived\" : ttt, (numeric) The time received in seconds since epoch (1 Jan 1970 GMT)\n" + " \"details\" : [\n" + " {\n" + " \"account\" : \"accountname\", (string) The account name involved in the transaction, can be \"\" for the default account.\n" + " \"address\" : \"xdnaaddress\", (string) The xdna address involved in the transaction\n" + " \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n" + " \"amount\" : x.xxx (numeric) The amount in btc\n" + " \"vout\" : n, (numeric) the vout value\n" + " }\n" + " ,...\n" + " ],\n" + " \"hex\" : \"data\" (string) Raw data for transaction\n" + "}\n" + + "\nExamples:\n" + + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" true") + HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")); + + uint256 hash; + hash.SetHex(params[0].get_str()); + + isminefilter filter = ISMINE_SPENDABLE; + if (params.size() > 1) + if (params[1].get_bool()) + filter = filter | ISMINE_WATCH_ONLY; + + UniValue entry(UniValue::VOBJ); + if (!pwalletMain->mapWallet.count(hash)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); + const CWalletTx& wtx = pwalletMain->mapWallet[hash]; + + CAmount nCredit = wtx.GetCredit(filter); + CAmount nDebit = wtx.GetDebit(filter); + CAmount nNet = nCredit - nDebit; + CAmount nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0); + + entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); + if (wtx.IsFromMe(filter)) + entry.push_back(Pair("fee", ValueFromAmount(nFee))); + + WalletTxToJSON(wtx, entry); + + UniValue details(UniValue::VARR); + ListTransactions(wtx, "*", 0, false, details, filter); + entry.push_back(Pair("details", details)); + + string strHex = EncodeHexTx(static_cast(wtx)); + entry.push_back(Pair("hex", strHex)); + + return entry; +} + + +UniValue backupwallet(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "backupwallet \"destination\"\n" + "\nSafely copies wallet.dat to destination, which can be a directory or a path with filename.\n" + "\nArguments:\n" + "1. \"destination\" (string) The destination directory or file\n" + "\nExamples:\n" + + HelpExampleCli("backupwallet", "\"backup.dat\"") + HelpExampleRpc("backupwallet", "\"backup.dat\"")); + + string strDest = params[0].get_str(); + if (!BackupWallet(*pwalletMain, strDest)) + throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!"); + + return NullUniValue; +} + + +UniValue keypoolrefill(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "keypoolrefill ( newsize )\n" + "\nFills the keypool." + + HelpRequiringPassphrase() + "\n" + "\nArguments\n" + "1. newsize (numeric, optional, default=100) The new keypool size\n" + "\nExamples:\n" + + HelpExampleCli("keypoolrefill", "") + HelpExampleRpc("keypoolrefill", "")); + + // 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool + unsigned int kpSize = 0; + if (params.size() > 0) { + if (params[0].get_int() < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size."); + kpSize = (unsigned int)params[0].get_int(); + } + + EnsureWalletIsUnlocked(); + pwalletMain->TopUpKeyPool(kpSize); + + if (pwalletMain->GetKeyPoolSize() < kpSize) + throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool."); + + return NullUniValue; +} + + +static void LockWallet(CWallet* pWallet) +{ + LOCK(cs_nWalletUnlockTime); + nWalletUnlockTime = 0; + pWallet->fWalletUnlockAnonymizeOnly = false; + pWallet->Lock(); +} + +UniValue walletpassphrase(const UniValue& params, bool fHelp) +{ + if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3)) + throw runtime_error( + "walletpassphrase \"passphrase\" timeout ( anonymizeonly )\n" + "\nStores the wallet decryption key in memory for 'timeout' seconds.\n" + "This is needed prior to performing transactions related to private keys such as sending XDNAs\n" + "\nArguments:\n" + "1. \"passphrase\" (string, required) The wallet passphrase\n" + "2. timeout (numeric, required) The time to keep the decryption key in seconds.\n" + "3. anonymizeonly (boolean, optional, default=flase) If is true sending functions are disabled." + "\nNote:\n" + "Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n" + "time that overrides the old one. A timeout of \"0\" unlocks until the wallet is closed.\n" + "\nExamples:\n" + "\nUnlock the wallet for 60 seconds\n" + + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") + + "\nUnlock the wallet for 60 seconds but allow Obfuscation only\n" + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60 true") + + "\nLock the wallet again (before 60 seconds)\n" + HelpExampleCli("walletlock", "") + + "\nAs json rpc call\n" + HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60")); + + if (fHelp) + return true; + if (!pwalletMain->IsCrypted()) + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called."); + + // Note that the walletpassphrase is stored in params[0] which is not mlock()ed + SecureString strWalletPass; + strWalletPass.reserve(100); + // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) + // Alternately, find a way to make params[0] mlock()'d to begin with. + strWalletPass = params[0].get_str().c_str(); + + bool anonymizeOnly = false; + if (params.size() == 3) + anonymizeOnly = params[2].get_bool(); + + if (!pwalletMain->IsLocked() && pwalletMain->fWalletUnlockAnonymizeOnly && anonymizeOnly) + throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked."); + + if (!pwalletMain->Unlock(strWalletPass, anonymizeOnly)) + throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); + + pwalletMain->TopUpKeyPool(); + + int64_t nSleepTime = params[1].get_int64(); + LOCK(cs_nWalletUnlockTime); + nWalletUnlockTime = GetTime() + nSleepTime; + + if (nSleepTime > 0) { + nWalletUnlockTime = GetTime () + nSleepTime; + RPCRunLater ("lockwallet", boost::bind (LockWallet, pwalletMain), nSleepTime); + } + + return NullUniValue; +} + + +UniValue walletpassphrasechange(const UniValue& params, bool fHelp) +{ + if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) + throw runtime_error( + "walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n" + "\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n" + "\nArguments:\n" + "1. \"oldpassphrase\" (string) The current passphrase\n" + "2. \"newpassphrase\" (string) The new passphrase\n" + "\nExamples:\n" + + HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"") + HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"")); + + if (fHelp) + return true; + if (!pwalletMain->IsCrypted()) + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called."); + + // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string) + // Alternately, find a way to make params[0] mlock()'d to begin with. + SecureString strOldWalletPass; + strOldWalletPass.reserve(100); + strOldWalletPass = params[0].get_str().c_str(); + + SecureString strNewWalletPass; + strNewWalletPass.reserve(100); + strNewWalletPass = params[1].get_str().c_str(); + + if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1) + throw runtime_error( + "walletpassphrasechange \n" + "Changes the wallet passphrase from to ."); + + if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) + throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); + + return NullUniValue; +} + + +UniValue walletlock(const UniValue& params, bool fHelp) +{ + if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0)) + throw runtime_error( + "walletlock\n" + "\nRemoves the wallet encryption key from memory, locking the wallet.\n" + "After calling this method, you will need to call walletpassphrase again\n" + "before being able to call any methods which require the wallet to be unlocked.\n" + "\nExamples:\n" + "\nSet the passphrase for 2 minutes to perform a transaction\n" + + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") + + "\nPerform a send (requires passphrase set)\n" + HelpExampleCli("sendtoaddress", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\" 1.0") + + "\nClear the passphrase since we are done before 2 minutes is up\n" + HelpExampleCli("walletlock", "") + + "\nAs json rpc call\n" + HelpExampleRpc("walletlock", "")); + + if (fHelp) + return true; + if (!pwalletMain->IsCrypted()) + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called."); + + { + LOCK(cs_nWalletUnlockTime); + pwalletMain->Lock(); + nWalletUnlockTime = 0; + } + + return NullUniValue; +} + + +UniValue encryptwallet(const UniValue& params, bool fHelp) +{ + if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1)) + throw runtime_error( + "encryptwallet \"passphrase\"\n" + "\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n" + "After this, any calls that interact with private keys such as sending or signing \n" + "will require the passphrase to be set prior the making these calls.\n" + "Use the walletpassphrase call for this, and then walletlock call.\n" + "If the wallet is already encrypted, use the walletpassphrasechange call.\n" + "Note that this will shutdown the server.\n" + "\nArguments:\n" + "1. \"passphrase\" (string) The pass phrase to encrypt the wallet with. It must be at least 1 character, but should be long.\n" + "\nExamples:\n" + "\nEncrypt you wallet\n" + + HelpExampleCli("encryptwallet", "\"my pass phrase\"") + + "\nNow set the passphrase to use the wallet, such as for signing or sending XDNAs\n" + HelpExampleCli("walletpassphrase", "\"my pass phrase\"") + + "\nNow we can so something like sign\n" + HelpExampleCli("signmessage", "\"xdnaaddress\" \"test message\"") + + "\nNow lock the wallet again by removing the passphrase\n" + HelpExampleCli("walletlock", "") + + "\nAs a json rpc call\n" + HelpExampleRpc("encryptwallet", "\"my pass phrase\"")); + + if (fHelp) + return true; + if (pwalletMain->IsCrypted()) + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called."); + + // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) + // Alternately, find a way to make params[0] mlock()'d to begin with. + SecureString strWalletPass; + strWalletPass.reserve(100); + strWalletPass = params[0].get_str().c_str(); + + if (strWalletPass.length() < 1) + throw runtime_error( + "encryptwallet \n" + "Encrypts the wallet with ."); + + if (!pwalletMain->EncryptWallet(strWalletPass)) + throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet."); + + // BDB seems to have a bad habit of writing old data into + // slack space in .dat files; that is bad if the old data is + // unencrypted private keys. So: + StartShutdown(); + return "wallet encrypted; xdna server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup."; +} + +UniValue lockunspent(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "lockunspent unlock [{\"txid\":\"txid\",\"vout\":n},...]\n" + "\nUpdates list of temporarily unspendable outputs.\n" + "Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n" + "A locked transaction output will not be chosen by automatic coin selection, when spending XDNAs.\n" + "Locks are stored in memory only. Nodes start with zero locked outputs, and the locked output list\n" + "is always cleared (by virtue of process exit) when a node stops or fails.\n" + "Also see the listunspent call\n" + "\nArguments:\n" + "1. unlock (boolean, required) Whether to unlock (true) or lock (false) the specified transactions\n" + "2. \"transactions\" (string, required) A json array of objects. Each object the txid (string) vout (numeric)\n" + " [ (json array of json objects)\n" + " {\n" + " \"txid\":\"id\", (string) The transaction id\n" + " \"vout\": n (numeric) The output number\n" + " }\n" + " ,...\n" + " ]\n" + + "\nResult:\n" + "true|false (boolean) Whether the command was successful or not\n" + + "\nExamples:\n" + "\nList the unspent transactions\n" + + HelpExampleCli("listunspent", "") + + "\nLock an unspent transaction\n" + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + + "\nList the locked transactions\n" + HelpExampleCli("listlockunspent", "") + + "\nUnlock the transaction again\n" + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + + "\nAs a json rpc call\n" + HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"")); + + if (params.size() == 1) + RPCTypeCheck(params, boost::assign::list_of(UniValue::VBOOL)); + else + RPCTypeCheck(params, boost::assign::list_of(UniValue::VBOOL)(UniValue::VARR)); + + bool fUnlock = params[0].get_bool(); + + if (params.size() == 1) { + if (fUnlock) + pwalletMain->UnlockAllCoins(); + return true; + } + + UniValue outputs = params[1].get_array(); + for (unsigned int idx = 0; idx < outputs.size(); idx++) { + const UniValue& output = outputs[idx]; + if (!output.isObject()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object"); + const UniValue& o = output.get_obj(); + + RPCTypeCheckObj(o, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)); + + string txid = find_value(o, "txid").get_str(); + if (!IsHex(txid)) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid"); + + int nOutput = find_value(o, "vout").get_int(); + if (nOutput < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); + + COutPoint outpt(uint256(txid), nOutput); + + if (fUnlock) + pwalletMain->UnlockCoin(outpt); + else + pwalletMain->LockCoin(outpt); + } + + return true; +} + +UniValue listlockunspent(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 0) + throw runtime_error( + "listlockunspent\n" + "\nReturns list of temporarily unspendable outputs.\n" + "See the lockunspent call to lock and unlock transactions for spending.\n" + "\nResult:\n" + "[\n" + " {\n" + " \"txid\" : \"transactionid\", (string) The transaction id locked\n" + " \"vout\" : n (numeric) The vout value\n" + " }\n" + " ,...\n" + "]\n" + "\nExamples:\n" + "\nList the unspent transactions\n" + + HelpExampleCli("listunspent", "") + + "\nLock an unspent transaction\n" + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + + "\nList the locked transactions\n" + HelpExampleCli("listlockunspent", "") + + "\nUnlock the transaction again\n" + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + + "\nAs a json rpc call\n" + HelpExampleRpc("listlockunspent", "")); + + vector vOutpts; + pwalletMain->ListLockedCoins(vOutpts); + + UniValue ret(UniValue::VARR); + + BOOST_FOREACH (COutPoint& outpt, vOutpts) { + UniValue o(UniValue::VOBJ); + + o.push_back(Pair("txid", outpt.hash.GetHex())); + o.push_back(Pair("vout", (int)outpt.n)); + ret.push_back(o); + } + + return ret; +} + +UniValue settxfee(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 1) + throw runtime_error( + "settxfee amount\n" + "\nSet the transaction fee per kB.\n" + "\nArguments:\n" + "1. amount (numeric, required) The transaction fee in XDNA/kB rounded to the nearest 0.00000001\n" + "\nResult\n" + "true|false (boolean) Returns true if successful\n" + "\nExamples:\n" + + HelpExampleCli("settxfee", "0.00001") + HelpExampleRpc("settxfee", "0.00001")); + + // Amount + CAmount nAmount = 0; + if (params[0].get_real() != 0.0) + nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts + + payTxFee = CFeeRate(nAmount, 1000); + return true; +} + +UniValue getwalletinfo(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getwalletinfo\n" + "Returns an object containing various wallet state info.\n" + "\nResult:\n" + "{\n" + " \"walletversion\": xxxxx, (numeric) the wallet version\n" + " \"balance\": xxxxxxx, (numeric) the total XDNA balance of the wallet\n" + " \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n" + " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n" + " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" + " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getwalletinfo", "") + HelpExampleRpc("getwalletinfo", "")); + + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); + obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); + obj.push_back(Pair("txcount", (int)pwalletMain->mapWallet.size())); + obj.push_back(Pair("keypoololdest", pwalletMain->GetOldestKeyPoolTime())); + obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); + if (pwalletMain->IsCrypted()) + obj.push_back(Pair("unlocked_until", nWalletUnlockTime)); + return obj; +} + +// ppcoin: reserve balance from being staked for network protection +UniValue reservebalance(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "reservebalance ( reserve amount )\n" + "\nShow or set the reserve amount not participating in network protection\n" + "If no parameters provided current setting is printed.\n" + + "\nArguments:\n" + "1. reserve (boolean, optional) is true or false to turn balance reserve on or off.\n" + "2. amount (numeric, optional) is a real and rounded to cent.\n" + + "\nResult:\n" + "{\n" + " \"reserve\": true|false, (boolean) Status of the reserve balance\n" + " \"amount\": x.xxxx (numeric) Amount reserved\n" + "\nExamples:\n" + + HelpExampleCli("reservebalance", "true 5000") + HelpExampleRpc("reservebalance", "true 5000")); + + if (params.size() > 0) { + bool fReserve = params[0].get_bool(); + if (fReserve) { + if (params.size() == 1) + throw runtime_error("must provide amount to reserve balance.\n"); + CAmount nAmount = AmountFromValue(params[1]); + nAmount = (nAmount / CENT) * CENT; // round to cent + if (nAmount < 0) + throw runtime_error("amount cannot be negative.\n"); + nReserveBalance = nAmount; + } else { + if (params.size() > 1) + throw runtime_error("cannot specify amount to turn off reserve.\n"); + nReserveBalance = 0; + } + } + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("reserve", (nReserveBalance > 0))); + result.push_back(Pair("amount", ValueFromAmount(nReserveBalance))); + return result; +} + +// presstab HyperStake +UniValue setstakesplitthreshold(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "setstakesplitthreshold value\n" + "\nThis will set the output size of your stakes to never be below this number\n" + + "\nArguments:\n" + "1. value (numeric, required) Threshold value between 1 and 999999\n" + "\nResult:\n" + "{\n" + " \"threshold\": n, (numeric) Threshold value set\n" + " \"saved\": true|false (boolean) 'true' if successfully saved to the wallet file\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("setstakesplitthreshold", "5000") + HelpExampleRpc("setstakesplitthreshold", "5000")); + + uint64_t nStakeSplitThreshold = params[0].get_int(); + if (pwalletMain->IsLocked()) + throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Unlock wallet to use this feature"); + if (nStakeSplitThreshold > 999999) + throw runtime_error("Value out of range, max allowed is 999999"); + + CWalletDB walletdb(pwalletMain->strWalletFile); + LOCK(pwalletMain->cs_wallet); + { + bool fFileBacked = pwalletMain->fFileBacked; + + UniValue result(UniValue::VOBJ); + pwalletMain->nStakeSplitThreshold = nStakeSplitThreshold; + result.push_back(Pair("threshold", int(pwalletMain->nStakeSplitThreshold))); + if (fFileBacked) { + walletdb.WriteStakeSplitThreshold(nStakeSplitThreshold); + result.push_back(Pair("saved", "true")); + } else + result.push_back(Pair("saved", "false")); + + return result; + } +} + +// presstab HyperStake +UniValue getstakesplitthreshold(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getstakesplitthreshold\n" + "Returns the threshold for stake splitting\n" + "\nResult:\n" + "n (numeric) Threshold value\n" + "\nExamples:\n" + + HelpExampleCli("getstakesplitthreshold", "") + HelpExampleRpc("getstakesplitthreshold", "")); + + return int(pwalletMain->nStakeSplitThreshold); +} + +UniValue autocombinerewards(const UniValue& params, bool fHelp) +{ + bool fEnable; + if (params.size() >= 1) + fEnable = params[0].get_bool(); + + if (fHelp || params.size() < 1 || (fEnable && params.size() != 2) || params.size() > 2) + throw runtime_error( + "autocombinerewards true|false ( threshold )\n" + "\nWallet will automatically monitor for any coins with value below the threshold amount, and combine them if they reside with the same XDNA address\n" + "When autocombinerewards runs it will create a transaction, and therefore will be subject to transaction fees.\n" + + "\nArguments:\n" + "1. true|false (boolean, required) Enable auto combine (true) or disable (false)\n" + "2. threshold (numeric, optional) Threshold amount (default: 0)\n" + "\nExamples:\n" + + HelpExampleCli("autocombinerewards", "true 500") + HelpExampleRpc("autocombinerewards", "true 500")); + + CWalletDB walletdb(pwalletMain->strWalletFile); + CAmount nThreshold = 0; + + if (fEnable) + nThreshold = params[1].get_int(); + + pwalletMain->fCombineDust = fEnable; + pwalletMain->nAutoCombineThreshold = nThreshold; + + if (!walletdb.WriteAutoCombineSettings(fEnable, nThreshold)) + throw runtime_error("Changed settings in wallet but failed to save to database\n"); + + return NullUniValue; +} + +UniValue printMultiSend() +{ + UniValue ret(UniValue::VARR); + UniValue act(UniValue::VOBJ); + act.push_back(Pair("MultiSendStake Activated?", pwalletMain->fMultiSendStake)); + act.push_back(Pair("MultiSendMasternode Activated?", pwalletMain->fMultiSendMasternodeReward)); + ret.push_back(act); + + if (pwalletMain->vDisabledAddresses.size() >= 1) { + UniValue disAdd(UniValue::VOBJ); + for (unsigned int i = 0; i < pwalletMain->vDisabledAddresses.size(); i++) { + disAdd.push_back(Pair("Disabled From Sending", pwalletMain->vDisabledAddresses[i])); + } + ret.push_back(disAdd); + } + + ret.push_back("MultiSend Addresses to Send To:"); + + UniValue vMS(UniValue::VOBJ); + for (unsigned int i = 0; i < pwalletMain->vMultiSend.size(); i++) { + vMS.push_back(Pair("Address " + boost::lexical_cast(i), pwalletMain->vMultiSend[i].first)); + vMS.push_back(Pair("Percent", pwalletMain->vMultiSend[i].second)); + } + + ret.push_back(vMS); + return ret; +} + +UniValue printAddresses() +{ + std::vector vCoins; + pwalletMain->AvailableCoins(vCoins); + std::map mapAddresses; + BOOST_FOREACH (const COutput& out, vCoins) { + CTxDestination utxoAddress; + ExtractDestination(out.tx->vout[out.i].scriptPubKey, utxoAddress); + std::string strAdd = CBitcoinAddress(utxoAddress).ToString(); + + if (mapAddresses.find(strAdd) == mapAddresses.end()) //if strAdd is not already part of the map + mapAddresses[strAdd] = (double)out.tx->vout[out.i].nValue / (double)COIN; + else + mapAddresses[strAdd] += (double)out.tx->vout[out.i].nValue / (double)COIN; + } + + UniValue ret(UniValue::VARR); + for (map::const_iterator it = mapAddresses.begin(); it != mapAddresses.end(); ++it) { + UniValue obj(UniValue::VOBJ); + const std::string* strAdd = &(*it).first; + const double* nBalance = &(*it).second; + obj.push_back(Pair("Address ", *strAdd)); + obj.push_back(Pair("Balance ", *nBalance)); + ret.push_back(obj); + } + + return ret; +} + +unsigned int sumMultiSend() +{ + unsigned int sum = 0; + for (unsigned int i = 0; i < pwalletMain->vMultiSend.size(); i++) + sum += pwalletMain->vMultiSend[i].second; + return sum; +} + +UniValue multisend(const UniValue& params, bool fHelp) +{ + CWalletDB walletdb(pwalletMain->strWalletFile); + bool fFileBacked; + //MultiSend Commands + if (params.size() == 1) { + string strCommand = params[0].get_str(); + UniValue ret(UniValue::VOBJ); + if (strCommand == "print") { + return printMultiSend(); + } else if (strCommand == "printaddress" || strCommand == "printaddresses") { + return printAddresses(); + } else if (strCommand == "clear") { + LOCK(pwalletMain->cs_wallet); + { + bool erased = false; + if (pwalletMain->fFileBacked) { + if (walletdb.EraseMultiSend(pwalletMain->vMultiSend)) + erased = true; + } + + pwalletMain->vMultiSend.clear(); + pwalletMain->setMultiSendDisabled(); + + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("Erased from database", erased)); + obj.push_back(Pair("Erased from RAM", true)); + + return obj; + } + } else if (strCommand == "enablestake" || strCommand == "activatestake") { + if (pwalletMain->vMultiSend.size() < 1) + throw JSONRPCError(RPC_INVALID_REQUEST, "Unable to activate MultiSend, check MultiSend vector"); + + if (CBitcoinAddress(pwalletMain->vMultiSend[0].first).IsValid()) { + pwalletMain->fMultiSendStake = true; + if (!walletdb.WriteMSettings(true, pwalletMain->fMultiSendMasternodeReward, pwalletMain->nLastMultiSendHeight)) { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("error", "MultiSend activated but writing settings to DB failed")); + UniValue arr(UniValue::VARR); + arr.push_back(obj); + arr.push_back(printMultiSend()); + return arr; + } else + return printMultiSend(); + } + + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to activate MultiSend, check MultiSend vector"); + } else if (strCommand == "enablemasternode" || strCommand == "activatemasternode") { + if (pwalletMain->vMultiSend.size() < 1) + throw JSONRPCError(RPC_INVALID_REQUEST, "Unable to activate MultiSend, check MultiSend vector"); + + if (CBitcoinAddress(pwalletMain->vMultiSend[0].first).IsValid()) { + pwalletMain->fMultiSendMasternodeReward = true; + + if (!walletdb.WriteMSettings(pwalletMain->fMultiSendStake, true, pwalletMain->nLastMultiSendHeight)) { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("error", "MultiSend activated but writing settings to DB failed")); + UniValue arr(UniValue::VARR); + arr.push_back(obj); + arr.push_back(printMultiSend()); + return arr; + } else + return printMultiSend(); + } + + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to activate MultiSend, check MultiSend vector"); + } else if (strCommand == "disable" || strCommand == "deactivate") { + pwalletMain->setMultiSendDisabled(); + if (!walletdb.WriteMSettings(false, false, pwalletMain->nLastMultiSendHeight)) + throw JSONRPCError(RPC_DATABASE_ERROR, "MultiSend deactivated but writing settings to DB failed"); + + return printMultiSend(); + } else if (strCommand == "enableall") { + if (!walletdb.EraseMSDisabledAddresses(pwalletMain->vDisabledAddresses)) + return "failed to clear old vector from walletDB"; + else { + pwalletMain->vDisabledAddresses.clear(); + return printMultiSend(); + } + } + } + if (params.size() == 2 && params[0].get_str() == "delete") { + int del = boost::lexical_cast(params[1].get_str()); + if (!walletdb.EraseMultiSend(pwalletMain->vMultiSend)) + throw JSONRPCError(RPC_DATABASE_ERROR, "failed to delete old MultiSend vector from database"); + + pwalletMain->vMultiSend.erase(pwalletMain->vMultiSend.begin() + del); + if (!walletdb.WriteMultiSend(pwalletMain->vMultiSend)) + throw JSONRPCError(RPC_DATABASE_ERROR, "walletdb WriteMultiSend failed!"); + + return printMultiSend(); + } + if (params.size() == 2 && params[0].get_str() == "disable") { + std::string disAddress = params[1].get_str(); + if (!CBitcoinAddress(disAddress).IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "address you want to disable is not valid"); + else { + pwalletMain->vDisabledAddresses.push_back(disAddress); + if (!walletdb.EraseMSDisabledAddresses(pwalletMain->vDisabledAddresses)) + throw JSONRPCError(RPC_DATABASE_ERROR, "disabled address from sending, but failed to clear old vector from walletDB"); + + if (!walletdb.WriteMSDisabledAddresses(pwalletMain->vDisabledAddresses)) + throw JSONRPCError(RPC_DATABASE_ERROR, "disabled address from sending, but failed to store it to walletDB"); + else + return printMultiSend(); + } + } + + //if no commands are used + if (fHelp || params.size() != 2) + throw runtime_error( + "multisend \n" + "****************************************************************\n" + "WHAT IS MULTISEND?\n" + "MultiSend allows a user to automatically send a percent of their stake reward to as many addresses as you would like\n" + "The MultiSend transaction is sent when the staked coins mature (100 confirmations)\n" + "****************************************************************\n" + "TO CREATE OR ADD TO THE MULTISEND VECTOR:\n" + "multisend \n" + "This will add a new address to the MultiSend vector\n" + "Percent is a whole number 1 to 100.\n" + "****************************************************************\n" + "MULTISEND COMMANDS (usage: multisend )\n" + " print - displays the current MultiSend vector \n" + " clear - deletes the current MultiSend vector \n" + " enablestake/activatestake - activates the current MultiSend vector to be activated on stake rewards\n" + " enablemasternode/activatemasternode - activates the current MultiSend vector to be activated on masternode rewards\n" + " disable/deactivate - disables the current MultiSend vector \n" + " delete
    - deletes an address from the MultiSend vector \n" + " disable
    - prevents a specific address from sending MultiSend transactions\n" + " enableall - enables all addresses to be eligible to send MultiSend transactions\n" + "****************************************************************\n"); + + //if the user is entering a new MultiSend item + string strAddress = params[0].get_str(); + CBitcoinAddress address(strAddress); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid XDNA address"); + if (boost::lexical_cast(params[1].get_str()) < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid percentage"); + if (pwalletMain->IsLocked()) + throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); + unsigned int nPercent = boost::lexical_cast(params[1].get_str()); + + LOCK(pwalletMain->cs_wallet); + { + fFileBacked = pwalletMain->fFileBacked; + //Error if 0 is entered + if (nPercent == 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Sending 0% of stake is not valid"); + } + + //MultiSend can only send 100% of your stake + if (nPercent + sumMultiSend() > 100) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Failed to add to MultiSend vector, the sum of your MultiSend is greater than 100%"); + + for (unsigned int i = 0; i < pwalletMain->vMultiSend.size(); i++) { + if (pwalletMain->vMultiSend[i].first == strAddress) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Failed to add to MultiSend vector, cannot use the same address twice"); + } + + if (fFileBacked) + walletdb.EraseMultiSend(pwalletMain->vMultiSend); + + std::pair newMultiSend; + newMultiSend.first = strAddress; + newMultiSend.second = nPercent; + pwalletMain->vMultiSend.push_back(newMultiSend); + if (fFileBacked) { + if (!walletdb.WriteMultiSend(pwalletMain->vMultiSend)) + throw JSONRPCError(RPC_DATABASE_ERROR, "walletdb WriteMultiSend failed!"); + } + } + return printMultiSend(); +} diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp new file mode 100644 index 0000000..20cd3c6 --- /dev/null +++ b/src/wallet/wallet.cpp @@ -0,0 +1,3591 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Copyright (c) 2014-2015 The Dash developers +// Copyright (c) 2015-2017 The PIVX developers +// Copyright (c) 2017-2019 The XDNA Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "wallet.h" + +#include "base58.h" +#include "checkpoints.h" +#include "coincontrol.h" +#include "kernel.h" +#include "net.h" +#include "primitives/transaction.h" +#include "script/script.h" +#include "script/sign.h" +#include "spork.h" +#include "swifttx.h" +#include "timedata.h" +#include "util.h" +#include "utilmoneystr.h" + +#include +#include + +#include +#include +#include + +using namespace std; + +/** + * Settings + */ +CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE); +CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; +unsigned int nTxConfirmTarget = 1; +bool bSpendZeroConfChange = true; +bool bdisableSystemnotifications = true; // Those bubbles can be annoying and slow down the UI when you get lots of trx +bool fSendFreeTransactions = false; +bool fPayAtLeastCustomFee = true; + +/** + * Fees smaller than this (in duffs) are considered zero fee (for transaction creation) + * We are ~100 times smaller then bitcoin now (2015-06-23), set minTxFee 10 times higher + * so it's still 10 times lower comparing to bitcoin. + * Override with -mintxfee + */ +CFeeRate CWallet::minTxFee = CFeeRate(10000); +int64_t nStartupTime = GetAdjustedTime(); + +/** @defgroup mapWallet + * + * @{ + */ + +struct CompareValueOnly { + bool operator()(const pair >& t1, + const pair >& t2) const + { + return t1.first < t2.first; + } +}; + +std::string COutput::ToString() const +{ + return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(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 NULL; + return &(it->second); +} + +CPubKey CWallet::GenerateNewKey() +{ + AssertLockHeld(cs_wallet); // mapKeyMetadata + bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets + + RandAddSeedPerfmon(); + CKey secret; + 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)); + + // Create new metadata + int64_t nCreationTime = GetTime(); + mapKeyMetadata[pubkey.GetID()] = CKeyMetadata(nCreationTime); + if (!nTimeFirstKey || nCreationTime < nTimeFirstKey) + nTimeFirstKey = nCreationTime; + + if (!AddKeyPubKey(secret, pubkey)) + throw std::runtime_error("CWallet::GenerateNewKey() : AddKey failed"); + return pubkey; +} + +bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey& pubkey) +{ + AssertLockHeld(cs_wallet); // mapKeyMetadata + 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); + + if (!fFileBacked) + return true; + if (!IsCrypted()) { + return CWalletDB(strWalletFile).WriteKey(pubkey, secret.GetPrivKey(), mapKeyMetadata[pubkey.GetID()]); + } + return true; +} + +bool CWallet::AddCryptedKey(const CPubKey& vchPubKey, + const 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()]); + else + return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); + } + return false; +} + +bool CWallet::LoadKeyMetadata(const CPubKey& pubkey, const CKeyMetadata& meta) +{ + AssertLockHeld(cs_wallet); // mapKeyMetadata + if (meta.nCreateTime && (!nTimeFirstKey || meta.nCreateTime < nTimeFirstKey)) + nTimeFirstKey = meta.nCreateTime; + + mapKeyMetadata[pubkey.GetID()] = meta; + return true; +} + +bool CWallet::LoadCryptedKey(const CPubKey& vchPubKey, const std::vector& vchCryptedSecret) +{ + return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); +} + +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; + nTimeFirstKey = 1; // No birthday information for watch-only keys. + NotifyWatchonlyChanged(true); + if (!fFileBacked) + return true; + return CWalletDB(strWalletFile).WriteWatchOnly(dest); +} + +bool CWallet::RemoveWatchOnly(const CScript& dest) +{ + AssertLockHeld(cs_wallet); + if (!CCryptoKeyStore::RemoveWatchOnly(dest)) + return false; + if (!HaveWatchOnly()) + NotifyWatchonlyChanged(false); + if (fFileBacked) + if (!CWalletDB(strWalletFile).EraseWatchOnly(dest)) + return false; + + return true; +} + +bool CWallet::LoadWatchOnly(const CScript& dest) +{ + return CCryptoKeyStore::AddWatchOnly(dest); +} + +bool CWallet::AddMultiSig(const CScript& dest) +{ + if (!CCryptoKeyStore::AddMultiSig(dest)) + return false; + nTimeFirstKey = 1; // No birthday information + NotifyMultiSigChanged(true); + if (!fFileBacked) + return true; + return CWalletDB(strWalletFile).WriteMultiSig(dest); +} + +bool CWallet::RemoveMultiSig(const CScript& dest) +{ + AssertLockHeld(cs_wallet); + if (!CCryptoKeyStore::RemoveMultiSig(dest)) + return false; + if (!HaveMultiSig()) + NotifyMultiSigChanged(false); + if (fFileBacked) + if (!CWalletDB(strWalletFile).EraseMultiSig(dest)) + return false; + + return true; +} + +bool CWallet::LoadMultiSig(const CScript& dest) +{ + return CCryptoKeyStore::AddMultiSig(dest); +} + +bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool anonymizeOnly) +{ + SecureString strWalletPassphraseFinal; + + if (!IsLocked()) { + fWalletUnlockAnonymizeOnly = anonymizeOnly; + return true; + } + + strWalletPassphraseFinal = strWalletPassphrase; + + + CCrypter crypter; + CKeyingMaterial vMasterKey; + + { + LOCK(cs_wallet); + BOOST_FOREACH (const MasterKeyMap::value_type& pMasterKey, mapMasterKeys) { + if (!crypter.SetKeyFromPassphrase(strWalletPassphraseFinal, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) + return false; + if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) + continue; // try another master key + if (CCryptoKeyStore::Unlock(vMasterKey)) { + fWalletUnlockAnonymizeOnly = anonymizeOnly; + return true; + } + } + } + return false; +} + +bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase) +{ + bool fWasLocked = IsLocked(); + SecureString strOldWalletPassphraseFinal = strOldWalletPassphrase; + + { + LOCK(cs_wallet); + Lock(); + + CCrypter crypter; + CKeyingMaterial vMasterKey; + BOOST_FOREACH (MasterKeyMap::value_type& pMasterKey, mapMasterKeys) { + if (!crypter.SetKeyFromPassphrase(strOldWalletPassphraseFinal, 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) +{ + LOCK(cs_wallet); // nWalletVersion + 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) +{ + LOCK(cs_wallet); // nWalletVersion, nWalletMaxVersion + // cannot downgrade below current version + if (nWalletVersion > nVersion) + return false; + + nWalletMaxVersion = nVersion; + + return true; +} + +set CWallet::GetConflicts(const uint256& txid) const +{ + 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; + + BOOST_FOREACH (const CTxIn& txin, wtx.vin) { + if (mapTxSpends.count(txin.prevout) <= 1) + continue; // No conflict if zero or one spends + range = mapTxSpends.equal_range(txin.prevout); + for (TxSpends::const_iterator it = range.first; it != range.second; ++it) + result.insert(it->second); + } + return result; +} + +void CWallet::SyncMetaData(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 = NULL; + 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; + 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); + 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() && mit->second.GetDepthInMainChain() >= 0) + return true; // Spent + } + return false; +} + +void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid) +{ + mapTxSpends.insert(make_pair(outpoint, wtxid)); + pair range; + range = mapTxSpends.equal_range(outpoint); + SyncMetaData(range); +} + + +void CWallet::AddToSpends(const uint256& wtxid) +{ + assert(mapWallet.count(wtxid)); + CWalletTx& thisTx = mapWallet[wtxid]; + if (thisTx.IsCoinBase()) // Coinbases don't spend anything! + return; + + BOOST_FOREACH (const CTxIn& txin, thisTx.vin) + AddToSpends(txin.prevout, wtxid); +} + +bool CWallet::GetMasternodeVinAndKeys(CTxIn& txinRet, CPubKey& pubKeyRet, CKey& keyRet, std::string strTxHash, std::string strOutputIndex) +{ + // wait for reindex and/or import to finish + if (fImporting || fReindex) return false; + + // Find possible candidates + std::vector vPossibleCoins; + AvailableCoins(vPossibleCoins, true, NULL, false, ONLY_DEPOSIT); + if (vPossibleCoins.empty()) { + LogPrintf("CWallet::GetMasternodeVinAndKeys -- Could not locate any valid masternode vin\n"); + return false; + } + + if (strTxHash.empty()) // No output specified, select the first one + return GetVinAndKeysFromOutput(vPossibleCoins[0], txinRet, pubKeyRet, keyRet); + + // Find specific vin + uint256 txHash = uint256S(strTxHash); + + int nOutputIndex; + try { + nOutputIndex = std::stoi(strOutputIndex.c_str()); + } catch (const std::exception& e) { + LogPrintf("%s: %s on strOutputIndex\n", __func__, e.what()); + return false; + } + + BOOST_FOREACH (COutput& out, vPossibleCoins) + if (out.tx->GetHash() == txHash && out.i == nOutputIndex) // found it! + return GetVinAndKeysFromOutput(out, txinRet, pubKeyRet, keyRet); + + LogPrintf("CWallet::GetMasternodeVinAndKeys -- Could not locate specified masternode vin\n"); + return false; +} + +bool CWallet::GetVinAndKeysFromOutput(COutput out, CTxIn& txinRet, CPubKey& pubKeyRet, CKey& keyRet) +{ + // wait for reindex and/or import to finish + if (fImporting || fReindex) return false; + + CScript pubScript; + + txinRet = CTxIn(out.tx->GetHash(), out.i); + pubScript = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey + + CTxDestination address1; + ExtractDestination(pubScript, address1); + CBitcoinAddress address2(address1); + + CKeyID keyID; + if (!address2.GetKeyID(keyID)) { + LogPrintf("CWallet::GetVinAndKeysFromOutput -- Address does not refer to a key\n"); + return false; + } + + if (!GetKey(keyID, keyRet)) { + LogPrintf("CWallet::GetVinAndKeysFromOutput -- Private key for address is not known\n"); + return false; + } + + pubKeyRet = keyRet.GetPubKey(); + return true; +} + +bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) +{ + if (IsCrypted()) + return false; + + CKeyingMaterial vMasterKey; + RandAddSeedPerfmon(); + + vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE); + GetRandBytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE); + + CMasterKey kMasterKey; + RandAddSeedPerfmon(); + + kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE); + GetRandBytes(&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 = NULL; + 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 their unencrypted wallet. + assert(false); + } + + 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 their unencrypted wallet. + assert(false); + } + + delete pwalletdbEncryption; + pwalletdbEncryption = NULL; + } + + Lock(); + Unlock(strWalletPassphrase); + 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; +} + +int64_t CWallet::IncOrderPosNext(CWalletDB* pwalletdb) +{ + AssertLockHeld(cs_wallet); // nOrderPosNext + int64_t nRet = nOrderPosNext++; + if (pwalletdb) { + pwalletdb->WriteOrderPosNext(nOrderPosNext); + } else { + CWalletDB(strWalletFile).WriteOrderPosNext(nOrderPosNext); + } + return nRet; +} + +CWallet::TxItems CWallet::OrderedTxItems(std::list& acentries, std::string strAccount) +{ + AssertLockHeld(cs_wallet); // mapWallet + CWalletDB walletdb(strWalletFile); + + // First: get all CWalletTx and CAccountingEntry into a sorted-by-order multimap. + TxItems txOrdered; + + // Note: maintaining indices in the database of (account,time) --> txid and (account, time) --> acentry + // would make this much faster for applications that do this a lot. + for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { + CWalletTx* wtx = &((*it).second); + txOrdered.insert(make_pair(wtx->nOrderPos, TxPair(wtx, (CAccountingEntry*)0))); + } + acentries.clear(); + walletdb.ListAccountCreditDebit(strAccount, acentries); + BOOST_FOREACH (CAccountingEntry& entry, acentries) { + txOrdered.insert(make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry))); + } + + return txOrdered; +} + +void CWallet::MarkDirty() +{ + { + LOCK(cs_wallet); + BOOST_FOREACH (PAIRTYPE(const uint256, CWalletTx) & item, mapWallet) + item.second.MarkDirty(); + } +} + +bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet) +{ + uint256 hash = wtxIn.GetHash(); + + if (fFromLoadWallet) { + mapWallet[hash] = wtxIn; + CWalletTx& wtx = mapWallet[hash]; + wtx.BindWallet(this); + wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0))); + AddToSpends(hash); + } else { + LOCK(cs_wallet); + // Inserts only if not already there, returns tx inserted or tx found + pair::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn)); + CWalletTx& wtx = (*ret.first).second; + wtx.BindWallet(this); + bool fInsertedNew = ret.second; + if (fInsertedNew) { + if (!wtx.nTimeReceived) + wtx.nTimeReceived = GetAdjustedTime(); + wtx.nOrderPos = IncOrderPosNext(); + wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0))); + + AddToSpends(hash); + } + + bool fUpdated = false; + if (!fInsertedNew) { + // Merge + if (wtxIn.hashBlock != 0 && wtxIn.hashBlock != wtx.hashBlock) { + wtx.hashBlock = wtxIn.hashBlock; + fUpdated = true; + } + if (wtxIn.nIndex != -1 && (wtxIn.vMerkleBranch != wtx.vMerkleBranch || wtxIn.nIndex != wtx.nIndex)) { + wtx.vMerkleBranch = wtxIn.vMerkleBranch; + 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.GetHash().ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); + + // Write to disk + if (fInsertedNew || fUpdated) + if (!wtx.WriteToDisk()) + 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.GetHash().GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } + } + return true; +} + +/** + * Add a transaction to the wallet, or update it. + * pblock is optional, but should be provided if the transaction is known to be in a block. + * If fUpdate is true, existing transactions will be updated. + */ +bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate) +{ + { + AssertLockHeld(cs_wallet); + bool fExisted = mapWallet.count(tx.GetHash()) != 0; + if (fExisted && !fUpdate) return false; + if (fExisted || IsMine(tx) || IsFromMe(tx)) { + CWalletTx wtx(this, tx); + // Get merkle branch if transaction was found in a block + if (pblock) + wtx.SetMerkleBranch(*pblock); + return AddToWallet(wtx); + } + } + return false; +} + +void CWallet::SyncTransaction(const CTransaction& tx, const CBlock* pblock) +{ + LOCK2(cs_main, cs_wallet); + if (!AddToWalletIfInvolvingMe(tx, pblock, true)) + return; // Not one of ours + + // If a transaction changes 'conflicted' state, that changes the balance + // available of the outputs it spends. So force those to be + // recomputed, also: + BOOST_FOREACH (const CTxIn& txin, tx.vin) { + if (mapWallet.count(txin.prevout.hash)) + mapWallet[txin.prevout.hash].MarkDirty(); + } +} + +void CWallet::EraseFromWallet(const uint256& hash) +{ + if (!fFileBacked) + return; + { + LOCK(cs_wallet); + if (mapWallet.erase(hash)) + CWalletDB(strWalletFile).EraseTx(hash); + } + return; +} + + +isminetype CWallet::IsMine(const CTxIn& txin) const +{ + { + LOCK(cs_wallet); + map::const_iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) { + const CWalletTx& prev = (*mi).second; + if (txin.prevout.n < prev.vout.size()) + return IsMine(prev.vout[txin.prevout.n]); + } + } + return ISMINE_NO; +} + +CAmount CWallet::GetDebit(const CTxIn& txin, const isminefilter& filter) const +{ + { + LOCK(cs_wallet); + map::const_iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) { + const CWalletTx& prev = (*mi).second; + if (txin.prevout.n < prev.vout.size()) + if (IsMine(prev.vout[txin.prevout.n]) & filter) + return prev.vout[txin.prevout.n].nValue; + } + } + return 0; +} + +// Recursively determine the rounds of a given input (How deep is the Obfuscation chain for a given input) +int CWallet::GetRealInputObfuscationRounds(CTxIn in, int rounds) const +{ + static std::map mDenomWtxes; + + if (rounds >= 16) return 15; // 16 rounds max + + uint256 hash = in.prevout.hash; + unsigned int nout = in.prevout.n; + + const CWalletTx* wtx = GetWalletTx(hash); + if (wtx != NULL) { + std::map::const_iterator mdwi = mDenomWtxes.find(hash); + // not known yet, let's add it + if (mdwi == mDenomWtxes.end()) { + LogPrint("obfuscation", "GetInputObfuscationRounds INSERTING %s\n", hash.ToString()); + mDenomWtxes[hash] = CMutableTransaction(*wtx); + } + // found and it's not an initial value, just return it + else if (mDenomWtxes[hash].vout[nout].nRounds != -10) { + return mDenomWtxes[hash].vout[nout].nRounds; + } + + + // bounds check + if (nout >= wtx->vout.size()) { + // should never actually hit this + LogPrint("obfuscation", "GetInputObfuscationRounds UPDATED %s %3d %3d\n", hash.ToString(), nout, -4); + return -4; + } + + if (pwalletMain->IsCollateralAmount(wtx->vout[nout].nValue)) { + mDenomWtxes[hash].vout[nout].nRounds = -3; + LogPrint("obfuscation", "GetInputObfuscationRounds UPDATED %s %3d %3d\n", hash.ToString(), nout, mDenomWtxes[hash].vout[nout].nRounds); + return mDenomWtxes[hash].vout[nout].nRounds; + } + + //make sure the final output is non-denominate + if (/*rounds == 0 && */ !IsDenominatedAmount(wtx->vout[nout].nValue)) //NOT DENOM + { + mDenomWtxes[hash].vout[nout].nRounds = -2; + LogPrint("obfuscation", "GetInputObfuscationRounds UPDATED %s %3d %3d\n", hash.ToString(), nout, mDenomWtxes[hash].vout[nout].nRounds); + return mDenomWtxes[hash].vout[nout].nRounds; + } + + bool fAllDenoms = true; + BOOST_FOREACH (CTxOut out, wtx->vout) { + fAllDenoms = fAllDenoms && IsDenominatedAmount(out.nValue); + } + // this one is denominated but there is another non-denominated output found in the same tx + if (!fAllDenoms) { + mDenomWtxes[hash].vout[nout].nRounds = 0; + LogPrint("obfuscation", "GetInputObfuscationRounds UPDATED %s %3d %3d\n", hash.ToString(), nout, mDenomWtxes[hash].vout[nout].nRounds); + return mDenomWtxes[hash].vout[nout].nRounds; + } + + int nShortest = -10; // an initial value, should be no way to get this by calculations + bool fDenomFound = false; + // only denoms here so let's look up + BOOST_FOREACH (CTxIn in2, wtx->vin) { + if (IsMine(in2)) { + int n = GetRealInputObfuscationRounds(in2, rounds + 1); + // denom found, find the shortest chain or initially assign nShortest with the first found value + if (n >= 0 && (n < nShortest || nShortest == -10)) { + nShortest = n; + fDenomFound = true; + } + } + } + mDenomWtxes[hash].vout[nout].nRounds = fDenomFound ? (nShortest >= 15 ? 16 : nShortest + 1) // good, we a +1 to the shortest one but only 16 rounds max allowed + : + 0; // too bad, we are the fist one in that chain + LogPrint("obfuscation", "GetInputObfuscationRounds UPDATED %s %3d %3d\n", hash.ToString(), nout, mDenomWtxes[hash].vout[nout].nRounds); + return mDenomWtxes[hash].vout[nout].nRounds; + } + + return rounds - 1; +} + +// respect current settings +int CWallet::GetInputObfuscationRounds(CTxIn in) const +{ + LOCK(cs_wallet); + int realObfuscationRounds = GetRealInputObfuscationRounds(in, 0); + return realObfuscationRounds > nObfuscationRounds ? nObfuscationRounds : realObfuscationRounds; +} + +bool CWallet::IsDenominated(const CTxIn& txin) const +{ + { + LOCK(cs_wallet); + map::const_iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) { + const CWalletTx& prev = (*mi).second; + if (txin.prevout.n < prev.vout.size()) return IsDenominatedAmount(prev.vout[txin.prevout.n].nValue); + } + } + return false; +} + +bool CWallet::IsDenominated(const CTransaction& tx) const +{ + /* + Return false if ANY inputs are non-denom + */ + bool ret = true; + BOOST_FOREACH (const CTxIn& txin, tx.vin) { + if (!IsDenominated(txin)) { + ret = false; + } + } + return ret; +} + + +bool CWallet::IsDenominatedAmount(CAmount nInputAmount) const +{ + BOOST_FOREACH (CAmount d, obfuScationDenominations) + if (nInputAmount == d) + return true; + return false; +} + +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; +} + +int64_t CWalletTx::GetTxTime() const +{ + int64_t n = nTimeSmart; + return n ? n : nTimeReceived; +} + +int CWalletTx::GetRequestCount() const +{ + // Returns -1 if it wasn't being tracked + int nRequests = -1; + { + LOCK(pwallet->cs_wallet); + if (IsCoinBase()) { + // Generated block + if (hashBlock != 0) { + map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); + if (mi != pwallet->mapRequestCount.end()) + nRequests = (*mi).second; + } + } else { + // Did anyone request this transaction? + map::const_iterator mi = pwallet->mapRequestCount.find(GetHash()); + if (mi != pwallet->mapRequestCount.end()) { + nRequests = (*mi).second; + + // How about the block it's in? + if (nRequests == 0 && hashBlock != 0) { + map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); + if (mi != pwallet->mapRequestCount.end()) + nRequests = (*mi).second; + else + nRequests = 1; // If it's in someone else's block it must have got out + } + } + } + } + return nRequests; +} + +void CWalletTx::GetAmounts(list& listReceived, + list& listSent, + CAmount& nFee, + string& strSentAccount, + const isminefilter& filter) const +{ + nFee = 0; + listReceived.clear(); + listSent.clear(); + strSentAccount = strFromAccount; + + // Compute fee: + CAmount nDebit = GetDebit(filter); + if (nDebit > 0) // debit>0 means we signed/sent this transaction + { + CAmount nValueOut = GetValueOut(); + nFee = nDebit - nValueOut; + } + + // Sent/received. + for (unsigned int i = 0; i < vout.size(); ++i) { + const CTxOut& txout = 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)) { + LogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", + this->GetHash().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 string& strAccount, CAmount& nReceived, CAmount& nSent, CAmount& nFee, const isminefilter& filter) const +{ + nReceived = nSent = nFee = 0; + + CAmount allFee; + string strSentAccount; + list listReceived; + list listSent; + GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); + + if (strAccount == strSentAccount) { + BOOST_FOREACH (const COutputEntry& s, listSent) + nSent += s.amount; + nFee = allFee; + } + { + LOCK(pwallet->cs_wallet); + BOOST_FOREACH (const COutputEntry& r, listReceived) { + if (pwallet->mapAddressBook.count(r.destination)) { + 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; + } + } + } +} + + +bool CWalletTx::WriteToDisk() +{ + return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this); +} + +/** + * 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. + */ +int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) +{ + int ret = 0; + int64_t nNow = GetTime(); + + CBlockIndex* pindex = pindexStart; + { + LOCK2(cs_main, cs_wallet); + + // 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); + + ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup + double dProgressStart = Checkpoints::GuessVerificationProgress(pindex, false); + double dProgressTip = Checkpoints::GuessVerificationProgress(chainActive.Tip(), false); + while (pindex) { + if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0) + ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((Checkpoints::GuessVerificationProgress(pindex, false) - dProgressStart) / (dProgressTip - dProgressStart) * 100)))); + + CBlock block; + ReadBlockFromDisk(block, pindex); + BOOST_FOREACH (CTransaction& tx, block.vtx) { + if (AddToWalletIfInvolvingMe(tx, &block, fUpdate)) + ret++; + } + pindex = chainActive.Next(pindex); + if (GetTime() >= nNow + 60) { + nNow = GetTime(); + LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, Checkpoints::GuessVerificationProgress(pindex)); + } + } + ShowProgress(_("Rescanning..."), 100); // hide progress dialog in GUI + } + return ret; +} + +void CWallet::ReacceptWalletTransactions() +{ + LOCK2(cs_main, cs_wallet); + BOOST_FOREACH (PAIRTYPE(const uint256, CWalletTx) & item, mapWallet) { + const uint256& wtxid = item.first; + CWalletTx& wtx = item.second; + assert(wtx.GetHash() == wtxid); + + int nDepth = wtx.GetDepthInMainChain(); + + if (!wtx.IsCoinBase() && nDepth < 0 && !wtx.IsCoinStake()) { + // Try to add to memory pool + LOCK(mempool.cs); + wtx.AcceptToMemoryPool(false); + } + } +} + +bool CWalletTx::InMempool() const +{ + LOCK(mempool.cs); + if (mempool.exists(GetHash())) { + return true; + } + return false; +} + +void CWalletTx::RelayWalletTransaction(std::string strCommand) +{ + LOCK(cs_main); + if (!IsCoinBase()) { + if (GetDepthInMainChain() == 0) { + uint256 hash = GetHash(); + LogPrintf("Relaying wtx %s\n", hash.ToString()); + + if (strCommand == "ix") { + mapTxLockReq.insert(make_pair(hash, (CTransaction) * this)); + CreateNewLock(((CTransaction) * this)); + RelayTransactionLockReq((CTransaction) * this, true); + } else { + RelayTransaction((CTransaction) * this); + } + } + } +} + +set CWalletTx::GetConflicts() const +{ + set result; + if (pwallet != NULL) { + uint256 myHash = GetHash(); + result = pwallet->GetConflicts(myHash); + result.erase(myHash); + } + return result; +} + +void CWallet::ResendWalletTransactions() +{ + // Do this infrequently and randomly to avoid giving away + // that these are our transactions. + if (GetTime() < nNextResend) + 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 (nTimeBestReceived < nLastResend) + return; + nLastResend = GetTime(); + + // Rebroadcast any of our txes that aren't in a block yet + LogPrintf("ResendWalletTransactions()\n"); + { + LOCK(cs_wallet); + // Sort them in chronological order + multimap mapSorted; + BOOST_FOREACH (PAIRTYPE(const uint256, CWalletTx) & item, mapWallet) { + CWalletTx& wtx = item.second; + // Don't rebroadcast until it's had plenty of time that + // it should have gotten in already by now. + if (nTimeBestReceived - (int64_t)wtx.nTimeReceived > 5 * 60) + mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); + } + BOOST_FOREACH (PAIRTYPE(const unsigned int, CWalletTx*) & item, mapSorted) { + CWalletTx& wtx = *item.second; + wtx.RelayWalletTransaction(); + } + } +} + +/** @} */ // end of mapWallet + + +/** @defgroup Actions + * + * @{ + */ + +CAmount CWallet::GetBalance() const +{ + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (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::GetAnonymizableBalance() const +{ + if(fLiteMode) + return 0; + + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for(const auto& entry : mapWallet) { + + const auto& coin = entry.second; + + if(coin.IsTrusted()) + nTotal += coin.GetAnonymizableCredit(); + } + } + + return nTotal; +} + +CAmount CWallet::GetAnonymizedBalance() const +{ + if (fLiteMode) return 0; + + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { + const CWalletTx* pcoin = &(*it).second; + + if (pcoin->IsTrusted()) + nTotal += pcoin->GetAnonymizedCredit(); + } + } + + return nTotal; +} + +// Note: calculated including unconfirmed, +// that's ok as long as we use it for informational purposes only +double CWallet::GetAverageAnonymizedRounds() const +{ + if (fLiteMode) return 0; + + double fTotal = 0; + double fCount = 0; + + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { + const CWalletTx* pcoin = &(*it).second; + + uint256 hash = (*it).first; + + for (unsigned int i = 0; i < pcoin->vout.size(); i++) { + CTxIn vin = CTxIn(hash, i); + + if (IsSpent(hash, i) || IsMine(pcoin->vout[i]) != ISMINE_SPENDABLE || !IsDenominated(vin)) continue; + + int rounds = GetInputObfuscationRounds(vin); + fTotal += (float)rounds; + fCount += 1; + } + } + } + + if (fCount == 0) return 0; + + return fTotal / fCount; +} + +// Note: calculated including unconfirmed, +// that's ok as long as we use it for informational purposes only +CAmount CWallet::GetNormalizedAnonymizedBalance() const +{ + if (fLiteMode) return 0; + + CAmount nTotal = 0; + + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { + const CWalletTx* pcoin = &(*it).second; + + uint256 hash = (*it).first; + + for (unsigned int i = 0; i < pcoin->vout.size(); i++) { + CTxIn vin = CTxIn(hash, i); + + if (IsSpent(hash, i) || IsMine(pcoin->vout[i]) != ISMINE_SPENDABLE || !IsDenominated(vin)) continue; + if (pcoin->GetDepthInMainChain() < 0) continue; + + int rounds = GetInputObfuscationRounds(vin); + nTotal += pcoin->vout[i].nValue * rounds / nObfuscationRounds; + } + } + } + + return nTotal; +} + +CAmount CWallet::GetDenominatedBalance(bool unconfirmed) const +{ + if (fLiteMode) return 0; + + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { + const CWalletTx* pcoin = &(*it).second; + + nTotal += pcoin->GetDenominatedCredit(unconfirmed); + } + } + + return nTotal; +} + +CAmount CWallet::GetUnconfirmedBalance() const +{ + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { + const CWalletTx* pcoin = &(*it).second; + if (!IsFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0)) + nTotal += pcoin->GetAvailableCredit(); + } + } + return nTotal; +} + +CAmount CWallet::GetImmatureBalance() const +{ + LOCK(cs_main); + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { + const CWalletTx* pcoin = &(*it).second; + nTotal += pcoin->GetImmatureCredit(); + } + } + return nTotal; +} + +CAmount CWallet::GetWatchOnlyBalance() const +{ + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (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 +{ + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { + const CWalletTx* pcoin = &(*it).second; + if (!IsFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0)) + nTotal += pcoin->GetAvailableWatchOnlyCredit(); + } + } + return nTotal; +} + +CAmount CWallet::GetImmatureWatchOnlyBalance() const +{ + LOCK(cs_main); + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { + const CWalletTx* pcoin = &(*it).second; + nTotal += pcoin->GetImmatureWatchOnlyCredit(); + } + } + return nTotal; +} + +/** + * populate vCoins with vector of available COutputs. + */ +void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl* coinControl, bool fIncludeZeroValue, AvailableCoinsType nCoinType, bool fUseIX) const +{ + vCoins.clear(); + + { + LOCK2(cs_main, cs_wallet); + for (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->IsCoinStake()) && pcoin->GetBlocksToMaturity() > 0) + continue; + + int nDepth = pcoin->GetDepthInMainChain(false); + // do not use IX for inputs that have less then 6 blockchain confirmations + if (fUseIX && nDepth < 6) + 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; + + for (unsigned int i = 0; i < pcoin->vout.size(); i++) { + bool found = false; + if (nCoinType == ONLY_DENOMINATED) { + found = IsDenominatedAmount(pcoin->vout[i].nValue); + } else if (nCoinType == ONLY_NOTDEPOSITIFMN) { + found = !(fMasterNode && CMasternode::IsDepositCoins(pcoin->vout[i].nValue)); + } else if (nCoinType == ONLY_NONDENOMINATED_NOTDEPOSITIFMN) { + if (IsCollateralAmount(pcoin->vout[i].nValue)) continue; // do not use collateral amounts + found = !IsDenominatedAmount(pcoin->vout[i].nValue); + if (found && fMasterNode) found = !CMasternode::IsDepositCoins(pcoin->vout[i].nValue); // do not use Hot MN funds + } else if (nCoinType == ONLY_DEPOSIT) { + found = CMasternode::IsDepositCoins(pcoin->vout[i].nValue); + } else { + found = true; + } + if (!found) continue; + + isminetype mine = IsMine(pcoin->vout[i]); + if (IsSpent(wtxid, i)) + continue; + if (mine == ISMINE_NO) + continue; + + if (IsLockedCoin((*it).first, i) && nCoinType != ONLY_DEPOSIT) + continue; + if (pcoin->vout[i].nValue <= 0 && !fIncludeZeroValue) + continue; + if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected((*it).first, i)) + continue; + + bool fIsSpendable = false; + if ((mine & ISMINE_SPENDABLE) != ISMINE_NO) + fIsSpendable = true; + if ((mine & ISMINE_MULTISIG) != ISMINE_NO) + fIsSpendable = true; + vCoins.emplace_back(COutput(pcoin, i, nDepth, fIsSpendable)); + } + } + } +} + +map > CWallet::AvailableCoinsByAddress(bool fConfirmed, CAmount maxCoinValue) +{ + vector vCoins; + AvailableCoins(vCoins, fConfirmed); + + map > mapCoins; + BOOST_FOREACH (COutput out, vCoins) { + if (maxCoinValue > 0 && out.tx->vout[out.i].nValue > maxCoinValue) + continue; + + CTxDestination address; + if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) + continue; + + mapCoins[CBitcoinAddress(address)].push_back(out); + } + + return mapCoins; +} + +static void ApproximateBestSubset(vector > > vValue, const CAmount& nTotalLower, const CAmount& nTargetValue, vector& vfBest, CAmount& nBest, int iterations = 1000) +{ + vector vfIncluded; + + vfBest.assign(vValue.size(), true); + nBest = nTotalLower; + + seed_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 (unsigned int 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() & 1 : !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; + } + } + } + } + } +} + + +// TODO: find appropriate place for this sort function +// move denoms down +bool less_then_denom(const COutput& out1, const COutput& out2) +{ + const CWalletTx* pcoin1 = out1.tx; + const CWalletTx* pcoin2 = out2.tx; + + bool found1 = false; + bool found2 = false; + BOOST_FOREACH (CAmount d, obfuScationDenominations) // loop through predefined denoms + { + if (pcoin1->vout[out1.i].nValue == d) found1 = true; + if (pcoin2->vout[out2.i].nValue == d) found2 = true; + } + return (!found1 && found2); +} + +bool CWallet::SelectStakeCoins(std::set >& setCoins, CAmount nTargetAmount) const +{ + vector vCoins; + AvailableCoins(vCoins, true, NULL, false, STAKABLE_COINS); + CAmount nAmountSelected = 0; + + for (const COutput& out : vCoins) { + //make sure not to outrun target amount + if (nAmountSelected + out.tx->vout[out.i].nValue > nTargetAmount) + continue; + + int64_t nTxTime = out.tx->GetTxTime(); + + //check for min age + if (GetAdjustedTime() - nTxTime < nStakeMinAge) + continue; + + //check that it is matured + if (out.nDepth < (out.tx->IsCoinStake() ? Params().COINBASE_MATURITY() : 10)) + continue; + + //add to our stake set + setCoins.insert(make_pair(out.tx, out.i)); + nAmountSelected += out.tx->vout[out.i].nValue; + } + return true; +} + +bool CWallet::MintableCoins() +{ + CAmount nBalance = GetBalance(); + if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance)) + return error("MintableCoins() : invalid reserve balance amount"); + if (nBalance <= nReserveBalance) + return false; + + vector vCoins; + AvailableCoins(vCoins, true); + + for (const COutput& out : vCoins) { + int64_t nTxTime = out.tx->GetTxTime(); + if (GetAdjustedTime() - nTxTime > nStakeMinAge) + return true; + } + + return false; +} + +bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, vector vCoins, set >& setCoinsRet, CAmount& nValueRet) const +{ + setCoinsRet.clear(); + nValueRet = 0; + + // List of values less than target + pair > coinLowestLarger; + coinLowestLarger.first = std::numeric_limits::max(); + coinLowestLarger.second.first = NULL; + vector > > vValue; + CAmount nTotalLower = 0; + + random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); + + // move denoms down on the list + sort(vCoins.begin(), vCoins.end(), less_then_denom); + + // try to find nondenom first to prevent unneeded spending of mixed coins + for (unsigned int tryDenom = 0; tryDenom < 2; tryDenom++) { + if (fDebug) LogPrint("selectcoins", "tryDenom: %d\n", tryDenom); + vValue.clear(); + nTotalLower = 0; + BOOST_FOREACH (const COutput& output, vCoins) { + if (!output.fSpendable) + continue; + + const CWalletTx* pcoin = output.tx; + + // if (fDebug) LogPrint("selectcoins", "value %s confirms %d\n", FormatMoney(pcoin->vout[output.i].nValue), output.nDepth); + if (output.nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs)) + continue; + + int i = output.i; + CAmount n = pcoin->vout[i].nValue; + if (tryDenom == 0 && IsDenominatedAmount(n)) continue; // we don't want denom values on first run + + pair > coin = make_pair(n, make_pair(pcoin, i)); + + if (n == nTargetValue) { + setCoinsRet.insert(coin.second); + nValueRet += coin.first; + return true; + } else if (n < nTargetValue + CENT) { + 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 == NULL) // there is no input larger than nTargetValue + { + if (tryDenom == 0) + // we didn't look at denom yet, let's do it + continue; + else + // we looked at everything possible and didn't find anything, no luck + return false; + } + setCoinsRet.insert(coinLowestLarger.second); + nValueRet += coinLowestLarger.first; + return true; + } + + // nTotalLower > nTargetValue + break; + } + + // Solve subset sum by stochastic approximation + sort(vValue.rbegin(), vValue.rend(), CompareValueOnly()); + vector vfBest; + CAmount nBest; + + ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest, 1000); + if (nBest != nTargetValue && nTotalLower >= nTargetValue + CENT) + ApproximateBestSubset(vValue, nTotalLower, nTargetValue + CENT, vfBest, nBest, 1000); + + // 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 + CENT) || coinLowestLarger.first <= nBest)) { + setCoinsRet.insert(coinLowestLarger.second); + nValueRet += coinLowestLarger.first; + } else { + string s = "CWallet::SelectCoinsMinConf best subset: "; + for (unsigned int i = 0; i < vValue.size(); i++) { + if (vfBest[i]) { + setCoinsRet.insert(vValue[i].second); + nValueRet += vValue[i].first; + s += FormatMoney(vValue[i].first) + " "; + } + } + LogPrintf("%s - total %s\n", s, FormatMoney(nBest)); + } + + return true; +} + +bool CWallet::SelectCoins(const CAmount& nTargetValue, set >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl, AvailableCoinsType coin_type, bool useIX) const +{ + // Note: this function should never be used for "always free" tx types like dstx + + vector vCoins; + AvailableCoins(vCoins, true, coinControl, false, coin_type, useIX); + + // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) + if (coinControl && coinControl->HasSelected()) { + BOOST_FOREACH (const COutput& out, vCoins) { + if (!out.fSpendable) + continue; + + if (coin_type == ONLY_DENOMINATED) { + CTxIn vin = CTxIn(out.tx->GetHash(), out.i); + int rounds = GetInputObfuscationRounds(vin); + // make sure it's actually anonymized + if (rounds < nObfuscationRounds) continue; + } + + nValueRet += out.tx->vout[out.i].nValue; + setCoinsRet.insert(make_pair(out.tx, out.i)); + } + return (nValueRet >= nTargetValue); + } + + //if we're doing only denominated, we need to round up to the nearest .1 XDNA + if (coin_type == ONLY_DENOMINATED) { + // Make outputs by looping through denominations, from large to small + BOOST_FOREACH (CAmount v, obfuScationDenominations) { + BOOST_FOREACH (const COutput& out, vCoins) { + if (out.tx->vout[out.i].nValue == v //make sure it's the denom we're looking for + && nValueRet + out.tx->vout[out.i].nValue < nTargetValue + (0.1 * COIN) + 100 //round the amount up to .1 XDNA over + ) { + CTxIn vin = CTxIn(out.tx->GetHash(), out.i); + int rounds = GetInputObfuscationRounds(vin); + // make sure it's actually anonymized + if (rounds < nObfuscationRounds) continue; + nValueRet += out.tx->vout[out.i].nValue; + setCoinsRet.insert(make_pair(out.tx, out.i)); + } + } + } + return (nValueRet >= nTargetValue); + } + + return (SelectCoinsMinConf(nTargetValue, 1, 6, vCoins, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue, 1, 1, vCoins, setCoinsRet, nValueRet) || + (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet))); +} + +struct CompareByPriority { + bool operator()(const COutput& t1, + const COutput& t2) const + { + return t1.Priority() > t2.Priority(); + } +}; + +bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount nValueMax, std::vector& vCoinsRet, std::vector& vCoinsRet2, CAmount& nValueRet, int nObfuscationRoundsMin, int nObfuscationRoundsMax) +{ + vCoinsRet.clear(); + nValueRet = 0; + + vCoinsRet2.clear(); + vector vCoins; + AvailableCoins(vCoins, true, NULL, ONLY_DENOMINATED); + + std::random_shuffle(vCoins.rbegin(), vCoins.rend()); + + //keep track of each denomination that we have + bool fFound10000 = false; + bool fFound1000 = false; + bool fFound100 = false; + bool fFound10 = false; + bool fFound1 = false; + bool fFoundDot1 = false; + + //Check to see if any of the denomination are off, in that case mark them as fulfilled + if (!(nDenom & (1 << 0))) fFound10000 = true; + if (!(nDenom & (1 << 1))) fFound1000 = true; + if (!(nDenom & (1 << 2))) fFound100 = true; + if (!(nDenom & (1 << 3))) fFound10 = true; + if (!(nDenom & (1 << 4))) fFound1 = true; + if (!(nDenom & (1 << 5))) fFoundDot1 = true; + + BOOST_FOREACH (const COutput& out, vCoins) { + // masternode-like input should not be selected by AvailableCoins now anyway + //if(out.tx->vout[out.i].nValue == 10000*COIN) continue; + if (nValueRet + out.tx->vout[out.i].nValue <= nValueMax) { + bool fAccepted = false; + + // Function returns as follows: + // + // bit 0 - 10000 XDNA+1 ( bit on if present ) + // bit 1 - 1000 XDNA+1 + // bit 2 - 100 XDNA+1 + // bit 3 - 10 XDNA+1 + // bit 4 - 1 XDNA+1 + // bit 5 - .1 XDNA+1 + + CTxIn vin = CTxIn(out.tx->GetHash(), out.i); + + int rounds = GetInputObfuscationRounds(vin); + if (rounds >= nObfuscationRoundsMax) continue; + if (rounds < nObfuscationRoundsMin) continue; + + if (fFound10000 && fFound1000 && fFound100 && fFound10 && fFound1 && fFoundDot1) { //if fulfilled + //we can return this for submission + if (nValueRet >= nValueMin) { + //random reduce the max amount we'll submit for anonymity + nValueMax -= (rand() % (nValueMax / 5)); + //on average use 50% of the inputs or less + int r = (rand() % (int)vCoins.size()); + if ((int)vCoinsRet.size() > r) return true; + } + //Denomination criterion has been met, we can take any matching denominations + if ((nDenom & (1 << 0)) && out.tx->vout[out.i].nValue == ((10000 * COIN) + 10000000)) { + fAccepted = true; + } else if ((nDenom & (1 << 1)) && out.tx->vout[out.i].nValue == ((1000 * COIN) + 1000000)) { + fAccepted = true; + } else if ((nDenom & (1 << 2)) && out.tx->vout[out.i].nValue == ((100 * COIN) + 100000)) { + fAccepted = true; + } else if ((nDenom & (1 << 3)) && out.tx->vout[out.i].nValue == ((10 * COIN) + 10000)) { + fAccepted = true; + } else if ((nDenom & (1 << 4)) && out.tx->vout[out.i].nValue == ((1 * COIN) + 1000)) { + fAccepted = true; + } else if ((nDenom & (1 << 5)) && out.tx->vout[out.i].nValue == ((.1 * COIN) + 100)) { + fAccepted = true; + } + } else { + //Criterion has not been satisfied, we will only take 1 of each until it is. + if ((nDenom & (1 << 0)) && out.tx->vout[out.i].nValue == ((10000 * COIN) + 10000000)) { + fAccepted = true; + fFound10000 = true; + } else if ((nDenom & (1 << 1)) && out.tx->vout[out.i].nValue == ((1000 * COIN) + 1000000)) { + fAccepted = true; + fFound1000 = true; + } else if ((nDenom & (1 << 2)) && out.tx->vout[out.i].nValue == ((100 * COIN) + 100000)) { + fAccepted = true; + fFound100 = true; + } else if ((nDenom & (1 << 3)) && out.tx->vout[out.i].nValue == ((10 * COIN) + 10000)) { + fAccepted = true; + fFound10 = true; + } else if ((nDenom & (1 << 4)) && out.tx->vout[out.i].nValue == ((1 * COIN) + 1000)) { + fAccepted = true; + fFound1 = true; + } else if ((nDenom & (1 << 5)) && out.tx->vout[out.i].nValue == ((.1 * COIN) + 100)) { + fAccepted = true; + fFoundDot1 = true; + } + } + if (!fAccepted) continue; + + vin.prevPubKey = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey + nValueRet += out.tx->vout[out.i].nValue; + vCoinsRet.push_back(vin); + vCoinsRet2.push_back(out); + } + } + + return (nValueRet >= nValueMin && fFound10000 && fFound1000 && fFound100 && fFound10 && fFound1 && fFoundDot1); +} + +bool CWallet::SelectCoinsDark(CAmount nValueMin, CAmount nValueMax, std::vector& setCoinsRet, CAmount& nValueRet, int nObfuscationRoundsMin, int nObfuscationRoundsMax) const +{ + CCoinControl* coinControl = NULL; + + setCoinsRet.clear(); + nValueRet = 0; + + vector vCoins; + AvailableCoins(vCoins, true, coinControl, nObfuscationRoundsMin < 0 ? ONLY_NONDENOMINATED_NOTDEPOSITIFMN : ONLY_DENOMINATED); + + set > setCoinsRet2; + + //order the array so largest nondenom are first, then denominations, then very small inputs. + sort(vCoins.rbegin(), vCoins.rend(), CompareByPriority()); + + for(const COutput& out : vCoins) { + //do not allow inputs less than 1 CENT + if (out.tx->vout[out.i].nValue < CENT) continue; + //do not allow collaterals to be selected + if (IsCollateralAmount(out.tx->vout[out.i].nValue)) continue; + if(fMasterNode && CMasternode::IsDepositCoins(out.tx->vout[out.i].nValue)) continue; //masternode input + + if (nValueRet + out.tx->vout[out.i].nValue <= nValueMax) { + CTxIn vin = CTxIn(out.tx->GetHash(), out.i); + + int rounds = GetInputObfuscationRounds(vin); + if (rounds >= nObfuscationRoundsMax) continue; + if (rounds < nObfuscationRoundsMin) continue; + + vin.prevPubKey = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey + nValueRet += out.tx->vout[out.i].nValue; + setCoinsRet.push_back(vin); + setCoinsRet2.insert(make_pair(out.tx, out.i)); + } + } + + // if it's more than min, we're good to return + if (nValueRet >= nValueMin) return true; + + return false; +} + +bool CWallet::SelectCoinsCollateral(std::vector& setCoinsRet, CAmount& nValueRet) const +{ + vector vCoins; + + //LogPrintf(" selecting coins for collateral\n"); + AvailableCoins(vCoins); + + //LogPrintf("found coins %d\n", (int)vCoins.size()); + + set > setCoinsRet2; + + BOOST_FOREACH (const COutput& out, vCoins) { + // collateral inputs will always be a multiple of DARSEND_COLLATERAL, up to five + if (IsCollateralAmount(out.tx->vout[out.i].nValue)) { + CTxIn vin = CTxIn(out.tx->GetHash(), out.i); + + vin.prevPubKey = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey + nValueRet += out.tx->vout[out.i].nValue; + setCoinsRet.push_back(vin); + setCoinsRet2.insert(make_pair(out.tx, out.i)); + return true; + } + } + + return false; +} + +int CWallet::CountInputsWithAmount(CAmount nInputAmount) +{ + CAmount nTotal = 0; + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsTrusted()) { + int nDepth = pcoin->GetDepthInMainChain(false); + + for (unsigned int i = 0; i < pcoin->vout.size(); i++) { + COutput out = COutput(pcoin, i, nDepth, true); + CTxIn vin = CTxIn(out.tx->GetHash(), out.i); + + if (out.tx->vout[out.i].nValue != nInputAmount) continue; + if (!IsDenominatedAmount(pcoin->vout[i].nValue)) continue; + if (IsSpent(out.tx->GetHash(), i) || IsMine(pcoin->vout[i]) != ISMINE_SPENDABLE || !IsDenominated(vin)) continue; + + nTotal++; + } + } + } + } + + return nTotal; +} + +bool CWallet::HasCollateralInputs(bool fOnlyConfirmed) const +{ + vector vCoins; + AvailableCoins(vCoins, fOnlyConfirmed); + + int nFound = 0; + BOOST_FOREACH (const COutput& out, vCoins) + if (IsCollateralAmount(out.tx->vout[out.i].nValue)) nFound++; + + return nFound > 0; +} + +bool CWallet::IsCollateralAmount(CAmount nInputAmount) const +{ + return nInputAmount != 0 && nInputAmount % OBFUSCATION_COLLATERAL == 0 && nInputAmount < OBFUSCATION_COLLATERAL * 5 && nInputAmount > OBFUSCATION_COLLATERAL; +} + +bool CWallet::CreateCollateralTransaction(CMutableTransaction& txCollateral, std::string& strReason) +{ + /* + To doublespend a collateral transaction, it will require a fee higher than this. So there's + still a significant cost. + */ + CAmount nFeeRet = 1 * COIN; + + txCollateral.vin.clear(); + txCollateral.vout.clear(); + + CReserveKey reservekey(this); + CAmount nValueIn2 = 0; + std::vector vCoinsCollateral; + + if (!SelectCoinsCollateral(vCoinsCollateral, nValueIn2)) { + strReason = "Error: Obfuscation requires a collateral transaction and could not locate an acceptable input!"; + return false; + } + + // make our change address + CScript scriptChange; + CPubKey vchPubKey; + assert(reservekey.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + reservekey.KeepKey(); + + BOOST_FOREACH (CTxIn v, vCoinsCollateral) + txCollateral.vin.push_back(v); + + if (nValueIn2 - OBFUSCATION_COLLATERAL - nFeeRet > 0) { + //pay collateral charge in fees + CTxOut vout3 = CTxOut(nValueIn2 - OBFUSCATION_COLLATERAL, scriptChange); + txCollateral.vout.push_back(vout3); + } + + int vinNumber = 0; + BOOST_FOREACH (CTxIn v, txCollateral.vin) { + if (!SignSignature(*this, v.prevPubKey, txCollateral, vinNumber, int(SIGHASH_ALL | SIGHASH_ANYONECANPAY))) { + BOOST_FOREACH (CTxIn v, vCoinsCollateral) + UnlockCoin(v.prevout); + + strReason = "CObfuscationPool::Sign - Unable to sign collateral transaction! \n"; + return false; + } + vinNumber++; + } + + return true; +} + +bool CWallet::ConvertList(std::vector vCoins, std::vector& vecAmounts) +{ + BOOST_FOREACH (CTxIn i, vCoins) { + if (mapWallet.count(i.prevout.hash)) { + CWalletTx& wtx = mapWallet[i.prevout.hash]; + if (i.prevout.n < wtx.vout.size()) { + vecAmounts.push_back(wtx.vout[i.prevout.n].nValue); + } + } else { + LogPrintf("ConvertList -- Couldn't find transaction\n"); + } + } + return true; +} + +bool CWallet::CreateTransaction(const vector >& vecSend, + CWalletTx& wtxNew, + CReserveKey& reservekey, + CAmount& nFeeRet, + std::string& strFailReason, + const CCoinControl* coinControl, + AvailableCoinsType coin_type, + bool useIX, + CAmount nFeePay) +{ + if (useIX && nFeePay < CENT) nFeePay = CENT; + + CAmount nValue = 0; + + BOOST_FOREACH (const PAIRTYPE(CScript, CAmount) & s, vecSend) { + if (nValue < 0) { + strFailReason = _("Transaction amounts must be positive"); + return false; + } + nValue += s.second; + } + if (vecSend.empty() || nValue < 0) { + strFailReason = _("Transaction amounts must be positive"); + return false; + } + + wtxNew.fTimeReceivedIsTxTime = true; + wtxNew.BindWallet(this); + CMutableTransaction txNew; + + { + LOCK2(cs_main, cs_wallet); + { + nFeeRet = 0; + if (nFeePay > 0) nFeeRet = nFeePay; + while (true) { + txNew.vin.clear(); + txNew.vout.clear(); + wtxNew.fFromMe = true; + + CAmount nTotalValue = nValue + nFeeRet; + double dPriority = 0; + + // vouts to the payees + if (coinControl && !coinControl->fSplitBlock) { + BOOST_FOREACH (const PAIRTYPE(CScript, CAmount) & s, vecSend) { + CTxOut txout(s.second, s.first); + if (txout.IsDust(::minRelayTxFee)) { + strFailReason = _("Transaction amount too small"); + return false; + } + txNew.vout.push_back(txout); + } + } else //UTXO Splitter Transaction + { + int nSplitBlock; + + if (coinControl) + nSplitBlock = coinControl->nSplitBlock; + else + nSplitBlock = 1; + + BOOST_FOREACH (const PAIRTYPE(CScript, CAmount) & s, vecSend) { + for (int i = 0; i < nSplitBlock; i++) { + if (i == nSplitBlock - 1) { + uint64_t nRemainder = s.second % nSplitBlock; + txNew.vout.push_back(CTxOut((s.second / nSplitBlock) + nRemainder, s.first)); + } else + txNew.vout.push_back(CTxOut(s.second / nSplitBlock, s.first)); + } + } + } + + // Choose coins to use + set > setCoins; + CAmount nValueIn = 0; + + if (!SelectCoins(nTotalValue, setCoins, nValueIn, coinControl, coin_type, useIX)) { + if (coin_type == ALL_COINS) { + strFailReason = _("Insufficient funds."); + } else if (coin_type == ONLY_NOTDEPOSITIFMN) { + strFailReason = _("Unable to locate enough funds for this transaction that are not equal MN collateral XDNA."); + } else if (coin_type == ONLY_NONDENOMINATED_NOTDEPOSITIFMN) { + strFailReason = _("Unable to locate enough Obfuscation non-denominated funds for this transaction that are not equal MN collateral XDNA."); + } else { + strFailReason = _("Unable to locate enough Obfuscation denominated funds for this transaction."); + strFailReason += " " + _("Obfuscation uses exact denominated amounts to send funds, you might simply need to anonymize some more coins."); + } + + if (useIX) { + strFailReason += " " + _("SwiftX requires inputs with at least 6 confirmations, you might need to wait a few minutes and try again."); + } + + return false; + } + + + BOOST_FOREACH (PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { + CAmount nCredit = pcoin.first->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(); + if (age != 0) + age += 1; + dPriority += (double)nCredit * age; + } + + CAmount nChange = nValueIn - nValue - nFeeRet; + + //over pay for denominated transactions + if (coin_type == ONLY_DENOMINATED) { + nFeeRet += nChange; + nChange = 0; + wtxNew.mapValue["DS"] = "1"; + } + + if (nChange > 0) { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-xdna-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); + assert(ret); // should never fail, as we just unlocked + + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + } + + CTxOut newTxOut(nChange, scriptChange); + + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(::minRelayTxFee)) { + nFeeRet += nChange; + nChange = 0; + reservekey.ReturnKey(); + } else { + // Insert change txn at random position: + vector::iterator position = txNew.vout.begin() + GetRandInt(txNew.vout.size() + 1); + txNew.vout.insert(position, newTxOut); + } + } else + reservekey.ReturnKey(); + + // Fill vin + BOOST_FOREACH (const PAIRTYPE(const CWalletTx*, unsigned int) & coin, setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second)); + + // Sign + int nIn = 0; + BOOST_FOREACH (const PAIRTYPE(const CWalletTx*, unsigned int) & coin, setCoins) + if (!SignSignature(*this, *coin.first, txNew, nIn++)) { + strFailReason = _("Signing transaction failed"); + return false; + } + + // Embed the constructed transaction data in wtxNew. + *static_cast(&wtxNew) = CTransaction(txNew); + + // Limit size + unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK, PROTOCOL_VERSION); + if (nBytes >= MAX_STANDARD_TX_SIZE) { + strFailReason = _("Transaction too large"); + return false; + } + dPriority = wtxNew.ComputePriority(dPriority, nBytes); + + // Can we complete this as a free transaction? + if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) { + // Not enough fee: enough priority? + double dPriorityNeeded = mempool.estimatePriority(nTxConfirmTarget); + // Not enough mempool history to estimate: use hard-coded AllowFree. + if (dPriorityNeeded <= 0 && AllowFree(dPriority)) + break; + + // Small enough, and priority high enough, to send for free + if (dPriorityNeeded > 0 && dPriority >= dPriorityNeeded) + break; + } + + CAmount nFeeNeeded = max(nFeePay, GetMinimumFee(nBytes, nTxConfirmTarget, mempool)); + + // 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) // Done, enough fee included + break; + + // Include more fee and try again. + nFeeRet = nFeeNeeded; + continue; + } + } + } + return true; +} + +bool CWallet::CreateTransaction(CScript scriptPubKey, const CAmount& nValue, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl, AvailableCoinsType coin_type, bool useIX, CAmount nFeePay) +{ + vector > vecSend; + vecSend.push_back(make_pair(scriptPubKey, nValue)); + return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet, strFailReason, coinControl, coin_type, useIX, nFeePay); +} + +// ppcoin: create coin stake transaction +bool CWallet::CreateCoinStake(const CKeyStore& keystore, uint32_t nTime, unsigned int nBits, int64_t nSearchInterval, CMutableTransaction& txNew, unsigned int& nTxNewTime) +{ + // The following split & combine thresholds are important to security + // Should not be adjusted if you don't understand the consequences + //int64_t nCombineThreshold = 0; + + txNew.vin.clear(); + txNew.vout.clear(); + + // Mark coin stake transaction + CScript scriptEmpty; + scriptEmpty.clear(); + txNew.vout.push_back(CTxOut(0, scriptEmpty)); + + // Choose coins to use + CAmount nBalance = GetBalance(); + + if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance)) + return error("CreateCoinStake : invalid reserve balance amount"); + + if (nBalance <= nReserveBalance) + return false; + + // presstab HyperStake - Initialize as static and don't update the set on every run of CreateCoinStake() in order to lighten resource use + static std::set > setStakeCoins; + static int nLastStakeSetUpdate = 0; + + if (GetTime() - nLastStakeSetUpdate > nStakeSetUpdateTime) { + setStakeCoins.clear(); + if (!SelectStakeCoins(setStakeCoins, nBalance - nReserveBalance)) + return false; + + nLastStakeSetUpdate = GetTime(); + } + + if (setStakeCoins.empty()) + return false; + + vector vwtxPrev; + + CAmount nCredit = 0; + CScript scriptPubKeyKernel; + + //prevent staking a time that won't be accepted + if (GetAdjustedTime() <= chainActive.Tip()->nTime) + MilliSleep(10000); + + BOOST_FOREACH (PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setStakeCoins) { + //make sure that enough time has elapsed between + CBlockIndex* pindex = NULL; + BlockMap::iterator it = mapBlockIndex.find(pcoin.first->hashBlock); + if (it != mapBlockIndex.end()) + pindex = it->second; + else { + if (fDebug) + LogPrintf("CreateCoinStake() failed to find block index \n"); + continue; + } + + // Read block header + CBlockHeader block = pindex->GetBlockHeader(); + + bool fKernelFound = false; + uint256 hashProofOfStake = 0; + COutPoint prevoutStake = COutPoint(pcoin.first->GetHash(), pcoin.second); + nTxNewTime = GetAdjustedTime(); + + //iterates each utxo inside of CheckStakeKernelHash() + if (CheckStakeKernelHash(nBits, block, *pcoin.first, prevoutStake, nTxNewTime, nHashDrift, false, hashProofOfStake, true)) { + //Double check that this will pass time requirements + if (nTxNewTime <= chainActive.Tip()->GetMedianTimePast()) { + LogPrintf("CreateCoinStake() : kernel found, but it is too far in the past \n"); + continue; + } + + // Found a kernel + if (fDebug && GetBoolArg("-printcoinstake", false)) + LogPrintf("CreateCoinStake : kernel found\n"); + + vector vSolutions; + txnouttype whichType; + CScript scriptPubKeyOut; + scriptPubKeyKernel = pcoin.first->vout[pcoin.second].scriptPubKey; + if (!Solver(scriptPubKeyKernel, whichType, vSolutions)) { + LogPrintf("CreateCoinStake : failed to parse kernel\n"); + break; + } + if (fDebug && GetBoolArg("-printcoinstake", false)) + LogPrintf("CreateCoinStake : parsed kernel type=%d\n", whichType); + if (whichType != TX_PUBKEY && whichType != TX_PUBKEYHASH) { + if (fDebug && GetBoolArg("-printcoinstake", false)) + LogPrintf("CreateCoinStake : no support for kernel type=%d\n", whichType); + break; // only support pay to public key and pay to address + } + if (whichType == TX_PUBKEYHASH) // pay to address type + { + //convert to pay to public key type + CKey key; + if (!keystore.GetKey(uint160(vSolutions[0]), key)) { + if (fDebug && GetBoolArg("-printcoinstake", false)) + LogPrintf("CreateCoinStake : failed to get key for kernel type=%d\n", whichType); + break; // unable to find corresponding public key + } + + scriptPubKeyOut << key.GetPubKey() << OP_CHECKSIG; + } else + scriptPubKeyOut = scriptPubKeyKernel; + + txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second)); + nCredit += pcoin.first->vout[pcoin.second].nValue; + vwtxPrev.push_back(pcoin.first); + txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); + + //presstab HyperStake - calculate the total size of our new output including the stake reward so that we can use it to decide whether to split the stake outputs + const CBlockIndex* pIndex0 = chainActive.Tip(); + uint64_t nTotalSize = pcoin.first->vout[pcoin.second].nValue + GetBlockValue(pIndex0->nHeight + 1, nTime); + + //presstab HyperStake - if MultiSend is set to send in coinstake we will add our outputs here (values asigned further down) + if (nTotalSize / 2 > nStakeSplitThreshold * COIN) + txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); //split stake + + if (fDebug && GetBoolArg("-printcoinstake", false)) + LogPrintf("CreateCoinStake : added kernel type=%d\n", whichType); + fKernelFound = true; + break; + } + if (fKernelFound) + break; // if kernel is found stop searching + } + if (nCredit == 0 || nCredit > nBalance - nReserveBalance) + return false; + + // Calculate reward + const CBlockIndex* pIndex0 = chainActive.Tip(); + + nCredit += GetBlockValue(pIndex0->nHeight + 1, nTime); + + //Masternode payment + CAmount MnPayee; + CAmount mnblock_value = GetBlockValue(chainActive.Height() + 1, nTime); + MnPayee = masternodePayments.FillBlockPayee(txNew, nTime, mnblock_value, true); + + nCredit -= MnPayee; + //presstab HyperStake - calculate the total size of our new output including the stake reward so that we can use it to decide whether to split the stake outputs + if (nCredit / 2 > nStakeSplitThreshold * COIN) { + txNew.vout[1].nValue = (nCredit / 2 / CENT) * CENT; + txNew.vout.push_back(CTxOut(nCredit - txNew.vout[1].nValue, txNew.vout[1].scriptPubKey)); + } else { + txNew.vout[1].nValue = nCredit; + } + + // Sign + int nIn = 0; + for(const CWalletTx* pcoin : vwtxPrev) { + if (!SignSignature(*this, *pcoin, txNew, nIn++)) + return error("CreateCoinStake : failed to sign coinstake"); + } + + // Successfully generated coinstake + nLastStakeSetUpdate = 0; //this will trigger stake set to repopulate next round + return true; +} + +/** + * Call after CreateTransaction unless you want to abort + */ +bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, std::string strCommand) +{ + { + LOCK2(cs_main, cs_wallet); + LogPrintf("CommitTransaction:\n%s", wtxNew.ToString()); + { + // This is only to keep the database open to defeat the auto-flush for the + // duration of this scope. This is the only place where this optimization + // maybe makes sense; please don't do it anywhere else. + CWalletDB* pwalletdb = fFileBacked ? new CWalletDB(strWalletFile, "r") : NULL; + + // 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); + + if (fFileBacked) + delete pwalletdb; + } + + // Track how many getdata requests our transaction gets + mapRequestCount[wtxNew.GetHash()] = 0; + + // Broadcast + if (!wtxNew.AcceptToMemoryPool(false)) { + // This must not fail. The transaction has already been signed and recorded. + LogPrintf("CommitTransaction() : Error: Transaction not valid\n"); + return false; + } + wtxNew.RelayWalletTransaction(strCommand); + } + return true; +} + +CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool) +{ + // payTxFee is user-set "I want to pay this much" + CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes); + // user selected total at least (default=true) + if (fPayAtLeastCustomFee && nFeeNeeded > 0 && nFeeNeeded < payTxFee.GetFeePerK()) + nFeeNeeded = payTxFee.GetFeePerK(); + // User didn't set: use -txconfirmtarget to estimate... + if (nFeeNeeded == 0) + nFeeNeeded = pool.estimateFee(nConfirmTarget).GetFee(nTxBytes); + // ... unless we don't have enough mempool data, in which case fall + // back to a hard-coded fee + if (nFeeNeeded == 0) + nFeeNeeded = minTxFee.GetFee(nTxBytes); + // prevent user from paying a non-sense fee (like 1 satoshi): 0 < fee < minRelayFee + if (nFeeNeeded < ::minRelayTxFee.GetFee(nTxBytes)) + nFeeNeeded = ::minRelayTxFee.GetFee(nTxBytes); + // But always obey the maximum + if (nFeeNeeded > maxTxFee) + nFeeNeeded = maxTxFee; + return nFeeNeeded; +} + +CAmount CWallet::GetTotalValue(std::vector vCoins) +{ + CAmount nTotalValue = 0; + CWalletTx wtx; + BOOST_FOREACH (CTxIn i, vCoins) { + if (mapWallet.count(i.prevout.hash)) { + CWalletTx& wtx = mapWallet[i.prevout.hash]; + if (i.prevout.n < wtx.vout.size()) { + nTotalValue += wtx.vout[i.prevout.n].nValue; + } + } else { + LogPrintf("GetTotalValue -- Couldn't find transaction\n"); + } + } + return nTotalValue; +} + +string CWallet::PrepareObfuscationDenominate(int minRounds, int maxRounds) +{ + if (IsLocked()) + return _("Error: Wallet locked, unable to create transaction!"); + + if (obfuScationPool.GetState() != POOL_STATUS_ERROR && obfuScationPool.GetState() != POOL_STATUS_SUCCESS) + if (obfuScationPool.GetEntriesCount() > 0) + return _("Error: You already have pending entries in the Obfuscation pool"); + + // ** find the coins we'll use + std::vector vCoins; + std::vector vCoinsResult; + std::vector vCoins2; + CAmount nValueIn = 0; + CReserveKey reservekey(this); + + /* + Select the coins we'll use + if minRounds >= 0 it means only denominated inputs are going in and coming out + */ + if (minRounds >= 0) { + if (!SelectCoinsByDenominations(obfuScationPool.sessionDenom, 0.1 * COIN, OBFUSCATION_POOL_MAX, vCoins, vCoins2, nValueIn, minRounds, maxRounds)) + return _("Error: Can't select current denominated inputs"); + } + + LogPrintf("PrepareObfuscationDenominate - preparing obfuscation denominate . Got: %d \n", nValueIn); + + { + LOCK(cs_wallet); + BOOST_FOREACH (CTxIn v, vCoins) + LockCoin(v.prevout); + } + + CAmount nValueLeft = nValueIn; + std::vector vOut; + + /* + TODO: Front load with needed denominations (e.g. .1, 1 ) + */ + + // Make outputs by looping through denominations: try to add every needed denomination, repeat up to 5-10 times. + // This way we can be pretty sure that it should have at least one of each needed denomination. + // NOTE: No need to randomize order of inputs because they were + // initially shuffled in CWallet::SelectCoinsByDenominations already. + int nStep = 0; + int nStepsMax = 5 + GetRandInt(5); + while (nStep < nStepsMax) { + BOOST_FOREACH (CAmount v, obfuScationDenominations) { + // only use the ones that are approved + bool fAccepted = false; + if ((obfuScationPool.sessionDenom & (1 << 0)) && v == ((10000 * COIN) + 10000000)) { + fAccepted = true; + } else if ((obfuScationPool.sessionDenom & (1 << 1)) && v == ((1000 * COIN) + 1000000)) { + fAccepted = true; + } else if ((obfuScationPool.sessionDenom & (1 << 2)) && v == ((100 * COIN) + 100000)) { + fAccepted = true; + } else if ((obfuScationPool.sessionDenom & (1 << 3)) && v == ((10 * COIN) + 10000)) { + fAccepted = true; + } else if ((obfuScationPool.sessionDenom & (1 << 4)) && v == ((1 * COIN) + 1000)) { + fAccepted = true; + } else if ((obfuScationPool.sessionDenom & (1 << 5)) && v == ((.1 * COIN) + 100)) { + fAccepted = true; + } + if (!fAccepted) continue; + + // try to add it + if (nValueLeft - v >= 0) { + // Note: this relies on a fact that both vectors MUST have same size + std::vector::iterator it = vCoins.begin(); + std::vector::iterator it2 = vCoins2.begin(); + while (it2 != vCoins2.end()) { + // we have matching inputs + if ((*it2).tx->vout[(*it2).i].nValue == v) { + // add new input in resulting vector + vCoinsResult.push_back(*it); + // remove corresponting items from initial vectors + vCoins.erase(it); + vCoins2.erase(it2); + + CScript scriptChange; + CPubKey vchPubKey; + // use a unique change address + assert(reservekey.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + reservekey.KeepKey(); + + // add new output + CTxOut o(v, scriptChange); + vOut.push_back(o); + + // subtract denomination amount + nValueLeft -= v; + + break; + } + ++it; + ++it2; + } + } + } + + nStep++; + + if (nValueLeft == 0) break; + } + + { + // unlock unused coins + LOCK(cs_wallet); + BOOST_FOREACH (CTxIn v, vCoins) + UnlockCoin(v.prevout); + } + + if (obfuScationPool.GetDenominations(vOut) != obfuScationPool.sessionDenom) { + // unlock used coins on failure + LOCK(cs_wallet); + BOOST_FOREACH (CTxIn v, vCoinsResult) + UnlockCoin(v.prevout); + return "Error: can't make current denominated outputs"; + } + + // randomize the output order + std::random_shuffle(vOut.begin(), vOut.end()); + + // We also do not care about full amount as long as we have right denominations, just pass what we found + obfuScationPool.SendObfuscationDenominate(vCoinsResult, vOut, nValueIn - nValueLeft); + + return ""; +} + +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 + // the requires a new key. + } + } + + if (nLoadWalletRet != DB_LOAD_OK) + return nLoadWalletRet; + fFirstRunRet = !vchDefaultKey.IsValid(); + + uiInterface.LoadWallet(this); + + 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 string& strName, const string& strPurpose) +{ + bool fUpdated = false; + { + LOCK(cs_wallet); // mapAddressBook + std::map::iterator mi = mapAddressBook.find(address); + fUpdated = mi != mapAddressBook.end(); + mapAddressBook[address].name = strName; + if (!strPurpose.empty()) /* update purpose only if requested */ + 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) +{ + { + LOCK(cs_wallet); // mapAddressBook + + if (fFileBacked) { + // Delete destdata tuples associated with address + std::string strAddress = CBitcoinAddress(address).ToString(); + BOOST_FOREACH (const PAIRTYPE(string, string) & 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) { + if (!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); + BOOST_FOREACH (int64_t nIndex, setKeyPool) + walletdb.ErasePool(nIndex); + setKeyPool.clear(); + + if (IsLocked()) + return false; + + int64_t nKeys = max(GetArg("-keypool", 1000), (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 = max(GetArg("-keypool", 1000), (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 runtime_error("TopUpKeyPool() : writing generated key failed"); + setKeyPool.insert(nEnd); + LogPrintf("keypool added key %d, size=%u\n", nEnd, setKeyPool.size()); + double dProgress = 100.f * nEnd / (nTargetSize + 1); + std::string strMsg = strprintf(_("Loading wallet... (%3.2f %%)"), dProgress); + uiInterface.InitMessage(strMsg); + } + } + 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 runtime_error("ReserveKeyFromKeyPool() : read failed"); + if (!HaveKey(keypool.vchPubKey.GetID())) + throw runtime_error("ReserveKeyFromKeyPool() : 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) +{ + int64_t nIndex = 0; + CKeyPool keypool; + { + LOCK(cs_wallet); + 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() +{ + int64_t nIndex = 0; + CKeyPool keypool; + ReserveKeyFromKeyPool(nIndex, keypool); + if (nIndex == -1) + return GetTime(); + ReturnKey(nIndex); + return keypool.nTime; +} + +std::map CWallet::GetAddressBalances() +{ + map balances; + + { + LOCK(cs_wallet); + BOOST_FOREACH (PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet) { + CWalletTx* pcoin = &walletEntry.second; + + if (!IsFinalTx(*pcoin) || !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->vout.size(); i++) { + CTxDestination addr; + if (!IsMine(pcoin->vout[i])) + continue; + if (!ExtractDestination(pcoin->vout[i].scriptPubKey, addr)) + continue; + + CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->vout[i].nValue; + + if (!balances.count(addr)) + balances[addr] = 0; + balances[addr] += n; + } + } + } + + return balances; +} + +set > CWallet::GetAddressGroupings() +{ + AssertLockHeld(cs_wallet); // mapWallet + set > groupings; + set grouping; + + BOOST_FOREACH (PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet) { + CWalletTx* pcoin = &walletEntry.second; + + if (pcoin->vin.size() > 0) { + bool any_mine = false; + // group all input addresses with each other + BOOST_FOREACH (CTxIn txin, pcoin->vin) { + CTxDestination address; + if (!IsMine(txin)) /* If this input isn't mine, ignore it */ + continue; + if (!ExtractDestination(mapWallet[txin.prevout.hash].vout[txin.prevout.n].scriptPubKey, address)) + continue; + grouping.insert(address); + any_mine = true; + } + + // group change with input addresses + if (any_mine) { + BOOST_FOREACH (CTxOut txout, pcoin->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->vout.size(); i++) + if (IsMine(pcoin->vout[i])) { + CTxDestination address; + if (!ExtractDestination(pcoin->vout[i].scriptPubKey, address)) + continue; + grouping.insert(address); + groupings.insert(grouping); + grouping.clear(); + } + } + + set*> uniqueGroupings; // a set of pointers to groups of addresses + map*> setmap; // map addresses to the unique group containing it + BOOST_FOREACH (set grouping, groupings) { + // make a set of all the groups hit by this new group + set*> hits; + map*>::iterator it; + BOOST_FOREACH (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 + set* merged = new set(grouping); + BOOST_FOREACH (set* hit, hits) { + merged->insert(hit->begin(), hit->end()); + uniqueGroupings.erase(hit); + delete hit; + } + uniqueGroupings.insert(merged); + + // update setmap + BOOST_FOREACH (CTxDestination element, *merged) + setmap[element] = merged; + } + + set > ret; + BOOST_FOREACH (set* uniqueGrouping, uniqueGroupings) { + ret.insert(*uniqueGrouping); + delete uniqueGrouping; + } + + return ret; +} + +set CWallet::GetAccountAddresses(string strAccount) const +{ + LOCK(cs_wallet); + set result; + BOOST_FOREACH (const PAIRTYPE(CTxDestination, CAddressBookData) & item, mapAddressBook) { + const CTxDestination& address = item.first; + const 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(set& setAddress) const +{ + setAddress.clear(); + + CWalletDB walletdb(strWalletFile); + + LOCK2(cs_main, cs_wallet); + BOOST_FOREACH (const int64_t& id, setKeyPool) { + CKeyPool keypool; + if (!walletdb.ReadPool(id, keypool)) + throw runtime_error("GetAllReserveKeyHashes() : read failed"); + assert(keypool.vchPubKey.IsValid()); + CKeyID keyID = keypool.vchPubKey.GetID(); + if (!HaveKey(keyID)) + throw runtime_error("GetAllReserveKeyHashes() : unknown key in key pool"); + setAddress.insert(keyID); + } +} + +bool CWallet::UpdatedTransaction(const uint256& hashTx) +{ + { + LOCK(cs_wallet); + // Only notify UI if this transaction is in this wallet + map::const_iterator mi = mapWallet.find(hashTx); + if (mi != mapWallet.end()) { + NotifyTransactionChanged(this, hashTx, CT_UPDATED); + return true; + } + } + return false; +} + +void CWallet::LockCoin(COutPoint& output) +{ + AssertLockHeld(cs_wallet); // setLockedCoins + setLockedCoins.insert(output); +} + +void CWallet::UnlockCoin(COutPoint& output) +{ + AssertLockHeld(cs_wallet); // setLockedCoins + setLockedCoins.erase(output); +} + +void CWallet::UnlockAllCoins() +{ + AssertLockHeld(cs_wallet); // setLockedCoins + setLockedCoins.clear(); +} + +bool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const +{ + AssertLockHeld(cs_wallet); // setLockedCoins + COutPoint outpt(hash, n); + + return (setLockedCoins.count(outpt) > 0); +} + +void CWallet::ListLockedCoins(std::vector& vOutpts) +{ + AssertLockHeld(cs_wallet); // setLockedCoins + 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)) { + BOOST_FOREACH (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 +{ + AssertLockHeld(cs_wallet); // mapKeyMetadata + mapKeyBirth.clear(); + + // get birth times for keys with metadata + for (std::map::const_iterator it = mapKeyMetadata.begin(); it != mapKeyMetadata.end(); it++) + if (it->second.nCreateTime) + mapKeyBirth[it->first] = it->second.nCreateTime; + + // map in which we'll infer heights of other keys + CBlockIndex* pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; // the tip can be reorganised; use a 144-block safety margin + std::map mapKeyFirstBlock; + std::set setKeys; + GetKeys(setKeys); + BOOST_FOREACH (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; + BOOST_FOREACH (const CTxOut& txout, wtx.vout) { + // iterate over all their outputs + CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey); + BOOST_FOREACH (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++) + mapKeyBirth[it->first] = it->second->GetBlockTime() - 7200; // block times can be 2h off +} + +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; +} + +void CWallet::AutoCombineDust() +{ + LOCK(cs_main); + if (IsInitialBlockDownload() || IsLocked()) { + return; + } + + map > mapCoinsByAddress = AvailableCoinsByAddress(true, 0); + + //coins are sectioned by address. This combination code only wants to combine inputs that belong to the same address + for (map >::iterator it = mapCoinsByAddress.begin(); it != mapCoinsByAddress.end(); it++) { + vector vCoins, vRewardCoins; + vCoins = it->second; + + //find masternode rewards that need to be combined + CCoinControl* coinControl = new CCoinControl(); + CAmount nTotalRewardsValue = 0; + BOOST_FOREACH (const COutput& out, vCoins) { + //no coins should get this far if they dont have proper maturity, this is double checking + if (out.tx->IsCoinStake() && out.tx->GetDepthInMainChain() < COINBASE_MATURITY + 1) + continue; + + if (out.Value() > nAutoCombineThreshold * COIN) + continue; + + COutPoint outpt(out.tx->GetHash(), out.i); + coinControl->Select(outpt); + vRewardCoins.push_back(out); + nTotalRewardsValue += out.Value(); + } + + //if no inputs found then return + if (!coinControl->HasSelected()) + continue; + + //we cannot combine one coin with itself + if (vRewardCoins.size() <= 1) + continue; + + vector > vecSend; + CScript scriptPubKey = GetScriptForDestination(it->first.Get()); + vecSend.push_back(make_pair(scriptPubKey, nTotalRewardsValue)); + + // Create the transaction and commit it to the network + CWalletTx wtx; + CReserveKey keyChange(this); // this change address does not end up being used, because change is returned with coin control switch + string strErr; + CAmount nFeeRet = 0; + + //get the fee amount + CWalletTx wtxdummy; + CreateTransaction(vecSend, wtxdummy, keyChange, nFeeRet, strErr, coinControl, ALL_COINS, false, CAmount(0)); + vecSend[0].second = nTotalRewardsValue - nFeeRet - 500; + + if (!CreateTransaction(vecSend, wtx, keyChange, nFeeRet, strErr, coinControl, ALL_COINS, false, CAmount(0))) { + LogPrintf("AutoCombineDust createtransaction failed, reason: %s\n", strErr); + continue; + } + + if (!CommitTransaction(wtx, keyChange)) { + LogPrintf("AutoCombineDust transaction commit failed\n"); + continue; + } + + LogPrintf("AutoCombineDust sent transaction\n"); + + delete coinControl; + } +} + +bool CWallet::MultiSend() +{ + LOCK(cs_main); + if (IsInitialBlockDownload() || IsLocked()) { + return false; + } + + if (chainActive.Tip()->nHeight <= nLastMultiSendHeight) { + LogPrintf("Multisend: lastmultisendheight is higher than current best height\n"); + return false; + } + + std::vector vCoins; + AvailableCoins(vCoins); + int stakeSent = 0; + int mnSent = 0; + BOOST_FOREACH (const COutput& out, vCoins) { + //need output with precise confirm count - this is how we identify which is the output to send + if (out.tx->GetDepthInMainChain() != COINBASE_MATURITY + 1) + continue; + + COutPoint outpoint(out.tx->GetHash(), out.i); + bool sendMSonMNReward = fMultiSendMasternodeReward && outpoint.IsMasternodeReward(out.tx); + bool sendMSOnStake = fMultiSendStake && out.tx->IsCoinStake() && !sendMSonMNReward; //output is either mnreward or stake reward, not both + + if (!(sendMSOnStake || sendMSonMNReward)) + continue; + + CTxDestination destMyAddress; + if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, destMyAddress)) { + LogPrintf("Multisend: failed to extract destination\n"); + continue; + } + + //Disabled Addresses won't send MultiSend transactions + if (vDisabledAddresses.size() > 0) { + for (unsigned int i = 0; i < vDisabledAddresses.size(); i++) { + if (vDisabledAddresses[i] == CBitcoinAddress(destMyAddress).ToString()) { + LogPrintf("Multisend: disabled address preventing multisend\n"); + return false; + } + } + } + + // create new coin control, populate it with the selected utxo, create sending vector + CCoinControl* cControl = new CCoinControl(); + COutPoint outpt(out.tx->GetHash(), out.i); + cControl->Select(outpt); + cControl->destChange = destMyAddress; + + CWalletTx wtx; + CReserveKey keyChange(this); // this change address does not end up being used, because change is returned with coin control switch + CAmount nFeeRet = 0; + vector > vecSend; + + // loop through multisend vector and add amounts and addresses to the sending vector + const isminefilter filter = ISMINE_SPENDABLE; + CAmount nAmount = 0; + for (unsigned int i = 0; i < vMultiSend.size(); i++) { + // MultiSend vector is a pair of 1)Address as a std::string 2) Percent of stake to send as an int + nAmount = ((out.tx->GetCredit(filter) - out.tx->GetDebit(filter)) * vMultiSend[i].second) / 100; + CBitcoinAddress strAddSend(vMultiSend[i].first); + CScript scriptPubKey; + scriptPubKey = GetScriptForDestination(strAddSend.Get()); + vecSend.push_back(make_pair(scriptPubKey, nAmount)); + } + + //get the fee amount + CWalletTx wtxdummy; + string strErr; + CreateTransaction(vecSend, wtxdummy, keyChange, nFeeRet, strErr, cControl, ALL_COINS, false, CAmount(0)); + CAmount nLastSendAmount = vecSend[vecSend.size() - 1].second; + if (nLastSendAmount < nFeeRet + 500) { + LogPrintf("%s: fee of %d is too large to insert into last output\n", __func__, nFeeRet + 500); + return false; + } + vecSend[vecSend.size() - 1].second = nLastSendAmount - nFeeRet - 500; + + // Create the transaction and commit it to the network + if (!CreateTransaction(vecSend, wtx, keyChange, nFeeRet, strErr, cControl, ALL_COINS, false, CAmount(0))) { + LogPrintf("MultiSend createtransaction failed\n"); + return false; + } + + if (!CommitTransaction(wtx, keyChange)) { + LogPrintf("MultiSend transaction commit failed\n"); + return false; + } else + fMultiSendNotify = true; + + delete cControl; + + //write nLastMultiSendHeight to DB + CWalletDB walletdb(strWalletFile); + nLastMultiSendHeight = chainActive.Tip()->nHeight; + if (!walletdb.WriteMSettings(fMultiSendStake, fMultiSendMasternodeReward, nLastMultiSendHeight)) + LogPrintf("Failed to write MultiSend setting to DB\n"); + + LogPrintf("MultiSend successfully sent\n"); + if (sendMSOnStake) + stakeSent++; + else + mnSent++; + + //stop iterating if we are done + if (mnSent > 0 && stakeSent > 0) + return true; + if (stakeSent > 0 && !fMultiSendMasternodeReward) + return true; + if (mnSent > 0 && !fMultiSendStake) + return true; + } + + return true; +} + +CKeyPool::CKeyPool() +{ + nTime = GetTime(); +} + +CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn) +{ + nTime = GetTime(); + vchPubKey = vchPubKeyIn; +} + +CWalletKey::CWalletKey(int64_t nExpires) +{ + nTimeCreated = (nExpires ? GetTime() : 0); + nTimeExpires = nExpires; +} + +int CMerkleTx::SetMerkleBranch(const CBlock& block) +{ + AssertLockHeld(cs_main); + CBlock blockTmp; + + // Update the tx's hashBlock + hashBlock = block.GetHash(); + + // Locate the transaction + for (nIndex = 0; nIndex < (int)block.vtx.size(); nIndex++) + if (block.vtx[nIndex] == *(CTransaction*)this) + break; + if (nIndex == (int)block.vtx.size()) { + vMerkleBranch.clear(); + nIndex = -1; + LogPrintf("ERROR: SetMerkleBranch() : couldn't find tx in block\n"); + return 0; + } + + // Fill in merkle branch + vMerkleBranch = block.GetMerkleBranch(nIndex); + + // Is the tx in a block that's in the main chain + BlockMap::iterator mi = mapBlockIndex.find(hashBlock); + if (mi == mapBlockIndex.end()) + return 0; + const CBlockIndex* pindex = (*mi).second; + if (!pindex || !chainActive.Contains(pindex)) + return 0; + + return chainActive.Height() - pindex->nHeight + 1; +} + +int CMerkleTx::GetDepthInMainChainINTERNAL(const CBlockIndex*& pindexRet) const +{ + if (hashBlock == 0 || nIndex == -1) + 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; + + // Make sure the merkle branch connects to this block + if (!fMerkleVerified) { + if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) + return 0; + fMerkleVerified = true; + } + + pindexRet = pindex; + return chainActive.Height() - pindex->nHeight + 1; +} + +int CMerkleTx::GetDepthInMainChain(const CBlockIndex*& pindexRet, bool enableIX) const +{ + AssertLockHeld(cs_main); + int nResult = GetDepthInMainChainINTERNAL(pindexRet); + if (nResult == 0 && !mempool.exists(GetHash())) + return -1; // Not in chain, not in mempool + + if (enableIX) { + if (nResult < 6) { + int signatures = GetTransactionLockSignatures(); + if (signatures >= SWIFTTX_SIGNATURES_REQUIRED) { + return nSwiftTXDepth + nResult; + } + } + } + + return nResult; +} + +int CMerkleTx::GetBlocksToMaturity() const +{ + LOCK(cs_main); + if (!(IsCoinBase() || IsCoinStake())) + return 0; + return max(0, (Params().COINBASE_MATURITY() + 1) - GetDepthInMainChain()); +} + + +bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectInsaneFee, bool ignoreFees) +{ + CValidationState state; + bool fAccepted = ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, fRejectInsaneFee, ignoreFees); + if (!fAccepted) + LogPrintf("%s : %s\n", __func__, state.GetRejectReason()); + return fAccepted; +} + +int CMerkleTx::GetTransactionLockSignatures() const +{ + if (fLargeWorkForkFound || fLargeWorkInvalidChainFound) return -2; + + //compile consessus vote + std::map::iterator i = mapTxLocks.find(GetHash()); + if (i != mapTxLocks.end()) { + return (*i).second.CountSignatures(); + } + + return -1; +} + +bool CMerkleTx::IsTransactionLockTimedOut() const +{ + //compile consessus vote + std::map::iterator i = mapTxLocks.find(GetHash()); + if (i != mapTxLocks.end()) { + return GetTime() > (*i).second.nTimeout; + } + + return false; +} diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h new file mode 100644 index 0000000..b272d81 --- /dev/null +++ b/src/wallet/wallet.h @@ -0,0 +1,1363 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Copyright (c) 2014-2015 The Dash developers +// Copyright (c) 2015-2017 The PIVX developers +// Copyright (c) 2017-2019 The XDNA Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_WALLET_H +#define BITCOIN_WALLET_H + +#include "amount.h" +#include "base58.h" +#include "crypter.h" +#include "kernel.h" +#include "key.h" +#include "keystore.h" +#include "main.h" +#include "primitives/block.h" +#include "primitives/transaction.h" +#include "ui_interface.h" +#include "util.h" +#include "validationinterface.h" +#include "wallet_ismine.h" +#include "walletdb.h" +#include "masternode.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Settings + */ +extern CFeeRate payTxFee; +extern CAmount maxTxFee; +extern unsigned int nTxConfirmTarget; +extern bool bSpendZeroConfChange; +extern bool bdisableSystemnotifications; +extern bool fSendFreeTransactions; +extern bool fPayAtLeastCustomFee; + +//! -paytxfee default +static const CAmount DEFAULT_TRANSACTION_FEE = 0; +//! -paytxfee will warn if called with a higher fee than this amount (in satoshis) per KB +static const CAmount nHighTransactionFeeWarning = 0.1 * COIN; +//! -maxtxfee default +static const CAmount DEFAULT_TRANSACTION_MAXFEE = 1 * COIN; +//! -maxtxfee will warn if called with a higher fee than this amount (in satoshis) +static const CAmount nHighTransactionMaxFeeWarning = 100 * nHighTransactionFeeWarning; +//! Largest (in bytes) free transaction we're willing to create +static const unsigned int MAX_FREE_TRANSACTION_CREATE_SIZE = 1000; + +class CAccountingEntry; +class CCoinControl; +class COutput; +class CReserveKey; +class CScript; +class CWalletTx; + +/** (client) version numbers for particular wallet features */ +enum WalletFeature { + FEATURE_BASE = 10500, // the earliest version new wallets supports (only useful for getinfo's clientversion output) + + FEATURE_WALLETCRYPT = 40000, // wallet encryption + FEATURE_COMPRPUBKEY = 60000, // compressed public keys + + FEATURE_LATEST = 61000 +}; + +enum AvailableCoinsType { + ALL_COINS = 1, + ONLY_DENOMINATED = 2, + ONLY_NOTDEPOSITIFMN = 3, + ONLY_NONDENOMINATED_NOTDEPOSITIFMN = 4, // ONLY_NONDENOMINATED and not deposit XDNA at the same time + ONLY_DEPOSIT = 5, // find masternode outputs including locked ones (use with caution) + STAKABLE_COINS = 6 // UTXO's that are valid for staking +}; + +struct CompactTallyItem { + CBitcoinAddress address; + CAmount nAmount; + std::vector vecTxIn; + CompactTallyItem() + { + nAmount = 0; + } +}; + +/** A key pool entry */ +class CKeyPool +{ +public: + int64_t nTime; + CPubKey vchPubKey; + + CKeyPool(); + CKeyPool(const CPubKey& vchPubKeyIn); + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) + { + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(nTime); + READWRITE(vchPubKey); + } +}; + +/** Address book data */ +class CAddressBookData +{ +public: + std::string name; + std::string purpose; + + CAddressBookData() + { + purpose = "unknown"; + } + + typedef std::map StringMap; + StringMap destdata; +}; + +/** + * A CWallet is an extension of a keystore, which also maintains a set of transactions and balances, + * and provides the ability to create new transactions. + */ +class CWallet : public CCryptoKeyStore, public CValidationInterface +{ +private: + bool SelectCoins(const CAmount& nTargetValue, std::set >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl = NULL, AvailableCoinsType coin_type = ALL_COINS, bool useIX = true) const; + //it was public bool SelectCoins(int64_t nTargetValue, std::set >& setCoinsRet, int64_t& nValueRet, const CCoinControl *coinControl = NULL, AvailableCoinsType coin_type=ALL_COINS, bool useIX = true) const; + + CWalletDB* pwalletdbEncryption; + + //! the current wallet version: clients below this version are not able to load the wallet + int nWalletVersion; + + //! the maximum wallet format version: memory-only variable that specifies to what version this wallet may be upgraded + int nWalletMaxVersion; + + int64_t nNextResend; + int64_t nLastResend; + + /** + * Used to keep track of spent outpoints, and + * detect and report conflicts (double-spends or + * mutated transactions where the mutant gets mined). + */ + typedef std::multimap TxSpends; + TxSpends mapTxSpends; + void AddToSpends(const COutPoint& outpoint, const uint256& wtxid); + void AddToSpends(const uint256& wtxid); + + void SyncMetaData(std::pair); + +public: + bool MintableCoins(); + bool SelectStakeCoins(std::set >& setCoins, CAmount nTargetAmount) const; + bool SelectCoinsDark(CAmount nValueMin, CAmount nValueMax, std::vector& setCoinsRet, CAmount& nValueRet, int nObfuscationRoundsMin, int nObfuscationRoundsMax) const; + bool SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount nValueMax, std::vector& vCoinsRet, std::vector& vCoinsRet2, CAmount& nValueRet, int nObfuscationRoundsMin, int nObfuscationRoundsMax); + bool SelectCoinsDarkDenominated(CAmount nTargetValue, std::vector& setCoinsRet, CAmount& nValueRet) const; + bool HasCollateralInputs(bool fOnlyConfirmed = true) const; + bool IsCollateralAmount(CAmount nInputAmount) const; + int CountInputsWithAmount(CAmount nInputAmount); + + bool SelectCoinsCollateral(std::vector& setCoinsRet, CAmount& nValueRet) const; + string GetUniqueWalletBackupName() const; + + /* + * Main wallet lock. + * This lock protects all the fields added by CWallet + * except for: + * fFileBacked (immutable after instantiation) + * strWalletFile (immutable after instantiation) + */ + mutable CCriticalSection cs_wallet; + + bool fFileBacked; + bool fWalletUnlockAnonymizeOnly; + std::string strWalletFile; + + std::set setKeyPool; + std::map mapKeyMetadata; + + typedef std::map MasterKeyMap; + MasterKeyMap mapMasterKeys; + unsigned int nMasterKeyMaxID; + + // Stake Settings + unsigned int nHashDrift; + unsigned int nHashInterval; + uint64_t nStakeSplitThreshold; + int nStakeSetUpdateTime; + + //MultiSend + std::vector > vMultiSend; + bool fMultiSendStake; + bool fMultiSendMasternodeReward; + bool fMultiSendNotify; + std::string strMultiSendChangeAddress; + int nLastMultiSendHeight; + std::vector vDisabledAddresses; + + //Auto Combine Inputs + bool fCombineDust; + CAmount nAutoCombineThreshold; + + CWallet() + { + SetNull(); + } + + CWallet(std::string strWalletFileIn) + { + SetNull(); + + strWalletFile = strWalletFileIn; + fFileBacked = true; + } + + ~CWallet() + { + delete pwalletdbEncryption; + } + + void SetNull() + { + nWalletVersion = FEATURE_BASE; + nWalletMaxVersion = FEATURE_BASE; + fFileBacked = false; + nMasterKeyMaxID = 0; + pwalletdbEncryption = NULL; + nOrderPosNext = 0; + nNextResend = 0; + nLastResend = 0; + nTimeFirstKey = 0; + fWalletUnlockAnonymizeOnly = false; + + // Stake Settings + nHashDrift = 180; + nStakeSplitThreshold = 500; + nHashInterval = 22; + nStakeSetUpdateTime = 300; // 5 minutes + + //MultiSend + vMultiSend.clear(); + fMultiSendStake = false; + fMultiSendMasternodeReward = false; + fMultiSendNotify = false; + strMultiSendChangeAddress = ""; + nLastMultiSendHeight = 0; + vDisabledAddresses.clear(); + + //Auto Combine Dust + fCombineDust = false; + nAutoCombineThreshold = 0; + } + + bool isMultiSendEnabled() + { + return fMultiSendMasternodeReward || fMultiSendStake; + } + + void setMultiSendDisabled() + { + fMultiSendMasternodeReward = false; + fMultiSendStake = false; + } + + std::map mapWallet; + + int64_t nOrderPosNext; + std::map mapRequestCount; + + std::map mapAddressBook; + + CPubKey vchDefaultKey; + + std::set setLockedCoins; + + int64_t nTimeFirstKey; + + const CWalletTx* GetWalletTx(const uint256& hash) const; + + //! check whether we are allowed to upgrade (or already support) to the named feature + bool CanSupportFeature(enum WalletFeature wf) + { + AssertLockHeld(cs_wallet); + return nWalletMaxVersion >= wf; + } + + void AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed = true, const CCoinControl* coinControl = NULL, bool fIncludeZeroValue = false, AvailableCoinsType nCoinType = ALL_COINS, bool fUseIX = false) const; + std::map > AvailableCoinsByAddress(bool fConfirmed = true, CAmount maxCoinValue = 0); + bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, std::vector vCoins, std::set >& setCoinsRet, CAmount& nValueRet) const; + + /// Get 1000DASH output and keys which can be used for the Masternode + bool GetMasternodeVinAndKeys(CTxIn& txinRet, CPubKey& pubKeyRet, CKey& keyRet, std::string strTxHash = "", std::string strOutputIndex = ""); + /// Extract txin information and keys from output + bool GetVinAndKeysFromOutput(COutput out, CTxIn& txinRet, CPubKey& pubKeyRet, CKey& keyRet); + + bool IsSpent(const uint256& hash, unsigned int n) const; + + bool IsLockedCoin(uint256 hash, unsigned int n) const; + void LockCoin(COutPoint& output); + void UnlockCoin(COutPoint& output); + void UnlockAllCoins(); + void ListLockedCoins(std::vector& vOutpts); + CAmount GetTotalValue(std::vector vCoins); + + // keystore implementation + // Generate a new key + CPubKey GenerateNewKey(); + + //! Adds a key to the store, and saves it to disk. + bool AddKeyPubKey(const CKey& key, const CPubKey& pubkey); + //! Adds a key to the store, without saving it to disk (used by LoadWallet) + bool LoadKey(const CKey& key, const CPubKey& pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); } + //! Load metadata (used by LoadWallet) + bool LoadKeyMetadata(const CPubKey& pubkey, const CKeyMetadata& metadata); + + bool LoadMinVersion(int nVersion) + { + AssertLockHeld(cs_wallet); + nWalletVersion = nVersion; + nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); + return true; + } + + //! Adds an encrypted key to the store, and saves it to disk. + bool AddCryptedKey(const CPubKey& vchPubKey, const std::vector& vchCryptedSecret); + //! Adds an encrypted key to the store, without saving it to disk (used by LoadWallet) + bool LoadCryptedKey(const CPubKey& vchPubKey, const std::vector& vchCryptedSecret); + bool AddCScript(const CScript& redeemScript); + bool LoadCScript(const CScript& redeemScript); + + //! Adds a destination data tuple to the store, and saves it to disk + bool AddDestData(const CTxDestination& dest, const std::string& key, const std::string& value); + //! Erases a destination data tuple in the store and on disk + bool EraseDestData(const CTxDestination& dest, const std::string& key); + //! Adds a destination data tuple to the store, without saving it to disk + bool LoadDestData(const CTxDestination& dest, const std::string& key, const std::string& value); + //! Look up a destination data tuple in the store, return true if found false otherwise + bool GetDestData(const CTxDestination& dest, const std::string& key, std::string* value) const; + + //! Adds a watch-only address to the store, and saves it to disk. + bool AddWatchOnly(const CScript& dest); + bool RemoveWatchOnly(const CScript& dest); + //! Adds a watch-only address to the store, without saving it to disk (used by LoadWallet) + bool LoadWatchOnly(const CScript& dest); + + //! Adds a MultiSig address to the store, and saves it to disk. + bool AddMultiSig(const CScript& dest); + bool RemoveMultiSig(const CScript& dest); + //! Adds a MultiSig address to the store, without saving it to disk (used by LoadWallet) + bool LoadMultiSig(const CScript& dest); + + bool Unlock(const SecureString& strWalletPassphrase, bool anonimizeOnly = false); + bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); + bool EncryptWallet(const SecureString& strWalletPassphrase); + + void GetKeyBirthTimes(std::map& mapKeyBirth) const; + unsigned int ComputeTimeSmart(const CWalletTx& wtx) const; + + /** + * Increment the next transaction order id + * @return next transaction order id + */ + int64_t IncOrderPosNext(CWalletDB* pwalletdb = NULL); + + typedef std::pair TxPair; + typedef std::multimap TxItems; + TxItems wtxOrdered; + + /** + * Get the wallet's activity log + * @return multimap of ordered transactions and accounting entries + * @warning Returned pointers are *only* valid within the scope of passed acentries + */ + TxItems OrderedTxItems(std::list& acentries, std::string strAccount = ""); + + void MarkDirty(); + bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet = false); + void SyncTransaction(const CTransaction& tx, const CBlock* pblock); + bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate); + void EraseFromWallet(const uint256& hash); + int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); + void ReacceptWalletTransactions(); + void ResendWalletTransactions(); + CAmount GetBalance() const; + CAmount GetUnconfirmedBalance() const; + CAmount GetImmatureBalance() const; + CAmount GetAnonymizableBalance() const; + CAmount GetAnonymizedBalance() const; + double GetAverageAnonymizedRounds() const; + CAmount GetNormalizedAnonymizedBalance() const; + CAmount GetDenominatedBalance(bool unconfirmed = false) const; + CAmount GetWatchOnlyBalance() const; + CAmount GetUnconfirmedWatchOnlyBalance() const; + CAmount GetImmatureWatchOnlyBalance() const; + bool CreateTransaction(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl); + bool CreateTransaction(const std::vector >& vecSend, + CWalletTx& wtxNew, + CReserveKey& reservekey, + CAmount& nFeeRet, + std::string& strFailReason, + const CCoinControl* coinControl = NULL, + AvailableCoinsType coin_type = ALL_COINS, + bool useIX = false, + CAmount nFeePay = 0); + bool CreateTransaction(CScript scriptPubKey, const CAmount& nValue, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl = NULL, AvailableCoinsType coin_type = ALL_COINS, bool useIX = false, CAmount nFeePay = 0); + bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, std::string strCommand = "tx"); + std::string PrepareObfuscationDenominate(int minRounds, int maxRounds); + int GenerateObfuscationOutputs(int nTotalValue, std::vector& vout); + bool CreateCollateralTransaction(CMutableTransaction& txCollateral, std::string& strReason); + bool ConvertList(std::vector vCoins, std::vector& vecAmounts); + bool CreateCoinStake(const CKeyStore& keystore, uint32_t nTime, unsigned int nBits, int64_t nSearchInterval, CMutableTransaction& txNew, unsigned int& nTxNewTime); + bool MultiSend(); + void AutoCombineDust(); + + static CFeeRate minTxFee; + static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool); + + bool NewKeyPool(); + bool TopUpKeyPool(unsigned int kpSize = 0); + void ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool); + void KeepKey(int64_t nIndex); + void ReturnKey(int64_t nIndex); + bool GetKeyFromPool(CPubKey& key); + int64_t GetOldestKeyPoolTime(); + void GetAllReserveKeys(std::set& setAddress) const; + + std::set > GetAddressGroupings(); + std::map GetAddressBalances(); + + std::set GetAccountAddresses(std::string strAccount) const; + + // get the Obfuscation chain depth for a given input + int GetRealInputObfuscationRounds(CTxIn in, int rounds) const; + // respect current settings + int GetInputObfuscationRounds(CTxIn in) const; + + bool IsDenominated(const CTxIn& txin) const; + bool IsDenominated(const CTransaction& tx) const; + + bool IsDenominatedAmount(CAmount nInputAmount) const; + + isminetype IsMine(const CTxIn& txin) const; + CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const; + isminetype IsMine(const CTxOut& txout) const + { + return ::IsMine(*this, txout.scriptPubKey); + } + CAmount GetCredit(const CTxOut& txout, const isminefilter& filter) const + { + if (!MoneyRange(txout.nValue)) + throw std::runtime_error("CWallet::GetCredit() : value out of range"); + return ((IsMine(txout) & filter) ? txout.nValue : 0); + } + bool IsChange(const CTxOut& txout) const; + CAmount GetChange(const CTxOut& txout) const + { + if (!MoneyRange(txout.nValue)) + throw std::runtime_error("CWallet::GetChange() : value out of range"); + return (IsChange(txout) ? txout.nValue : 0); + } + bool IsMine(const CTransaction& tx) const + { + BOOST_FOREACH (const CTxOut& txout, tx.vout) + if (IsMine(txout)) + return true; + return false; + } + /** should probably be renamed to IsRelevantToMe */ + bool IsFromMe(const CTransaction& tx) const + { + return (GetDebit(tx, ISMINE_ALL) > 0); + } + CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const + { + CAmount nDebit = 0; + BOOST_FOREACH (const CTxIn& txin, tx.vin) { + nDebit += GetDebit(txin, filter); + if (!MoneyRange(nDebit)) + throw std::runtime_error("CWallet::GetDebit() : value out of range"); + } + return nDebit; + } + CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const + { + CAmount nCredit = 0; + BOOST_FOREACH (const CTxOut& txout, tx.vout) { + nCredit += GetCredit(txout, filter); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWallet::GetCredit() : value out of range"); + } + return nCredit; + } + CAmount GetChange(const CTransaction& tx) const + { + CAmount nChange = 0; + BOOST_FOREACH (const CTxOut& txout, tx.vout) { + nChange += GetChange(txout); + if (!MoneyRange(nChange)) + throw std::runtime_error("CWallet::GetChange() : value out of range"); + } + return nChange; + } + void SetBestChain(const CBlockLocator& loc); + + DBErrors LoadWallet(bool& fFirstRunRet); + DBErrors ZapWalletTx(std::vector& vWtx); + + bool SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& purpose); + + bool DelAddressBook(const CTxDestination& address); + + bool UpdatedTransaction(const uint256& hashTx); + + void Inventory(const uint256& hash) + { + { + LOCK(cs_wallet); + std::map::iterator mi = mapRequestCount.find(hash); + if (mi != mapRequestCount.end()) + (*mi).second++; + } + } + + unsigned int GetKeyPoolSize() + { + AssertLockHeld(cs_wallet); // setKeyPool + return setKeyPool.size(); + } + + bool SetDefaultKey(const CPubKey& vchPubKey); + + //! signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVersion if those are lower + bool SetMinVersion(enum WalletFeature, CWalletDB* pwalletdbIn = NULL, bool fExplicit = false); + + //! change which version we're allowed to upgrade to (note that this does not immediately imply upgrading to that format) + bool SetMaxVersion(int nVersion); + + //! get the current wallet format (the oldest client version guaranteed to understand this wallet) + int GetVersion() + { + LOCK(cs_wallet); + return nWalletVersion; + } + + //! Get wallet transactions that conflict with given transaction (spend same outputs) + std::set GetConflicts(const uint256& txid) const; + + /** + * Address book entry changed. + * @note called with lock cs_wallet held. + */ + boost::signals2::signal NotifyAddressBookChanged; + + /** + * Wallet transaction added, removed or updated. + * @note called with lock cs_wallet held. + */ + boost::signals2::signal NotifyTransactionChanged; + + /** Show progress e.g. for rescan */ + boost::signals2::signal ShowProgress; + + /** Watch-only address added */ + boost::signals2::signal NotifyWatchonlyChanged; + + /** MultiSig address added */ + boost::signals2::signal NotifyMultiSigChanged; +}; + + +/** A key allocated from the key pool. */ +class CReserveKey +{ +protected: + CWallet* pwallet; + int64_t nIndex; + CPubKey vchPubKey; + +public: + CReserveKey(CWallet* pwalletIn) + { + nIndex = -1; + pwallet = pwalletIn; + } + + ~CReserveKey() + { + ReturnKey(); + } + + void ReturnKey(); + bool GetReservedKey(CPubKey& pubkey); + void KeepKey(); +}; + + +typedef std::map mapValue_t; + + +static void ReadOrderPos(int64_t& nOrderPos, mapValue_t& mapValue) +{ + if (!mapValue.count("n")) { + nOrderPos = -1; // TODO: calculate elsewhere + return; + } + nOrderPos = atoi64(mapValue["n"].c_str()); +} + + +static void WriteOrderPos(const int64_t& nOrderPos, mapValue_t& mapValue) +{ + if (nOrderPos == -1) + return; + mapValue["n"] = i64tostr(nOrderPos); +} + +struct COutputEntry { + CTxDestination destination; + CAmount amount; + int vout; +}; + +/** A transaction with a merkle branch linking it to the block chain. */ +class CMerkleTx : public CTransaction +{ +private: + int GetDepthInMainChainINTERNAL(const CBlockIndex*& pindexRet) const; + +public: + uint256 hashBlock; + std::vector vMerkleBranch; + int nIndex; + + // memory only + mutable bool fMerkleVerified; + + + CMerkleTx() + { + Init(); + } + + CMerkleTx(const CTransaction& txIn) : CTransaction(txIn) + { + Init(); + } + + void Init() + { + hashBlock = 0; + nIndex = -1; + fMerkleVerified = false; + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) + { + READWRITE(*(CTransaction*)this); + nVersion = this->nVersion; + READWRITE(hashBlock); + READWRITE(vMerkleBranch); + READWRITE(nIndex); + } + + int SetMerkleBranch(const CBlock& block); + + + /** + * Return depth of transaction in blockchain: + * -1 : not in blockchain, and not in memory pool (conflicted transaction) + * 0 : in memory pool, waiting to be included in a block + * >=1 : this many blocks deep in the main chain + */ + int GetDepthInMainChain(const CBlockIndex*& pindexRet, bool enableIX = true) const; + int GetDepthInMainChain(bool enableIX = true) const + { + const CBlockIndex* pindexRet; + return GetDepthInMainChain(pindexRet, enableIX); + } + bool IsInMainChain() const + { + const CBlockIndex* pindexRet; + return GetDepthInMainChainINTERNAL(pindexRet) > 0; + } + int GetBlocksToMaturity() const; + bool AcceptToMemoryPool(bool fLimitFree = true, bool fRejectInsaneFee = true, bool ignoreFees = false); + int GetTransactionLockSignatures() const; + bool IsTransactionLockTimedOut() const; +}; + +/** + * A transaction with a bunch of additional info that only the owner cares about. + * It includes any unrecorded transactions needed to link it back to the block chain. + */ +class CWalletTx : public CMerkleTx +{ +private: + const CWallet* pwallet; + +public: + mapValue_t mapValue; + std::vector > vOrderForm; + unsigned int fTimeReceivedIsTxTime; + unsigned int nTimeReceived; //! time received by this node + unsigned int nTimeSmart; + char fFromMe; + std::string strFromAccount; + int64_t nOrderPos; //! position in ordered transaction list + + // memory only + mutable bool fDebitCached; + mutable bool fCreditCached; + mutable bool fImmatureCreditCached; + mutable bool fAvailableCreditCached; + mutable bool fAnonymizableCreditCached; + mutable bool fAnonymizedCreditCached; + mutable bool fDenomUnconfCreditCached; + mutable bool fDenomConfCreditCached; + mutable bool fWatchDebitCached; + mutable bool fWatchCreditCached; + mutable bool fImmatureWatchCreditCached; + mutable bool fAvailableWatchCreditCached; + mutable bool fChangeCached; + mutable CAmount nDebitCached; + mutable CAmount nCreditCached; + mutable CAmount nImmatureCreditCached; + mutable CAmount nAvailableCreditCached; + mutable CAmount nAnonymizableCreditCached; + mutable CAmount nAnonymizedCreditCached; + mutable CAmount nDenomUnconfCreditCached; + mutable CAmount nDenomConfCreditCached; + mutable CAmount nWatchDebitCached; + mutable CAmount nWatchCreditCached; + mutable CAmount nImmatureWatchCreditCached; + mutable CAmount nAvailableWatchCreditCached; + mutable CAmount nChangeCached; + + CWalletTx() + { + Init(NULL); + } + + CWalletTx(const CWallet* pwalletIn) + { + Init(pwalletIn); + } + + CWalletTx(const CWallet* pwalletIn, const CMerkleTx& txIn) : CMerkleTx(txIn) + { + Init(pwalletIn); + } + + CWalletTx(const CWallet* pwalletIn, const CTransaction& txIn) : CMerkleTx(txIn) + { + Init(pwalletIn); + } + + void Init(const CWallet* pwalletIn) + { + pwallet = pwalletIn; + mapValue.clear(); + vOrderForm.clear(); + fTimeReceivedIsTxTime = false; + nTimeReceived = 0; + nTimeSmart = 0; + fFromMe = false; + strFromAccount.clear(); + fDebitCached = false; + fCreditCached = false; + fImmatureCreditCached = false; + fAvailableCreditCached = false; + fAnonymizableCreditCached = false; + fAnonymizedCreditCached = false; + fDenomUnconfCreditCached = false; + fDenomConfCreditCached = false; + fWatchDebitCached = false; + fWatchCreditCached = false; + fImmatureWatchCreditCached = false; + fAvailableWatchCreditCached = false; + fChangeCached = false; + nDebitCached = 0; + nCreditCached = 0; + nImmatureCreditCached = 0; + nAvailableCreditCached = 0; + nAnonymizableCreditCached = 0; + nAnonymizedCreditCached = 0; + nDenomUnconfCreditCached = 0; + nDenomConfCreditCached = 0; + nWatchDebitCached = 0; + nWatchCreditCached = 0; + nAvailableWatchCreditCached = 0; + nImmatureWatchCreditCached = 0; + nChangeCached = 0; + nOrderPos = -1; + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) + { + if (ser_action.ForRead()) + Init(NULL); + char fSpent = false; + + if (!ser_action.ForRead()) { + mapValue["fromaccount"] = strFromAccount; + + WriteOrderPos(nOrderPos, mapValue); + + if (nTimeSmart) + mapValue["timesmart"] = strprintf("%u", nTimeSmart); + } + + READWRITE(*(CMerkleTx*)this); + std::vector vUnused; //! Used to be vtxPrev + READWRITE(vUnused); + READWRITE(mapValue); + READWRITE(vOrderForm); + READWRITE(fTimeReceivedIsTxTime); + READWRITE(nTimeReceived); + READWRITE(fFromMe); + READWRITE(fSpent); + + if (ser_action.ForRead()) { + strFromAccount = mapValue["fromaccount"]; + + ReadOrderPos(nOrderPos, mapValue); + + nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(mapValue["timesmart"]) : 0; + } + + mapValue.erase("fromaccount"); + mapValue.erase("version"); + mapValue.erase("spent"); + mapValue.erase("n"); + mapValue.erase("timesmart"); + } + + //! make sure balances are recalculated + void MarkDirty() + { + fCreditCached = false; + fAvailableCreditCached = false; + fAnonymizableCreditCached = false; + fAnonymizedCreditCached = false; + fDenomUnconfCreditCached = false; + fDenomConfCreditCached = false; + fWatchDebitCached = false; + fWatchCreditCached = false; + fAvailableWatchCreditCached = false; + fImmatureWatchCreditCached = false; + fDebitCached = false; + fChangeCached = false; + } + + void BindWallet(CWallet* pwalletIn) + { + pwallet = pwalletIn; + MarkDirty(); + } + + //! filter decides which addresses will count towards the debit + CAmount GetDebit(const isminefilter& filter) const + { + if (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 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 GetImmatureCredit(bool fUseCache = true) const + { + if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0 && IsInMainChain()) { + if (fUseCache && fImmatureCreditCached) + return nImmatureCreditCached; + nImmatureCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE); + fImmatureCreditCached = true; + return nImmatureCreditCached; + } + + return 0; + } + + CAmount GetAvailableCredit(bool fUseCache = true) 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 = GetHash(); + for (unsigned int i = 0; i < vout.size(); i++) { + if (!pwallet->IsSpent(hashTx, i)) { + const CTxOut& txout = 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 GetAnonymizableCredit(bool fUseCache = true) 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 && fAnonymizableCreditCached) + return nAnonymizableCreditCached; + + CAmount nCredit = 0; + uint256 hashTx = GetHash(); + for (unsigned int i = 0; i < vout.size(); i++) { + const CTxOut& txout = vout[i]; + const CTxIn vin = CTxIn(hashTx, i); + + if (pwallet->IsSpent(hashTx, i) || pwallet->IsLockedCoin(hashTx, i)) continue; + if (fMasterNode && CMasternode::IsDepositCoins(vout[i].nValue)) + continue; // do not count MN-like outputs + + const int rounds = pwallet->GetInputObfuscationRounds(vin); + if (rounds >= -2 && rounds < nObfuscationRounds) { + nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetAnonamizableCredit() : value out of range"); + } + } + + nAnonymizableCreditCached = nCredit; + fAnonymizableCreditCached = true; + return nCredit; + } + + CAmount GetAnonymizedCredit(bool fUseCache = true) 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 && fAnonymizedCreditCached) + return nAnonymizedCreditCached; + + CAmount nCredit = 0; + uint256 hashTx = GetHash(); + for (unsigned int i = 0; i < vout.size(); i++) { + const CTxOut& txout = vout[i]; + const CTxIn vin = CTxIn(hashTx, i); + + if (pwallet->IsSpent(hashTx, i) || !pwallet->IsDenominated(vin)) continue; + + const int rounds = pwallet->GetInputObfuscationRounds(vin); + if (rounds >= nObfuscationRounds) { + nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetAnonymizedCredit() : value out of range"); + } + } + + nAnonymizedCreditCached = nCredit; + fAnonymizedCreditCached = true; + return nCredit; + } + + CAmount GetDenominatedCredit(bool unconfirmed, bool fUseCache = true) 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; + + int nDepth = GetDepthInMainChain(false); + if (nDepth < 0) return 0; + + bool isUnconfirmed = !IsFinalTx(*this) || (!IsTrusted() && nDepth == 0); + if (unconfirmed != isUnconfirmed) return 0; + + if (fUseCache) { + if (unconfirmed && fDenomUnconfCreditCached) + return nDenomUnconfCreditCached; + else if (!unconfirmed && fDenomConfCreditCached) + return nDenomConfCreditCached; + } + + CAmount nCredit = 0; + uint256 hashTx = GetHash(); + for (unsigned int i = 0; i < vout.size(); i++) { + const CTxOut& txout = vout[i]; + + if (pwallet->IsSpent(hashTx, i) || !pwallet->IsDenominatedAmount(vout[i].nValue)) continue; + + nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetDenominatedCredit() : value out of range"); + } + + if (unconfirmed) { + nDenomUnconfCreditCached = nCredit; + fDenomUnconfCreditCached = true; + } else { + nDenomConfCreditCached = nCredit; + fDenomConfCreditCached = true; + } + return nCredit; + } + + CAmount GetImmatureWatchOnlyCredit(const bool& fUseCache = true) 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 GetAvailableWatchOnlyCredit(const bool& fUseCache = true) 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 < vout.size(); i++) { + if (!pwallet->IsSpent(GetHash(), i)) { + const CTxOut& txout = 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 GetChange() const + { + if (fChangeCached) + return nChangeCached; + nChangeCached = pwallet->GetChange(*this); + fChangeCached = true; + return nChangeCached; + } + + void GetAmounts(std::list& listReceived, + std::list& listSent, + CAmount& nFee, + std::string& strSentAccount, + const isminefilter& filter) const; + + void GetAccountAmounts(const std::string& strAccount, CAmount& nReceived, CAmount& nSent, CAmount& nFee, const isminefilter& filter) const; + + bool IsFromMe(const isminefilter& filter) const + { + return (GetDebit(filter) > 0); + } + + bool InMempool() const; + + bool IsTrusted() const + { + // Quick answer in most cases + if (!IsFinalTx(*this)) + return false; + int nDepth = GetDepthInMainChain(); + if (nDepth >= 1) + return true; + if (nDepth < 0) + return false; + if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) // using wtx's cached debit + return false; + + // Trusted if all inputs are from us and are in the mempool: + BOOST_FOREACH (const CTxIn& txin, vin) { + // Transactions not sent by us: not trusted + const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash); + if (parent == NULL) + return false; + const CTxOut& parentOut = parent->vout[txin.prevout.n]; + if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) + return false; + } + return true; + } + + bool WriteToDisk(); + + int64_t GetTxTime() const; + int GetRequestCount() const; + void RelayWalletTransaction(std::string strCommand = "tx"); + + std::set GetConflicts() const; +}; + + +class COutput +{ +public: + const CWalletTx* tx; + int i; + int nDepth; + bool fSpendable; + + COutput(const CWalletTx* txIn, int iIn, int nDepthIn, bool fSpendableIn) + { + tx = txIn; + i = iIn; + nDepth = nDepthIn; + fSpendable = fSpendableIn; + } + + //Used with Obfuscation. Will return largest nondenom, then denominations, then very small inputs + int Priority() const + { + BOOST_FOREACH (CAmount d, obfuScationDenominations) + if (tx->vout[i].nValue == d) return 10000; + if (tx->vout[i].nValue < 1 * COIN) return 20000; + + //nondenom return largest first + return -(tx->vout[i].nValue / COIN); + } + + CAmount Value() const + { + return tx->vout[i].nValue; + } + + std::string ToString() const; +}; + + +/** Private key that includes an expiration date in case it never gets used. */ +class CWalletKey +{ +public: + CPrivKey vchPrivKey; + int64_t nTimeCreated; + int64_t nTimeExpires; + std::string strComment; + //! todo: add something to note what created it (user, getnewaddress, change) + //! maybe should have a map property map + + CWalletKey(int64_t nExpires = 0); + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) + { + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vchPrivKey); + READWRITE(nTimeCreated); + READWRITE(nTimeExpires); + READWRITE(LIMITED_STRING(strComment, 65536)); + } +}; + + +/** + * Account information. + * Stored in wallet with key "acc"+string account name. + */ +class CAccount +{ +public: + CPubKey vchPubKey; + + CAccount() + { + SetNull(); + } + + void SetNull() + { + vchPubKey = CPubKey(); + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) + { + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vchPubKey); + } +}; + + +/** + * Internal transfers. + * Database key is acentry. + */ +class CAccountingEntry +{ +public: + std::string strAccount; + CAmount nCreditDebit; + int64_t nTime; + std::string strOtherAccount; + std::string strComment; + mapValue_t mapValue; + int64_t nOrderPos; //! position in ordered transaction list + uint64_t nEntryNo; + + CAccountingEntry() + { + SetNull(); + } + + void SetNull() + { + nCreditDebit = 0; + nTime = 0; + strAccount.clear(); + strOtherAccount.clear(); + strComment.clear(); + nOrderPos = -1; + nEntryNo = 0; + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) + { + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + //! Note: strAccount is serialized as part of the key, not here. + READWRITE(nCreditDebit); + READWRITE(nTime); + READWRITE(LIMITED_STRING(strOtherAccount, 65536)); + + if (!ser_action.ForRead()) { + WriteOrderPos(nOrderPos, mapValue); + + if (!(mapValue.empty() && _ssExtra.empty())) { + CDataStream ss(nType, nVersion); + ss.insert(ss.begin(), '\0'); + ss << mapValue; + ss.insert(ss.end(), _ssExtra.begin(), _ssExtra.end()); + strComment.append(ss.str()); + } + } + + READWRITE(LIMITED_STRING(strComment, 65536)); + + size_t nSepPos = strComment.find("\0", 0, 1); + if (ser_action.ForRead()) { + mapValue.clear(); + if (std::string::npos != nSepPos) { + CDataStream ss(std::vector(strComment.begin() + nSepPos + 1, strComment.end()), nType, nVersion); + ss >> mapValue; + _ssExtra = std::vector(ss.begin(), ss.end()); + } + ReadOrderPos(nOrderPos, mapValue); + } + if (std::string::npos != nSepPos) + strComment.erase(nSepPos); + + mapValue.erase("n"); + } + +private: + std::vector _ssExtra; +}; + +#endif // BITCOIN_WALLET_H diff --git a/src/wallet/wallet_ismine.cpp b/src/wallet/wallet_ismine.cpp new file mode 100644 index 0000000..cbbb62d --- /dev/null +++ b/src/wallet/wallet_ismine.cpp @@ -0,0 +1,101 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Copyright (c) 2016-2017 The PIVX developers +// Copyright (c) 2017-2019 The XDNA Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "wallet_ismine.h" + +#include "key.h" +#include "keystore.h" +#include "script/script.h" +#include "script/standard.h" +#include "util.h" + +#include + +using namespace std; + +typedef vector valtype; + +unsigned int HaveKeys(const vector& pubkeys, const CKeyStore& keystore) +{ + unsigned int nResult = 0; + BOOST_FOREACH (const valtype& pubkey, pubkeys) { + CKeyID keyID = CPubKey(pubkey).GetID(); + if(keystore.HaveKey(keyID)) + ++nResult; + } + return nResult; +} + +isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest) +{ + CScript script = GetScriptForDestination(dest); + return IsMine(keystore, script); +} + +isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey) +{ + if(keystore.HaveWatchOnly(scriptPubKey)) + return ISMINE_WATCH_ONLY; + if(keystore.HaveMultiSig(scriptPubKey)) + return ISMINE_MULTISIG; + + vector vSolutions; + txnouttype whichType; + if(!Solver(scriptPubKey, whichType, vSolutions)) { + if(keystore.HaveWatchOnly(scriptPubKey)) + return ISMINE_WATCH_ONLY; + if(keystore.HaveMultiSig(scriptPubKey)) + return ISMINE_MULTISIG; + + return ISMINE_NO; + } + + CKeyID keyID; + switch (whichType) { + case TX_NONSTANDARD: + case TX_NULL_DATA: + break; + case TX_PUBKEY: + keyID = CPubKey(vSolutions[0]).GetID(); + if(keystore.HaveKey(keyID)) + return ISMINE_SPENDABLE; + break; + case TX_PUBKEYHASH: + keyID = CKeyID(uint160(vSolutions[0])); + if(keystore.HaveKey(keyID)) + return ISMINE_SPENDABLE; + break; + case TX_SCRIPTHASH: { + CScriptID scriptID = CScriptID(uint160(vSolutions[0])); + CScript subscript; + if(keystore.GetCScript(scriptID, subscript)) { + isminetype ret = IsMine(keystore, subscript); + if(ret != ISMINE_NO) + return ret; + } + break; + } + case TX_MULTISIG: { + // Only consider transactions "mine" if we own ALL the + // keys involved. multi-signature transactions that are + // partially owned (somebody else has a key that can spend + // them) enable spend-out-from-under-you attacks, especially + // in shared-wallet situations. + vector keys(vSolutions.begin() + 1, vSolutions.begin() + vSolutions.size() - 1); + if(HaveKeys(keys, keystore) == keys.size()) + return ISMINE_SPENDABLE; + break; + } + } + + if(keystore.HaveWatchOnly(scriptPubKey)) + return ISMINE_WATCH_ONLY; + if(keystore.HaveMultiSig(scriptPubKey)) + return ISMINE_MULTISIG; + + return ISMINE_NO; +} diff --git a/src/wallet/wallet_ismine.h b/src/wallet/wallet_ismine.h new file mode 100644 index 0000000..32e7b2d --- /dev/null +++ b/src/wallet/wallet_ismine.h @@ -0,0 +1,31 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 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_WALLET_ISMINE_H +#define BITCOIN_WALLET_ISMINE_H + +#include "key.h" +#include "script/standard.h" + +class CKeyStore; +class CScript; + +/** IsMine() return codes */ +enum isminetype { + ISMINE_NO = 0, + //! Indicates that we dont know how to create a scriptSig that would solve this if we were given the appropriate private keys + ISMINE_WATCH_ONLY = 1, + //! Indicates that we know how to create a scriptSig that would solve this if we were given the appropriate private keys + ISMINE_MULTISIG = 2, + ISMINE_SPENDABLE = 4, + ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE +}; +/** used for bitflags of isminetype */ +typedef uint8_t isminefilter; + +isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); +isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest); + +#endif // BITCOIN_WALLET_ISMINE_H diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp new file mode 100644 index 0000000..e2c98bf --- /dev/null +++ b/src/wallet/walletdb.cpp @@ -0,0 +1,1027 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Copyright (c) 2014-2015 The Dash developers +// Copyright (c) 2015-2017 The PIVX developers +// Copyright (c) 2017-2019 The XDNA Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "walletdb.h" + +#include "base58.h" +#include "protocol.h" +#include "serialize.h" +#include "sync.h" +#include "util.h" +#include "utiltime.h" +#include "wallet.h" + +#include +#include +#include +#include +#include + +using namespace boost; +using namespace std; + +static uint64_t nAccountingEntryNumber = 0; + +// +// CWalletDB +// + +bool CWalletDB::WriteName(const string& strAddress, const string& strName) +{ + nWalletDBUpdated++; + return Write(make_pair(string("name"), strAddress), strName); +} + +bool CWalletDB::EraseName(const string& strAddress) +{ + // This should only be used for sending addresses, never for receiving addresses, + // receiving addresses must always have an address book entry if they're not change return. + nWalletDBUpdated++; + return Erase(make_pair(string("name"), strAddress)); +} + +bool CWalletDB::WritePurpose(const string& strAddress, const string& strPurpose) +{ + nWalletDBUpdated++; + return Write(make_pair(string("purpose"), strAddress), strPurpose); +} + +bool CWalletDB::ErasePurpose(const string& strPurpose) +{ + nWalletDBUpdated++; + return Erase(make_pair(string("purpose"), strPurpose)); +} + +bool CWalletDB::WriteTx(uint256 hash, const CWalletTx& wtx) +{ + nWalletDBUpdated++; + return Write(std::make_pair(std::string("tx"), hash), wtx); +} + +bool CWalletDB::EraseTx(uint256 hash) +{ + nWalletDBUpdated++; + return Erase(std::make_pair(std::string("tx"), hash)); +} + +bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta) +{ + nWalletDBUpdated++; + + if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), + keyMeta, false)) + return false; + + // hash pubkey/privkey to accelerate wallet load + std::vector vchKey; + vchKey.reserve(vchPubKey.size() + vchPrivKey.size()); + vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end()); + vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end()); + + return Write(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false); +} + +bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey, + const std::vector& vchCryptedSecret, + const CKeyMetadata& keyMeta) +{ + const bool fEraseUnencryptedKey = true; + nWalletDBUpdated++; + + if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), + keyMeta)) + return false; + + if (!Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false)) + return false; + if (fEraseUnencryptedKey) { + Erase(std::make_pair(std::string("key"), vchPubKey)); + Erase(std::make_pair(std::string("wkey"), vchPubKey)); + } + return true; +} + +bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) +{ + nWalletDBUpdated++; + return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true); +} + +bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript) +{ + nWalletDBUpdated++; + return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false); +} + +bool CWalletDB::WriteWatchOnly(const CScript& dest) +{ + nWalletDBUpdated++; + return Write(std::make_pair(std::string("watchs"), dest), '1'); +} + +bool CWalletDB::EraseWatchOnly(const CScript& dest) +{ + nWalletDBUpdated++; + return Erase(std::make_pair(std::string("watchs"), dest)); +} + +bool CWalletDB::WriteMultiSig(const CScript& dest) +{ + nWalletDBUpdated++; + return Write(std::make_pair(std::string("multisig"), dest), '1'); +} + +bool CWalletDB::EraseMultiSig(const CScript& dest) +{ + nWalletDBUpdated++; + return Erase(std::make_pair(std::string("multisig"), dest)); +} + +bool CWalletDB::WriteBestBlock(const CBlockLocator& locator) +{ + nWalletDBUpdated++; + return Write(std::string("bestblock"), locator); +} + +bool CWalletDB::ReadBestBlock(CBlockLocator& locator) +{ + return Read(std::string("bestblock"), locator); +} + +bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext) +{ + nWalletDBUpdated++; + return Write(std::string("orderposnext"), nOrderPosNext); +} + +// presstab HyperStake +bool CWalletDB::WriteStakeSplitThreshold(uint64_t nStakeSplitThreshold) +{ + nWalletDBUpdated++; + return Write(std::string("stakeSplitThreshold"), nStakeSplitThreshold); +} + +//presstab HyperStake +bool CWalletDB::WriteMultiSend(std::vector > vMultiSend) +{ + nWalletDBUpdated++; + bool ret = true; + for (unsigned int i = 0; i < vMultiSend.size(); i++) { + std::pair pMultiSend; + pMultiSend = vMultiSend[i]; + if (!Write(std::make_pair(std::string("multisend"), i), pMultiSend, true)) + ret = false; + } + return ret; +} +//presstab HyperStake +bool CWalletDB::EraseMultiSend(std::vector > vMultiSend) +{ + nWalletDBUpdated++; + bool ret = true; + for (unsigned int i = 0; i < vMultiSend.size(); i++) { + std::pair pMultiSend; + pMultiSend = vMultiSend[i]; + if (!Erase(std::make_pair(std::string("multisend"), i))) + ret = false; + } + return ret; +} +//presstab HyperStake +bool CWalletDB::WriteMSettings(bool fMultiSendStake, bool fMultiSendMasternode, int nLastMultiSendHeight) +{ + nWalletDBUpdated++; + std::pair enabledMS(fMultiSendStake, fMultiSendMasternode); + std::pair, int> pSettings(enabledMS, nLastMultiSendHeight); + + return Write(std::string("msettingsv2"), pSettings, true); +} +//presstab HyperStake +bool CWalletDB::WriteMSDisabledAddresses(std::vector vDisabledAddresses) +{ + nWalletDBUpdated++; + bool ret = true; + for (unsigned int i = 0; i < vDisabledAddresses.size(); i++) { + if (!Write(std::make_pair(std::string("mdisabled"), i), vDisabledAddresses[i])) + ret = false; + } + return ret; +} +//presstab HyperStake +bool CWalletDB::EraseMSDisabledAddresses(std::vector vDisabledAddresses) +{ + nWalletDBUpdated++; + bool ret = true; + for (unsigned int i = 0; i < vDisabledAddresses.size(); i++) { + if (!Erase(std::make_pair(std::string("mdisabled"), i))) + ret = false; + } + return ret; +} +bool CWalletDB::WriteAutoCombineSettings(bool fEnable, CAmount nCombineThreshold) +{ + nWalletDBUpdated++; + std::pair pSettings; + pSettings.first = fEnable; + pSettings.second = nCombineThreshold; + return Write(std::string("autocombinesettings"), pSettings, true); +} + +bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey) +{ + nWalletDBUpdated++; + return Write(std::string("defaultkey"), vchPubKey); +} + +bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool) +{ + return Read(std::make_pair(std::string("pool"), nPool), keypool); +} + +bool CWalletDB::WritePool(int64_t nPool, const CKeyPool& keypool) +{ + nWalletDBUpdated++; + return Write(std::make_pair(std::string("pool"), nPool), keypool); +} + +bool CWalletDB::ErasePool(int64_t nPool) +{ + nWalletDBUpdated++; + return Erase(std::make_pair(std::string("pool"), nPool)); +} + +bool CWalletDB::WriteMinVersion(int nVersion) +{ + return Write(std::string("minversion"), nVersion); +} + +bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account) +{ + account.SetNull(); + return Read(make_pair(string("acc"), strAccount), account); +} + +bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account) +{ + return Write(make_pair(string("acc"), strAccount), account); +} + +bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry) +{ + return Write(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry); +} + +bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry) +{ + return WriteAccountingEntry(++nAccountingEntryNumber, acentry); +} + +CAmount CWalletDB::GetAccountCreditDebit(const string& strAccount) +{ + list entries; + ListAccountCreditDebit(strAccount, entries); + + CAmount nCreditDebit = 0; + BOOST_FOREACH (const CAccountingEntry& entry, entries) + nCreditDebit += entry.nCreditDebit; + + return nCreditDebit; +} + +void CWalletDB::ListAccountCreditDebit(const string& strAccount, list& entries) +{ + bool fAllAccounts = (strAccount == "*"); + + Dbc* pcursor = GetCursor(); + if (!pcursor) + throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor"); + unsigned int fFlags = DB_SET_RANGE; + while (true) { + // Read next record + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + if (fFlags == DB_SET_RANGE) + ssKey << std::make_pair(std::string("acentry"), std::make_pair((fAllAccounts ? string("") : strAccount), uint64_t(0))); + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); + fFlags = DB_NEXT; + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) { + pcursor->close(); + throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB"); + } + + // Unserialize + string strType; + ssKey >> strType; + if (strType != "acentry") + break; + CAccountingEntry acentry; + ssKey >> acentry.strAccount; + if (!fAllAccounts && acentry.strAccount != strAccount) + break; + + ssValue >> acentry; + ssKey >> acentry.nEntryNo; + entries.push_back(acentry); + } + + pcursor->close(); +} + +DBErrors CWalletDB::ReorderTransactions(CWallet* pwallet) +{ + LOCK(pwallet->cs_wallet); + // 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 pair TxPair; + typedef multimap TxItems; + TxItems txByTime; + + for (map::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it) { + CWalletTx* wtx = &((*it).second); + txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0))); + } + list acentries; + ListAccountCreditDebit("", acentries); + BOOST_FOREACH (CAccountingEntry& entry, acentries) { + txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); + } + + int64_t& nOrderPosNext = pwallet->nOrderPosNext; + 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 (!WriteTx(pwtx->GetHash(), *pwtx)) + return DB_LOAD_FAIL; + } else if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) + return DB_LOAD_FAIL; + } else { + int64_t nOrderPosOff = 0; + BOOST_FOREACH (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 (!WriteTx(pwtx->GetHash(), *pwtx)) + return DB_LOAD_FAIL; + } else if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) + return DB_LOAD_FAIL; + } + } + WriteOrderPosNext(nOrderPosNext); + + return DB_LOAD_OK; +} + +class CWalletScanState +{ +public: + unsigned int nKeys; + unsigned int nCKeys; + unsigned int nKeyMeta; + bool fIsEncrypted; + bool fAnyUnordered; + int nFileVersion; + vector vWalletUpgrade; + + CWalletScanState() + { + nKeys = nCKeys = nKeyMeta = 0; + fIsEncrypted = false; + fAnyUnordered = false; + nFileVersion = 0; + } +}; + +bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, CWalletScanState& wss, string& strType, string& strErr) +{ + try { + // Unserialize + // Taking advantage of the fact that pair serialization + // is just the two items serialized one after the other + ssKey >> strType; + if (strType == "name") { + string strAddress; + ssKey >> strAddress; + ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].name; + } else if (strType == "purpose") { + string strAddress; + ssKey >> strAddress; + ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].purpose; + } else if (strType == "tx") { + uint256 hash; + ssKey >> hash; + CWalletTx wtx; + ssValue >> wtx; + CValidationState state; + if (!(CheckTransaction(wtx, state) && (wtx.GetHash() == hash) && state.IsValid())) + return false; + + // Undo serialize changes in 31600 + if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703) { + if (!ssValue.empty()) { + char fTmp; + char fUnused; + ssValue >> fTmp >> fUnused >> wtx.strFromAccount; + strErr = strprintf("LoadWallet() upgrading tx ver=%d %d '%s' %s", + wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount, hash.ToString()); + wtx.fTimeReceivedIsTxTime = fTmp; + } else { + strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString()); + wtx.fTimeReceivedIsTxTime = 0; + } + wss.vWalletUpgrade.push_back(hash); + } + + if (wtx.nOrderPos == -1) + wss.fAnyUnordered = true; + + pwallet->AddToWallet(wtx, true); + } else if (strType == "acentry") { + string strAccount; + ssKey >> strAccount; + uint64_t nNumber; + ssKey >> nNumber; + if (nNumber > nAccountingEntryNumber) + nAccountingEntryNumber = nNumber; + + if (!wss.fAnyUnordered) { + CAccountingEntry acentry; + ssValue >> acentry; + if (acentry.nOrderPos == -1) + wss.fAnyUnordered = true; + } + } else if (strType == "watchs") { + CScript script; + ssKey >> script; + char fYes; + ssValue >> fYes; + if (fYes == '1') + pwallet->LoadWatchOnly(script); + + // Watch-only addresses have no birthday information for now, + // so set the wallet birthday to the beginning of time. + pwallet->nTimeFirstKey = 1; + } else if (strType == "multisig") { + CScript script; + ssKey >> script; + char fYes; + ssValue >> fYes; + if (fYes == '1') + pwallet->LoadMultiSig(script); + + // MultiSig addresses have no birthday information for now, + // so set the wallet birthday to the beginning of time. + pwallet->nTimeFirstKey = 1; + } else if (strType == "key" || strType == "wkey") { + CPubKey vchPubKey; + ssKey >> vchPubKey; + if (!vchPubKey.IsValid()) { + strErr = "Error reading wallet database: CPubKey corrupt"; + return false; + } + CKey key; + CPrivKey pkey; + uint256 hash = 0; + + if (strType == "key") { + wss.nKeys++; + ssValue >> pkey; + } else { + CWalletKey wkey; + ssValue >> wkey; + pkey = wkey.vchPrivKey; + } + + // Old wallets store keys as "key" [pubkey] => [privkey] + // ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key + // using EC operations as a checksum. + // Newer wallets store keys as "key"[pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while + // remaining backwards-compatible. + try { + ssValue >> hash; + } catch (...) { + } + + bool fSkipCheck = false; + + if (hash != 0) { + // hash pubkey/privkey to accelerate wallet load + std::vector vchKey; + vchKey.reserve(vchPubKey.size() + pkey.size()); + vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end()); + vchKey.insert(vchKey.end(), pkey.begin(), pkey.end()); + + if (Hash(vchKey.begin(), vchKey.end()) != hash) { + strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt"; + return false; + } + + fSkipCheck = true; + } + + if (!key.Load(pkey, vchPubKey, fSkipCheck)) { + strErr = "Error reading wallet database: CPrivKey corrupt"; + return false; + } + if (!pwallet->LoadKey(key, vchPubKey)) { + strErr = "Error reading wallet database: LoadKey failed"; + return false; + } + } else if (strType == "mkey") { + unsigned int nID; + ssKey >> nID; + CMasterKey kMasterKey; + ssValue >> kMasterKey; + if (pwallet->mapMasterKeys.count(nID) != 0) { + strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID); + return false; + } + pwallet->mapMasterKeys[nID] = kMasterKey; + if (pwallet->nMasterKeyMaxID < nID) + pwallet->nMasterKeyMaxID = nID; + } else if (strType == "ckey") { + vector vchPubKey; + ssKey >> vchPubKey; + vector vchPrivKey; + ssValue >> vchPrivKey; + wss.nCKeys++; + + if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey)) { + strErr = "Error reading wallet database: LoadCryptedKey failed"; + return false; + } + wss.fIsEncrypted = true; + } else if (strType == "keymeta") { + CPubKey vchPubKey; + ssKey >> vchPubKey; + CKeyMetadata keyMeta; + ssValue >> keyMeta; + wss.nKeyMeta++; + + pwallet->LoadKeyMetadata(vchPubKey, keyMeta); + + // find earliest key creation time, as wallet birthday + if (!pwallet->nTimeFirstKey || + (keyMeta.nCreateTime < pwallet->nTimeFirstKey)) + pwallet->nTimeFirstKey = keyMeta.nCreateTime; + } else if (strType == "defaultkey") { + ssValue >> pwallet->vchDefaultKey; + } else if (strType == "pool") { + int64_t nIndex; + ssKey >> nIndex; + CKeyPool keypool; + ssValue >> keypool; + pwallet->setKeyPool.insert(nIndex); + + // If no metadata exists yet, create a default with the pool key's + // creation time. Note that this may be overwritten by actually + // stored metadata for that key later, which is fine. + CKeyID keyid = keypool.vchPubKey.GetID(); + if (pwallet->mapKeyMetadata.count(keyid) == 0) + pwallet->mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime); + } else if (strType == "version") { + ssValue >> wss.nFileVersion; + if (wss.nFileVersion == 10300) + wss.nFileVersion = 300; + } else if (strType == "cscript") { + uint160 hash; + ssKey >> hash; + CScript script; + ssValue >> script; + if (!pwallet->LoadCScript(script)) { + strErr = "Error reading wallet database: LoadCScript failed"; + return false; + } + } else if (strType == "orderposnext") { + ssValue >> pwallet->nOrderPosNext; + } else if (strType == "stakeSplitThreshold") //presstab HyperStake + { + ssValue >> pwallet->nStakeSplitThreshold; + } else if (strType == "multisend") //presstab HyperStake + { + unsigned int i; + ssKey >> i; + std::pair pMultiSend; + ssValue >> pMultiSend; + if (CBitcoinAddress(pMultiSend.first).IsValid()) { + pwallet->vMultiSend.push_back(pMultiSend); + } + } else if (strType == "msettingsv2") //presstab HyperStake + { + std::pair, int> pSettings; + ssValue >> pSettings; + pwallet->fMultiSendStake = pSettings.first.first; + pwallet->fMultiSendMasternodeReward = pSettings.first.second; + pwallet->nLastMultiSendHeight = pSettings.second; + } else if (strType == "mdisabled") //presstab HyperStake + { + std::string strDisabledAddress; + ssValue >> strDisabledAddress; + pwallet->vDisabledAddresses.push_back(strDisabledAddress); + } else if (strType == "autocombinesettings") { + std::pair pSettings; + ssValue >> pSettings; + pwallet->fCombineDust = pSettings.first; + pwallet->nAutoCombineThreshold = pSettings.second; + } else if (strType == "destdata") { + std::string strAddress, strKey, strValue; + ssKey >> strAddress; + ssKey >> strKey; + ssValue >> strValue; + if (!pwallet->LoadDestData(CBitcoinAddress(strAddress).Get(), strKey, strValue)) { + strErr = "Error reading wallet database: LoadDestData failed"; + return false; + } + } + } catch (...) { + return false; + } + return true; +} + +static bool IsKeyType(string strType) +{ + return (strType == "key" || strType == "wkey" || + strType == "mkey" || strType == "ckey"); +} + +DBErrors CWalletDB::LoadWallet(CWallet* pwallet) +{ + pwallet->vchDefaultKey = CPubKey(); + CWalletScanState wss; + bool fNoncriticalErrors = false; + DBErrors result = DB_LOAD_OK; + + try { + LOCK(pwallet->cs_wallet); + int nMinVersion = 0; + if (Read((string) "minversion", nMinVersion)) { + if (nMinVersion > CLIENT_VERSION) + return DB_TOO_NEW; + pwallet->LoadMinVersion(nMinVersion); + } + + // Get cursor + Dbc* pcursor = GetCursor(); + if (!pcursor) { + LogPrintf("Error getting wallet database cursor\n"); + return DB_CORRUPT; + } + + while (true) { + // Read next record + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + int ret = ReadAtCursor(pcursor, ssKey, ssValue); + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) { + LogPrintf("Error reading next record from wallet database\n"); + return DB_CORRUPT; + } + + // Try to be tolerant of single corrupt records: + string strType, strErr; + if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr)) { + // losing keys is considered a catastrophic error, anything else + // we assume the user can live with: + if (IsKeyType(strType)) + result = DB_CORRUPT; + else { + // Leave other errors alone, if we try to fix them we might make things worse. + fNoncriticalErrors = true; // ... but do warn the user there is something wrong. + if (strType == "tx") + // Rescan if there is a bad transaction record: + SoftSetBoolArg("-rescan", true); + } + } + if (!strErr.empty()) + LogPrintf("%s\n", strErr); + } + pcursor->close(); + } catch (boost::thread_interrupted) { + throw; + } catch (...) { + result = DB_CORRUPT; + } + + if (fNoncriticalErrors && result == DB_LOAD_OK) + result = DB_NONCRITICAL_ERROR; + + // Any wallet corruption at all: skip any rewriting or + // upgrading, we don't want to make it worse. + if (result != DB_LOAD_OK) + return result; + + LogPrintf("nFileVersion = %d\n", wss.nFileVersion); + + LogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total\n", + wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys); + + // nTimeFirstKey is only reliable if all keys have metadata + if ((wss.nKeys + wss.nCKeys) != wss.nKeyMeta) + pwallet->nTimeFirstKey = 1; // 0 would be considered 'no value' + + BOOST_FOREACH (uint256 hash, wss.vWalletUpgrade) + WriteTx(hash, pwallet->mapWallet[hash]); + + // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: + if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000)) + return DB_NEED_REWRITE; + + if (wss.nFileVersion < CLIENT_VERSION) // Update + WriteVersion(CLIENT_VERSION); + + if (wss.fAnyUnordered) + result = ReorderTransactions(pwallet); + + return result; +} + +DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector& vTxHash, vector& vWtx) +{ + pwallet->vchDefaultKey = CPubKey(); + bool fNoncriticalErrors = false; + DBErrors result = DB_LOAD_OK; + + try { + LOCK(pwallet->cs_wallet); + int nMinVersion = 0; + if (Read((string) "minversion", nMinVersion)) { + if (nMinVersion > CLIENT_VERSION) + return DB_TOO_NEW; + pwallet->LoadMinVersion(nMinVersion); + } + + // Get cursor + Dbc* pcursor = GetCursor(); + if (!pcursor) { + LogPrintf("Error getting wallet database cursor\n"); + return DB_CORRUPT; + } + + while (true) { + // Read next record + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + int ret = ReadAtCursor(pcursor, ssKey, ssValue); + if (ret == DB_NOTFOUND) + break; + else if (ret != 0) { + LogPrintf("Error reading next record from wallet database\n"); + return DB_CORRUPT; + } + + string strType; + ssKey >> strType; + if (strType == "tx") { + uint256 hash; + ssKey >> hash; + + CWalletTx wtx; + ssValue >> wtx; + + vTxHash.push_back(hash); + vWtx.push_back(wtx); + } + } + pcursor->close(); + } catch (boost::thread_interrupted) { + throw; + } catch (...) { + result = DB_CORRUPT; + } + + if (fNoncriticalErrors && result == DB_LOAD_OK) + result = DB_NONCRITICAL_ERROR; + + return result; +} + +DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet, vector& vWtx) +{ + // build list of wallet TXs + vector vTxHash; + DBErrors err = FindWalletTx(pwallet, vTxHash, vWtx); + if (err != DB_LOAD_OK) + return err; + + // erase each wallet TX + BOOST_FOREACH (uint256& hash, vTxHash) { + if (!EraseTx(hash)) + return DB_CORRUPT; + } + + return DB_LOAD_OK; +} + +void ThreadFlushWalletDB(const string& strFile) +{ + // Make this thread recognisable as the wallet flushing thread + RenameThread("xdna-wallet"); + + static bool fOneThread; + if (fOneThread) + return; + fOneThread = true; + if (!GetBoolArg("-flushwallet", true)) + return; + + unsigned int nLastSeen = nWalletDBUpdated; + unsigned int nLastFlushed = nWalletDBUpdated; + int64_t nLastWalletUpdate = GetTime(); + while (true) { + MilliSleep(500); + + if (nLastSeen != nWalletDBUpdated) { + nLastSeen = nWalletDBUpdated; + nLastWalletUpdate = GetTime(); + } + + if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2) { + TRY_LOCK(bitdb.cs_db, lockDb); + if (lockDb) { + // Don't do this if any databases are in use + int nRefCount = 0; + map::iterator mi = bitdb.mapFileUseCount.begin(); + while (mi != bitdb.mapFileUseCount.end()) { + nRefCount += (*mi).second; + mi++; + } + + if (nRefCount == 0) { + boost::this_thread::interruption_point(); + map::iterator mi = bitdb.mapFileUseCount.find(strFile); + if (mi != bitdb.mapFileUseCount.end()) { + LogPrint("db", "Flushing wallet.dat\n"); + nLastFlushed = nWalletDBUpdated; + int64_t nStart = GetTimeMillis(); + + // Flush wallet.dat so it's self contained + bitdb.CloseDb(strFile); + bitdb.CheckpointLSN(strFile); + + bitdb.mapFileUseCount.erase(mi++); + LogPrint("db", "Flushed wallet.dat %dms\n", GetTimeMillis() - nStart); + } + } + } + } + } +} + +bool BackupWallet(const CWallet& wallet, const string& strDest) +{ + if (!wallet.fFileBacked) + return false; + while (true) { + { + LOCK(bitdb.cs_db); + if (!bitdb.mapFileUseCount.count(wallet.strWalletFile) || bitdb.mapFileUseCount[wallet.strWalletFile] == 0) { + // Flush log data to the dat file + bitdb.CloseDb(wallet.strWalletFile); + bitdb.CheckpointLSN(wallet.strWalletFile); + bitdb.mapFileUseCount.erase(wallet.strWalletFile); + + // Copy wallet.dat + filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile; + filesystem::path pathDest(strDest); + if (filesystem::is_directory(pathDest)) + pathDest /= wallet.strWalletFile; + + try { +#if BOOST_VERSION >= 158000 + filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists); +#else + std::ifstream src(pathSrc.string(), std::ios::binary); + std::ofstream dst(pathDest.string(), std::ios::binary); + dst << src.rdbuf(); +#endif + LogPrintf("copied wallet.dat to %s\n", pathDest.string()); + return true; + } catch (const filesystem::filesystem_error& e) { + LogPrintf("error copying wallet.dat to %s - %s\n", pathDest.string(), e.what()); + return false; + } + } + } + MilliSleep(100); + } + return false; +} + +// +// Try to (very carefully!) recover wallet.dat if there is a problem. +// +bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys) +{ + // Recovery procedure: + // move wallet.dat to wallet.timestamp.bak + // Call Salvage with fAggressive=true to + // get as much data as possible. + // Rewrite salvaged data to wallet.dat + // Set -rescan so any missing transactions will be + // found. + int64_t now = GetTime(); + std::string newFilename = strprintf("wallet.%d.bak", now); + + int result = dbenv.dbenv.dbrename(NULL, filename.c_str(), NULL, + newFilename.c_str(), DB_AUTO_COMMIT); + if (result == 0) + LogPrintf("Renamed %s to %s\n", filename, newFilename); + else { + LogPrintf("Failed to rename %s to %s\n", filename, newFilename); + return false; + } + + std::vector salvagedData; + bool allOK = dbenv.Salvage(newFilename, true, salvagedData); + if (salvagedData.empty()) { + LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename); + return false; + } + LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size()); + + bool fSuccess = allOK; + boost::scoped_ptr pdbCopy(new Db(&dbenv.dbenv, 0)); + int ret = pdbCopy->open(NULL, // Txn pointer + filename.c_str(), // Filename + "main", // Logical db name + DB_BTREE, // Database type + DB_CREATE, // Flags + 0); + if (ret > 0) { + LogPrintf("Cannot create database file %s\n", filename); + return false; + } + CWallet dummyWallet; + CWalletScanState wss; + + DbTxn* ptxn = dbenv.TxnBegin(); + BOOST_FOREACH (CDBEnv::KeyValPair& row, salvagedData) { + if (fOnlyKeys) { + CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION); + CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION); + string strType, strErr; + bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, + wss, strType, strErr); + if (!IsKeyType(strType)) + continue; + if (!fReadOK) { + LogPrintf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType, strErr); + continue; + } + } + Dbt datKey(&row.first[0], row.first.size()); + Dbt datValue(&row.second[0], row.second.size()); + int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE); + if (ret2 > 0) + fSuccess = false; + } + ptxn->commit(0); + pdbCopy->close(0); + + return fSuccess; +} + +bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename) +{ + return CWalletDB::Recover(dbenv, filename, false); +} + +bool CWalletDB::WriteDestData(const std::string& address, const std::string& key, const std::string& value) +{ + nWalletDBUpdated++; + return Write(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value); +} + +bool CWalletDB::EraseDestData(const std::string& address, const std::string& key) +{ + nWalletDBUpdated++; + return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key))); +} diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h new file mode 100644 index 0000000..c818d15 --- /dev/null +++ b/src/wallet/walletdb.h @@ -0,0 +1,156 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2013 The Bitcoin developers +// Copyright (c) 2016-2017 The PIVX developers +// Copyright (c) 2017-2019 The XDNA Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_WALLETDB_H +#define BITCOIN_WALLETDB_H + +#include "amount.h" +#include "db.h" +#include "key.h" +#include "keystore.h" + +#include +#include +#include +#include +#include + +class CAccount; +class CAccountingEntry; +struct CBlockLocator; +class CKeyPool; +class CMasterKey; +class CScript; +class CWallet; +class CWalletTx; +class uint160; +class uint256; + +/** Error statuses for the wallet database */ +enum DBErrors { + DB_LOAD_OK, + DB_CORRUPT, + DB_NONCRITICAL_ERROR, + DB_TOO_NEW, + DB_LOAD_FAIL, + DB_NEED_REWRITE +}; + +class CKeyMetadata +{ +public: + static const int CURRENT_VERSION = 1; + int nVersion; + int64_t nCreateTime; // 0 means unknown + + CKeyMetadata() + { + SetNull(); + } + CKeyMetadata(int64_t nCreateTime_) + { + nVersion = CKeyMetadata::CURRENT_VERSION; + nCreateTime = nCreateTime_; + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) + { + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(nCreateTime); + } + + void SetNull() + { + nVersion = CKeyMetadata::CURRENT_VERSION; + nCreateTime = 0; + } +}; + +/** Access to the wallet database (wallet.dat) */ +class CWalletDB : public CDB +{ +public: + CWalletDB(const std::string& strFilename, const char* pszMode = "r+") : CDB(strFilename, pszMode) + { + } + + bool WriteName(const std::string& strAddress, const std::string& strName); + bool EraseName(const std::string& strAddress); + + bool WritePurpose(const std::string& strAddress, const std::string& purpose); + bool ErasePurpose(const std::string& strAddress); + + bool WriteTx(uint256 hash, const CWalletTx& wtx); + bool EraseTx(uint256 hash); + + bool WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta); + bool WriteCryptedKey(const CPubKey& vchPubKey, const std::vector& vchCryptedSecret, const CKeyMetadata& keyMeta); + bool WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey); + + bool WriteCScript(const uint160& hash, const CScript& redeemScript); + + bool WriteWatchOnly(const CScript& script); + bool EraseWatchOnly(const CScript& script); + + bool WriteMultiSig(const CScript& script); + bool EraseMultiSig(const CScript& script); + + bool WriteBestBlock(const CBlockLocator& locator); + bool ReadBestBlock(CBlockLocator& locator); + + bool WriteOrderPosNext(int64_t nOrderPosNext); + + // presstab + bool WriteStakeSplitThreshold(uint64_t nStakeSplitThreshold); + bool WriteMultiSend(std::vector > vMultiSend); + bool EraseMultiSend(std::vector > vMultiSend); + bool WriteMSettings(bool fMultiSendStake, bool fMultiSendMasternode, int nLastMultiSendHeight); + bool WriteMSDisabledAddresses(std::vector vDisabledAddresses); + bool EraseMSDisabledAddresses(std::vector vDisabledAddresses); + bool WriteAutoCombineSettings(bool fEnable, CAmount nCombineThreshold); + + bool WriteDefaultKey(const CPubKey& vchPubKey); + + bool ReadPool(int64_t nPool, CKeyPool& keypool); + bool WritePool(int64_t nPool, const CKeyPool& keypool); + bool ErasePool(int64_t nPool); + + bool WriteMinVersion(int nVersion); + + bool ReadAccount(const std::string& strAccount, CAccount& account); + bool WriteAccount(const std::string& strAccount, const CAccount& account); + + /// Write destination data key,value tuple to database + bool WriteDestData(const std::string& address, const std::string& key, const std::string& value); + /// Erase destination data tuple from wallet database + bool EraseDestData(const std::string& address, const std::string& key); + + bool WriteAccountingEntry(const CAccountingEntry& acentry); + CAmount GetAccountCreditDebit(const std::string& strAccount); + void ListAccountCreditDebit(const std::string& strAccount, std::list& acentries); + + DBErrors ReorderTransactions(CWallet* pwallet); + DBErrors LoadWallet(CWallet* pwallet); + DBErrors FindWalletTx(CWallet* pwallet, std::vector& vTxHash, std::vector& vWtx); + DBErrors ZapWalletTx(CWallet* pwallet, std::vector& vWtx); + static bool Recover(CDBEnv& dbenv, std::string filename, bool fOnlyKeys); + static bool Recover(CDBEnv& dbenv, std::string filename); + +private: + CWalletDB(const CWalletDB&); + void operator=(const CWalletDB&); + + bool WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry); +}; + +bool BackupWallet(const CWallet& wallet, const std::string& strDest); + +#endif // BITCOIN_WALLETDB_H diff --git a/src/xdna-cli.cpp b/src/xdna-cli.cpp index a9040aa..b8a6752 100644 --- a/src/xdna-cli.cpp +++ b/src/xdna-cli.cpp @@ -2,14 +2,14 @@ // Copyright (c) 2009-2015 The Bitcoin developers // Copyright (c) 2009-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "chainparamsbase.h" #include "clientversion.h" -#include "rpcclient.h" -#include "rpcprotocol.h" +#include "rpc/client.h" +#include "rpc/protocol.h" #include "util.h" #include "utilstrencodings.h" @@ -111,7 +111,7 @@ UniValue CallRPC(const string& strMethod, const UniValue& params) // Connect to localhost bool fUseSSL = GetBoolArg("-rpcssl", false); asio::io_service io_service; - ssl::context context(io_service, ssl::context::sslv23); + boost::asio::ssl::context context(boost::asio::ssl::context::sslv23); context.set_options(ssl::context::no_sslv2 | ssl::context::no_sslv3); asio::ssl::stream sslStream(io_service, context); SSLIOStreamDevice d(sslStream, fUseSSL); diff --git a/src/xdna-tx.cpp b/src/xdna-tx.cpp index 1995a2f..fcfd40b 100644 --- a/src/xdna-tx.cpp +++ b/src/xdna-tx.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/xdnad.cpp b/src/xdnad.cpp index 8eda826..2bfed17 100644 --- a/src/xdnad.cpp +++ b/src/xdnad.cpp @@ -2,7 +2,7 @@ // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2017 The PIVX developers -// Copyright (c) 2017-2018 The XDNA Core developers +// Copyright (c) 2017-2019 The XDNA Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -11,7 +11,7 @@ #include "main.h" #include "masternodeconfig.h" #include "noui.h" -#include "rpcserver.h" +#include "rpc/server.h" #include "ui_interface.h" #include "util.h" @@ -37,7 +37,7 @@ static bool fDaemon; -void DetectShutdownThread(boost::thread_group* threadGroup) +void DetectShutdownThread() { bool fShutdown = ShutdownRequested(); // Tell the main threads to shutdown. @@ -45,10 +45,7 @@ void DetectShutdownThread(boost::thread_group* threadGroup) MilliSleep(200); fShutdown = ShutdownRequested(); } - if (threadGroup) { - threadGroup->interrupt_all(); - threadGroup->join_all(); - } + Interrupt(); } ////////////////////////////////////////////////////////////////////////////// @@ -57,15 +54,15 @@ void DetectShutdownThread(boost::thread_group* threadGroup) // bool AppInit(int argc, char* argv[]) { - boost::thread_group threadGroup; - boost::thread* detectShutdownThread = NULL; + //boost::thread_group threadGroup; + //boost::thread* detectShutdownThread = NULL; bool fRet = false; // // Parameters // - // If Qt is used, parameters/xdna.conf are parsed in qt/xdna.cpp's main() + // If Qt is used, parameters/livenodes.conf are parsed in qt/livenodes.cpp's main() ParseParameters(argc, argv); // Process help and version before taking care about datadir @@ -143,8 +140,8 @@ bool AppInit(int argc, char* argv[]) #endif SoftSetBoolArg("-server", true); - detectShutdownThread = new boost::thread(boost::bind(&DetectShutdownThread, &threadGroup)); - fRet = AppInit2(threadGroup); + //detectShutdownThread = new boost::thread(boost::bind(&DetectShutdownThread)); + fRet = AppInit2(); } catch (std::exception& e) { PrintExceptionContinue(&e, "AppInit()"); } catch (...) { @@ -152,20 +149,24 @@ bool AppInit(int argc, char* argv[]) } if (!fRet) { - if (detectShutdownThread) - detectShutdownThread->interrupt(); + //if (detectShutdownThread) + //detectShutdownThread->interrupt(); - threadGroup.interrupt_all(); + Interrupt(); + //threadGroup.interrupt_all(); // threadGroup.join_all(); was left out intentionally here, because we didn't re-test all of // the startup-failure cases to make sure they don't result in a hang due to some // thread-blocking-waiting-for-another-thread-during-startup case + } else { + DetectShutdownThread(); } - if (detectShutdownThread) { + /*if (detectShutdownThread) { detectShutdownThread->join(); delete detectShutdownThread; detectShutdownThread = NULL; } + */ Shutdown(); return fRet;