diff --git a/contrib/bitcoin-qt.pro b/contrib/bitcoin-qt.pro index 01393ea7..509cd7f0 100644 --- a/contrib/bitcoin-qt.pro +++ b/contrib/bitcoin-qt.pro @@ -2,6 +2,7 @@ FORMS += \ ../src/qt/forms/aboutdialog.ui \ ../src/qt/forms/addmilestonedialog.ui \ ../src/qt/forms/addressbookpage.ui \ + ../src/qt/forms/addressconverter.ui \ ../src/qt/forms/askpassphrasedialog.ui \ ../src/qt/forms/castvotesdialog.ui \ ../src/qt/forms/coincontroldialog.ui \ diff --git a/contrib/gitian-descriptors/gitian-rpi.yml b/contrib/gitian-descriptors/gitian-rpi.yml new file mode 100644 index 00000000..1178b89f --- /dev/null +++ b/contrib/gitian-descriptors/gitian-rpi.yml @@ -0,0 +1,112 @@ +--- +name: "smartcash-rpi-1.3" +enable_cache: true +suites: +- "trusty" +architectures: +- "amd64" +packages: +- "g++-multilib" +- "git-core" +- "pkg-config" +- "autoconf2.13" +- "libtool" +- "automake" +- "faketime" +- "bsdmainutils" +- "binutils-gold" +reference_datetime: "2017-01-01 00:00:00" +remotes: +- "url": "https://github.com/smartcash/core-smart.git" + "dir": "core-smart" +files: +- "raspberrypi-tools.tar.gz" +script: | + WRAP_DIR=$HOME/wrapped + HOSTS="arm-linux-gnueabihf" + CONFIGFLAGS="--enable-upnp-default --enable-glibc-back-compat" + FAKETIME_HOST_PROGS="" + FAKETIME_PROGS="date ar ranlib nm strip" + + tar --warning=no-timestamp -xzf raspberrypi-tools.tar.gz + export TOOLCHAIN_BIN=$(pwd)/raspberrypi-tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin + export PATH=$PATH:$TOOLCHAIN_BIN + + export QT_RCC_TEST=1 + export GZIP="-9n" + export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME"" + export TZ="UTC" + export BUILD_DIR=`pwd` + mkdir -p ${WRAP_DIR} + if test -n "$GBUILD_CACHE_ENABLED"; then + export SOURCES_PATH=${GBUILD_COMMON_CACHE} + export BASE_CACHE=${GBUILD_PACKAGE_CACHE} + mkdir -p ${BASE_CACHE} ${SOURCES_PATH} + fi + + # Create global faketime wrappers + for prog in ${FAKETIME_PROGS}; do + echo '#!/bin/bash' > ${WRAP_DIR}/${prog} + echo "REAL=\`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1\`" >> ${WRAP_DIR}/${prog} + echo 'export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${prog} + echo "export FAKETIME=\"${REFERENCE_DATETIME}\"" >> ${WRAP_DIR}/${prog} + echo "\$REAL \$@" >> $WRAP_DIR/${prog} + chmod +x ${WRAP_DIR}/${prog} + done + + # Create per-host faketime wrappers + for i in $HOSTS; do + for prog in ${FAKETIME_HOST_PROGS}; do + echo '#!/bin/bash' > ${WRAP_DIR}/${i}-${prog} + echo "REAL=\`which -a ${i}-${prog} | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog} + echo 'export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog} + echo "export FAKETIME=\"${REFERENCE_DATETIME}\"" >> ${WRAP_DIR}/${i}-${prog} + echo "\$REAL \$@" >> $WRAP_DIR/${i}-${prog} + chmod +x ${WRAP_DIR}/${i}-${prog} + done + done + export PATH=${WRAP_DIR}:${PATH} + + cd core-smart + BASEPREFIX=`pwd`/depends + # Build dependencies for each host + for i in $HOSTS; do + make ${MAKEOPTS} NO_QT=1 -C ${BASEPREFIX} HOST="${i}" + done + + # Create the release tarball using (arbitrarily) the first host + ./autogen.sh + ./configure --prefix=${BASEPREFIX}/`echo "${HOSTS}" | awk '{print $1;}'` + make NO_QT=1 dist + SOURCEDIST=`echo smartcash-*.tar.gz` + DISTNAME=`echo ${SOURCEDIST} | sed 's/.tar.*//'` + # Correct tar file order + mkdir -p temp + pushd temp + tar xf ../$SOURCEDIST + find smartcash-* | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ../$SOURCEDIST + popd + + ORIGPATH="$PATH" + # Extract the release tarball into a dir for each host and build + for i in ${HOSTS}; do + export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} + mkdir -p distsrc-${i} + cd distsrc-${i} + INSTALLPATH=`pwd`/installed/${DISTNAME} + mkdir -p ${INSTALLPATH} + tar --strip-components=1 -xf ../$SOURCEDIST + + ./configure --prefix=${BASEPREFIX}/${i} --bindir=${INSTALLPATH}/bin --includedir=${INSTALLPATH}/include --libdir=${INSTALLPATH}/lib --disable-ccache --disable-maintainer-mode --disable-dependency-tracking ${CONFIGFLAGS} + make ${MAKEOPTS} NO_QT=1 + make NO_QT=1 install-strip + cd installed + find . -name "lib*.la" -delete + find . -name "lib*.a" -delete + rm -rf ${DISTNAME}/lib/pkgconfig + find ${DISTNAME} | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}.tar.gz + cd ../../ + done + mkdir -p $OUTDIR/src + mv $SOURCEDIST $OUTDIR/src + mv ${OUTDIR}/${DISTNAME}-arm-*.tar.gz ${OUTDIR}/${DISTNAME}-arm-rpi.tar.gz diff --git a/contrib/seeds/nodes_main.txt b/contrib/seeds/nodes_main.txt index 57fb951a..890fa475 100644 --- a/contrib/seeds/nodes_main.txt +++ b/contrib/seeds/nodes_main.txt @@ -7,4 +7,6 @@ 64.110.130.146:9678 103.42.212.200:9678 45.76.38.200:9678 -198.245.51.138:19678 \ No newline at end of file +[2620:1e8:2:1::300:9678] +[2604:880:a:6::776:9678] +172.110.9.185:19678 diff --git a/depends/packages/zeromq.mk b/depends/packages/zeromq.mk index f8901f72..6f35ede2 100644 --- a/depends/packages/zeromq.mk +++ b/depends/packages/zeromq.mk @@ -1,21 +1,32 @@ package=zeromq -$(package)_version=4.1.4 -$(package)_download_path=http://download.zeromq.org +$(package)_version=4.3.1 +$(package)_download_path=https://github.com/zeromq/libzmq/releases/download/v$($(package)_version)/ $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=e99f44fde25c2e4cb84ce440f87ca7d3fe3271c2b8cfbc67d55e4de25e6fe378 +$(package)_sha256_hash=bcbabe1e2c7d0eec4ed612e10b94b112dd5f06fcefa994a0c79a45d835cd21eb +$(package)_patches=0001-fix-build-with-older-mingw64.patch 0002-disable-pthread_set_name_np.patch define $(package)_set_vars - $(package)_config_opts=--without-documentation --disable-shared --without-libsodium + $(package)_config_opts=--without-docs --disable-shared --disable-curve --disable-curve-keygen --disable-perf + $(package)_config_opts += --without-libsodium --without-libgssapi_krb5 --without-pgm --without-norm --without-vmci + $(package)_config_opts += --disable-libunwind --disable-radix-tree --without-gcov --disable-dependency-tracking + $(package)_config_opts += --disable-Werror --disable-drafts --enable-option-checking $(package)_config_opts_linux=--with-pic + $(package)_config_opts_android=--with-pic $(package)_cxxflags=-std=c++11 endef +define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/0001-fix-build-with-older-mingw64.patch && \ + patch -p1 < $($(package)_patch_dir)/0002-disable-pthread_set_name_np.patch && \ + cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub config +endef + define $(package)_config_cmds $($(package)_autoconf) endef define $(package)_build_cmds - $(MAKE) libzmq.la + $(MAKE) src/libzmq.la endef define $(package)_stage_cmds @@ -23,5 +34,6 @@ define $(package)_stage_cmds endef define $(package)_postprocess_cmds - rm -rf bin share + sed -i.old "s/ -lstdc++//" lib/pkgconfig/libzmq.pc && \ + rm -rf bin share lib/*.la endef 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 00000000..b911ac56 --- /dev/null +++ b/depends/patches/zeromq/0001-fix-build-with-older-mingw64.patch @@ -0,0 +1,30 @@ +From f6866b0f166ad168618aae64c7fbee8775d3eb23 Mon Sep 17 00:00:00 2001 +From: mruddy <6440430+mruddy@users.noreply.github.com> +Date: Sat, 30 Jun 2018 09:44:58 -0400 +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 6c3839fd..2c32ec79 100644 +--- a/src/windows.hpp ++++ b/src/windows.hpp +@@ -58,6 +58,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.17.1 + diff --git a/depends/patches/zeromq/0002-disable-pthread_set_name_np.patch b/depends/patches/zeromq/0002-disable-pthread_set_name_np.patch new file mode 100644 index 00000000..b1c6f78a --- /dev/null +++ b/depends/patches/zeromq/0002-disable-pthread_set_name_np.patch @@ -0,0 +1,35 @@ +From c9bbdd6581d07acfe8971e4bcebe278a3676cf03 Mon Sep 17 00:00:00 2001 +From: mruddy <6440430+mruddy@users.noreply.github.com> +Date: Sat, 30 Jun 2018 09:57:18 -0400 +Subject: [PATCH] disable pthread_set_name_np + +pthread_set_name_np adds a Glibc requirement on >= 2.12. +--- + src/thread.cpp | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/thread.cpp b/src/thread.cpp +index a1086b0c..9943f354 100644 +--- a/src/thread.cpp ++++ b/src/thread.cpp +@@ -308,7 +308,7 @@ void zmq::thread_t::setThreadName (const char *name_) + */ + if (!name_) + return; +- ++#if 0 + #if defined(ZMQ_HAVE_PTHREAD_SETNAME_1) + int rc = pthread_setname_np (name_); + if (rc) +@@ -324,6 +324,8 @@ void zmq::thread_t::setThreadName (const char *name_) + #elif defined(ZMQ_HAVE_PTHREAD_SET_NAME) + pthread_set_name_np (_descriptor, name_); + #endif ++#endif ++ return; + } + + #endif +-- +2.17.1 + diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 9a2baaa0..15771d76 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -96,6 +96,7 @@ QT_TS = \ QT_FORMS_UI = \ qt/forms/addressbookpage.ui \ + qt/forms/addressconverter.ui \ qt/forms/addmilestonedialog.ui \ qt/forms/askpassphrasedialog.ui \ qt/forms/coincontroldialog.ui \ @@ -130,6 +131,7 @@ QT_FORMS_UI = \ QT_MOC_CPP = \ qt/moc_addressbookpage.cpp \ + qt/moc_addressconverter.cpp \ qt/moc_addmilestonedialog.cpp \ qt/moc_addresstablemodel.cpp \ qt/moc_askpassphrasedialog.cpp \ @@ -178,6 +180,7 @@ QT_MOC_CPP = \ qt/moc_smartproposaltab.cpp \ qt/moc_splashscreen.cpp \ qt/moc_specialtransactiondialog.cpp \ + qt/moc_timelocksettingswidget.cpp \ qt/moc_trafficgraphwidget.cpp \ qt/moc_transactiondesc.cpp \ qt/moc_transactiondescdialog.cpp \ @@ -214,6 +217,7 @@ PROTOBUF_PROTO = qt/paymentrequest.proto BITCOIN_QT_H = \ qt/addressbookpage.h \ + qt/addressconverter.h \ qt/addmilestonedialog.h \ qt/addresstablemodel.h \ qt/askpassphrasedialog.h \ @@ -266,6 +270,7 @@ BITCOIN_QT_H = \ qt/smartproposaltab.h \ qt/splashscreen.h \ qt/specialtransactiondialog.h \ + qt/timelocksettingswidget.h \ qt/trafficgraphdata.h \ qt/trafficgraphwidget.h \ qt/transactiondesc.h \ @@ -372,6 +377,7 @@ BITCOIN_QT_WINDOWS_CPP = qt/winshutdownmonitor.cpp BITCOIN_QT_WALLET_CPP = \ qt/addressbookpage.cpp \ + qt/addressconverter.cpp \ qt/addmilestonedialog.cpp \ qt/addresstablemodel.cpp \ qt/askpassphrasedialog.cpp \ @@ -401,6 +407,7 @@ BITCOIN_QT_WALLET_CPP = \ qt/smartvotingmanager.cpp \ qt/smartproposaltab.cpp \ qt/specialtransactiondialog.cpp \ + qt/timelocksettingswidget.cpp \ qt/transactiondesc.cpp \ qt/transactiondescdialog.cpp \ qt/transactionfilterproxy.cpp \ diff --git a/src/base58.cpp b/src/base58.cpp index 675e8d66..c757d5cc 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -18,6 +18,8 @@ /** All alphanumeric characters except for "0", "I", "O", and "l" */ static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; +bool fUseNewAddressFormat = DEFAULT_USE_NEW_ADDRESS_FORMAT; + bool DecodeBase58(const char* psz, std::vector& vch) { // Skip leading spaces. @@ -218,7 +220,7 @@ bool CBase58Data::SetString(const char* psz, unsigned int nVersionBytes) vchData.resize(vchTempNew.size() - nVersionBytes); if (!vchData.empty()) memcpy(&vchData[0], &vchTempNew[nVersionBytes], vchData.size()); - memory_cleanse(&vchTemp[0], vchTempNew.size()); + memory_cleanse(&vchTempNew[0], vchTempNew.size()); } @@ -230,19 +232,85 @@ bool CBase58Data::SetString(const std::string& str) return SetString(str.c_str()); } -std::string CBase58Data::ToString(bool newFormat) const +std::string CBase58Data::ToString() const +{ + std::vector vch = vchVersion; + if(fUseNewAddressFormat){ + if(vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)){ + vch = Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS_V2); + } + if(vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS)){ + vch = Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS_V2); + } + if(vchVersion == Params().Base58Prefix(CChainParams::SECRET_KEY)){ + vch = Params().Base58Prefix(CChainParams::SECRET_KEY_V2); + } + if(vchVersion == Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY)){ + vch = Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY_V2); + } + if(vchVersion == Params().Base58Prefix(CChainParams::EXT_SECRET_KEY)){ + vch = Params().Base58Prefix(CChainParams::EXT_SECRET_KEY_V2); + } + vch.insert(vch.end(), vchData.begin(), vchData.end()); + return EncodeBase58CheckNew(vch); + }else{ + if(vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS_V2)){ + vch = Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS); + } + if(vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS_V2)){ + vch = Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS); + } + if(vchVersion == Params().Base58Prefix(CChainParams::SECRET_KEY_V2)){ + vch = Params().Base58Prefix(CChainParams::SECRET_KEY); + } + if(vchVersion == Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY_V2)){ + vch = Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY); + } + if(vchVersion == Params().Base58Prefix(CChainParams::EXT_SECRET_KEY_V2)){ + vch = Params().Base58Prefix(CChainParams::EXT_SECRET_KEY); + } + vch.insert(vch.end(), vchData.begin(), vchData.end()); + return EncodeBase58Check(vch); + } +} + +std::string CBase58Data::ToString(bool fNewFormat) const { std::vector vch = vchVersion; - if(newFormat){ - if(vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)){ - vch = Params().Base58Prefix(CChainParams::NEW_PUBKEY_ADDRESS); - } + if(fNewFormat){ + if(vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)){ + vch = Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS_V2); + } + if(vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS)){ + vch = Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS_V2); + } + if(vchVersion == Params().Base58Prefix(CChainParams::SECRET_KEY)){ + vch = Params().Base58Prefix(CChainParams::SECRET_KEY_V2); + } + if(vchVersion == Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY)){ + vch = Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY_V2); + } + if(vchVersion == Params().Base58Prefix(CChainParams::EXT_SECRET_KEY)){ + vch = Params().Base58Prefix(CChainParams::EXT_SECRET_KEY_V2); + } vch.insert(vch.end(), vchData.begin(), vchData.end()); return EncodeBase58CheckNew(vch); }else{ - if(vchVersion == Params().Base58Prefix(CChainParams::NEW_PUBKEY_ADDRESS)){ - vch = Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS); - } + if(vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS_V2)){ + vch = Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS); + } + if(vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS_V2)){ + vch = Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS); + } + if(vchVersion == Params().Base58Prefix(CChainParams::SECRET_KEY_V2)){ + vch = Params().Base58Prefix(CChainParams::SECRET_KEY); + } + if(vchVersion == Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY_V2)){ + vch = Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY); + } + if(vchVersion == Params().Base58Prefix(CChainParams::EXT_SECRET_KEY_V2)){ + vch = Params().Base58Prefix(CChainParams::EXT_SECRET_KEY); + } vch.insert(vch.end(), vchData.begin(), vchData.end()); return EncodeBase58Check(vch); } @@ -304,20 +372,26 @@ bool CBitcoinAddress::IsValid(const CChainParams& params) const { bool fCorrectSize = vchData.size() == 20; bool fKnownVersion = vchVersion == params.Base58Prefix(CChainParams::PUBKEY_ADDRESS) || - vchVersion == params.Base58Prefix(CChainParams::NEW_PUBKEY_ADDRESS) || - vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); + vchVersion == params.Base58Prefix(CChainParams::PUBKEY_ADDRESS_V2) || + vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS) || + vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS_V2); return fCorrectSize && fKnownVersion; } +bool CBitcoinAddress::IsValid(const CChainParams::Base58Type& type) const +{ + return vchData.size() == 20 && vchVersion == Params().Base58Prefix(type); +} + CTxDestination CBitcoinAddress::Get() const { if (!IsValid()) return CNoDestination(); uint160 id; memcpy(&id, &vchData[0], 20); - if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS) || vchVersion == Params().Base58Prefix(CChainParams::NEW_PUBKEY_ADDRESS)) + if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS) || vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS_V2)) return CKeyID(id); - else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS)) + else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS) || vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS_V2)) return CScriptID(id); else return CNoDestination(); @@ -327,11 +401,11 @@ bool CBitcoinAddress::GetIndexKey(uint160& hashBytes, int& type) const { if (!IsValid()) { return false; - } else if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS) || vchVersion == Params().Base58Prefix(CChainParams::NEW_PUBKEY_ADDRESS)) { + } else if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS) || vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS_V2)) { memcpy(&hashBytes, &vchData[0], 20); type = 1; return true; - } else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS)) { + } else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS) || vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS_V2)) { memcpy(&hashBytes, &vchData[0], 20); type = 2; return true; @@ -342,7 +416,7 @@ bool CBitcoinAddress::GetIndexKey(uint160& hashBytes, int& type) const bool CBitcoinAddress::GetKeyID(CKeyID& keyID) const { - if (!IsValid() || (vchVersion != Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS) && vchVersion != Params().Base58Prefix(CChainParams::NEW_PUBKEY_ADDRESS))) + if (!IsValid() || (vchVersion != Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS) && vchVersion != Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS_V2))) return false; uint160 id; memcpy(&id, &vchData[0], 20); @@ -352,7 +426,7 @@ bool CBitcoinAddress::GetKeyID(CKeyID& keyID) const bool CBitcoinAddress::IsScript() const { - return IsValid() && vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS); + return IsValid() && ( vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS) || vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS_V2) ); } void CBitcoinSecret::SetKey(const CKey& vchSecret) @@ -374,7 +448,7 @@ CKey CBitcoinSecret::GetKey() bool CBitcoinSecret::IsValid() const { bool fExpectedFormat = vchData.size() == 32 || (vchData.size() == 33 && vchData[32] == 1); - bool fCorrectVersion = vchVersion == Params().Base58Prefix(CChainParams::SECRET_KEY); + bool fCorrectVersion = ( vchVersion == Params().Base58Prefix(CChainParams::SECRET_KEY) || vchVersion == Params().Base58Prefix(CChainParams::SECRET_KEY_V2) ); return fExpectedFormat && fCorrectVersion; } diff --git a/src/base58.h b/src/base58.h index 9f262bd4..08673e47 100644 --- a/src/base58.h +++ b/src/base58.h @@ -24,6 +24,11 @@ #include #include +//! Default for -usenewaddressformat +static const bool DEFAULT_USE_NEW_ADDRESS_FORMAT = false; + +extern bool fUseNewAddressFormat; + /** * Encode a byte sequence as a base58-encoded string. * pbegin and pend cannot be NULL, unless both are. @@ -88,7 +93,8 @@ class CBase58Data public: bool SetString(const char* psz, unsigned int nVersionBytes = 1); bool SetString(const std::string& str); - std::string ToString(bool newFormat=false) const; + std::string ToString() const; + std::string ToString(bool fNewFormat) const; int CompareTo(const CBase58Data& b58) const; bool operator==(const CBase58Data& b58) const { return CompareTo(b58) == 0; } @@ -111,6 +117,7 @@ class CBitcoinAddress : public CBase58Data { bool Set(const CTxDestination &dest); bool IsValid() const; bool IsValid(const CChainParams ¶ms) const; + bool IsValid(const CChainParams::Base58Type& type) const; CBitcoinAddress() {} CBitcoinAddress(const CTxDestination &dest) { Set(dest); } diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 1edaacfe..8f7a1be9 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -139,10 +139,10 @@ class CMainParams : public CChainParams { consensus.nRewardsPayouts_1_2_BlockPayees = 1000; //! 1.3 Parameter - consensus.nRewardsBlocksPerRound_1_3 = 142500; - consensus.nRewardsFirst_1_3_Round = 29; - consensus.nRewardsPayouts_1_3_BlockStretch = 10990; // 1 week - consensus.nRewardsPayouts_1_3_BlockPayees = 10; + consensus.nRewardsBlocksPerRound_1_3 = 11000; // 1 week + consensus.nRewardsFirst_1_3_Round = 36; // Round 36 on 6/25 starts on block 1666600 + consensus.nRewardsPayouts_1_3_BlockStretch = 10000; + consensus.nRewardsPayouts_1_3_BlockPayees = 100; consensus.strRewardsGlobalVoteProofAddress = "TBD"; @@ -171,14 +171,11 @@ class CMainParams : public CChainParams { // Note that of those with the service bits flag, most only support a subset of possible options vSeeds.push_back(CDNSSeedData("seed.smrt.cash", "seed.smrt.cash", false)); - vSeeds.push_back(CDNSSeedData("seed1.smrt.cash", "seed1.smrt.cash", false)); - vSeeds.push_back(CDNSSeedData("seed2.smrt.cash", "seed2.smrt.cash", false)); + vSeeds.push_back(CDNSSeedData("seed.smrt.run", "seed.smrt.run", false)); + vSeeds.push_back(CDNSSeedData("seed.smrt.best", "seed.smrt.best", false)); + vSeeds.push_back(CDNSSeedData("seed.smarts.cash", "seed.smarts.cash", false)); vSeeds.push_back(CDNSSeedData("seed1.smartcash.org", "seed1.smartcash.org", false)); vSeeds.push_back(CDNSSeedData("seed2.smartcash.org", "seed2.smartcash.org", false)); - vSeeds.push_back(CDNSSeedData("seed.smartcash.cc", "seed.smartcash.cc", false)); - vSeeds.push_back(CDNSSeedData("seed2.smartcash.cc", "seed2.smartcash.cc", false)); - vSeeds.push_back(CDNSSeedData("seed3.smartcash.cc", "seed3.smartcash.cc", false)); - vSeeds.push_back(CDNSSeedData("seed4.smartcash.cc", "seed4.smartcash.cc", false)); base58Prefixes[PUBKEY_ADDRESS] = std::vector(1,63); //S @@ -186,9 +183,15 @@ class CMainParams : public CChainParams { base58Prefixes[SECRET_KEY] = std::vector(1,191); base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x88)(0xB2)(0x1E).convert_to_container >(); base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x88)(0xAD)(0xE4).convert_to_container >(); + + base58Prefixes[PUBKEY_ADDRESS_V2] = std::vector(1,125); //s + base58Prefixes[SCRIPT_ADDRESS_V2] = std::vector(1,110); + base58Prefixes[SECRET_KEY_V2] = std::vector(1,237); + base58Prefixes[EXT_PUBLIC_KEY_V2] = boost::assign::list_of(0x04)(0x20)(0xBD)(0x3F).convert_to_container >(); + base58Prefixes[EXT_SECRET_KEY_V2] = boost::assign::list_of(0x04)(0x20)(0xB9)(0x03).convert_to_container >(); + base58Prefixes[VOTE_KEY_PUBLIC] = std::vector(1,125); base58Prefixes[VOTE_KEY_SECRET] = std::vector(3,82); - base58Prefixes[NEW_PUBKEY_ADDRESS] = std::vector(1,125); //s // SmartCash BIP44 coin type is '224' nExtCoinType = 224; @@ -209,12 +212,16 @@ class CMainParams : public CChainParams { ( 75000, uint256S("0x000000000002ee203026137ebc460e1886e09b9fdb0e83697e5a74976088e75c")) ( 170000, uint256S("0x000000000000670ff41fbb4ad819b48bfe1c35623f13297d3fbf9bf02abcd87c")) ( 500000, uint256S("0x00000000000016a1fa8e650e5a82babefeb9225ffe78614bc4b23cf160d16eeb")) + ( 750000, uint256S("0x000000000000456bd57843a6650155f9c09b42c47e5a8d24418881a88ce8aa2e")) ( 1000000, uint256S("0x00000000000008e14776878dba228ac957a97205df4716ce1913ae4339e7aeb9")) - ( 1030000, uint256S("0x00000000000000d7e76cc6c30a2bece10f552123ad3c9a63beceb0d553a46f04")), - 1557812640, // * UNIX timestamp of last checkpoint block - 4095887, // * total number of transactions between genesis and last checkpoint + ( 1030000, uint256S("0x00000000000000d7e76cc6c30a2bece10f552123ad3c9a63beceb0d553a46f04")) + ( 1250000, uint256S("0x00000000000036b03ca216e92c83c9d0d152c1fdfac74c1bfc0cfc1cfa00f451")) + ( 1500000, uint256S("0x0000000000001e396ce1ea9dfde2956fef0f606a5d6cbbcb1a5ba6e1081eadf5")) + ( 1599000, uint256S("0x00000000000024edb61519ed6ebdf085f5dd25a0963103dc108b68e5f88604f3")), + 1589123846, // * UNIX timestamp of last checkpoint block + 11577739, // * total number of transactions between genesis and last checkpoint // (the tx=... number in the SetBestChain debug.log lines) - 12000.0 // * estimated number of transactions per day after checkpoint + 33000.0 // * estimated number of transactions per day after checkpoint }; } }; @@ -277,17 +284,17 @@ class CTestNetParams : public CChainParams { /* SmartReward params */ consensus.nRewardsConfirmationsRequired = 1; - consensus.nRewardsPayoutStartDelay = 20; + consensus.nRewardsPayoutStartDelay = 10; //! 1.2 Parameter - consensus.nRewardsBlocksPerRound_1_2 = 500; + consensus.nRewardsBlocksPerRound_1_2 = 100; consensus.nRewardsPayouts_1_2_BlockInterval = 2; consensus.nRewardsPayouts_1_2_BlockPayees = 1000; //! 1.3 Parameter - consensus.nRewardsBlocksPerRound_1_3 = 1500; - consensus.nRewardsFirst_1_3_Round = 5; - consensus.nRewardsPayouts_1_3_BlockStretch = 1000; + consensus.nRewardsBlocksPerRound_1_3 = 100; + consensus.nRewardsFirst_1_3_Round = 10; // block 201 start 1_2_8 401 start 1_3 + consensus.nRewardsPayouts_1_3_BlockStretch = 80; consensus.nRewardsPayouts_1_3_BlockPayees = 10; consensus.strRewardsGlobalVoteProofAddress = "TTUR2YweEsouT7nnqLGn3LgoykhPnFQkSY"; @@ -314,17 +321,22 @@ class CTestNetParams : public CChainParams { vFixedSeeds.clear(); vSeeds.clear(); // nodes with support for servicebits filtering should be at the top - vSeeds.push_back(CDNSSeedData("testnet.smartcash.cc", "testnet.smartcash.cc", true)); - vSeeds.push_back(CDNSSeedData("testnet.smrt.cash", "testnet.smrt.cash", true)); + vSeeds.push_back(CDNSSeedData("testnet.smrt.run", "testnet.smrt.run", true)); base58Prefixes[PUBKEY_ADDRESS] = std::vector(1,65); //T base58Prefixes[SCRIPT_ADDRESS] = std::vector(1,21); base58Prefixes[SECRET_KEY] = std::vector(1,193); base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container >(); base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container >(); + + base58Prefixes[PUBKEY_ADDRESS_V2] = std::vector(1,127); //t + base58Prefixes[SCRIPT_ADDRESS_V2] = std::vector(1,13); //6 + base58Prefixes[SECRET_KEY_V2] = std::vector(1,130); //u + base58Prefixes[EXT_PUBLIC_KEY_V2] = boost::assign::list_of(0x7F)(0x35)(0x87)(0xCF).convert_to_container >(); + base58Prefixes[EXT_SECRET_KEY_V2] = boost::assign::list_of(0x7F)(0x35)(0x87)(0xCF).convert_to_container >(); + base58Prefixes[VOTE_KEY_PUBLIC] = std::vector(1,112); base58Prefixes[VOTE_KEY_SECRET] = std::vector(3,160); - base58Prefixes[NEW_PUBKEY_ADDRESS] = std::vector(1,127); //t // SmartCash BIP44 coin type is '224' nExtCoinType = 224; diff --git a/src/chainparams.h b/src/chainparams.h index 84dcd8f7..be68a3bb 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -49,9 +49,13 @@ class CChainParams SECRET_KEY, EXT_PUBLIC_KEY, EXT_SECRET_KEY, + PUBKEY_ADDRESS_V2, + SCRIPT_ADDRESS_V2, + SECRET_KEY_V2, + EXT_PUBLIC_KEY_V2, + EXT_SECRET_KEY_V2, VOTE_KEY_PUBLIC, VOTE_KEY_SECRET, - NEW_PUBKEY_ADDRESS, MAX_BASE58_TYPES }; diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h index 289a4279..4a2b272d 100644 --- a/src/chainparamsseeds.h +++ b/src/chainparamsseeds.h @@ -16,10 +16,12 @@ static SeedSpec6 pnSeed6_main[] = { {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xac,0x6e,0x12,0x45}, 9678}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x40,0x6e,0x82,0x92}, 9678}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x67,0x2a,0xd4,0xc8}, 9678}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x4c,0x26,0xc8}, 9678} + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x4c,0x26,0xc8}, 9678}, + {{0x26,0x20,0x01,0xe8,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x03,0x00,0x96,0x78}, 9678}, + {{0x26,0x04,0x08,0x80,0x00,0x0a,0x00,0x06,0x00,0x00,0x00,0x00,0x07,0x76,0x96,0x78}, 9678} }; static SeedSpec6 pnSeed6_test[] = { - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc6,0xf5,0x33,0x8a}, 19678} + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xac,0x6e,0x09,0xb9}, 19678} }; #endif // BITCOIN_CHAINPARAMSSEEDS_H diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index 5d83dc15..2816c939 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -23,7 +23,7 @@ namespace Checkpoints { * can be up to 20, while when downloading from a slow network with a * fast multicore CPU, it won't be much higher than 1. */ - static const double SIGCHECK_VERIFICATION_FACTOR = 5.0; + static const double SIGCHECK_VERIFICATION_FACTOR = 1.0; //! Guess how far we are in the verification process at the given block index double GuessVerificationProgress(const CCheckpointData& data, CBlockIndex *pindex, bool fSigchecks) { diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index ee5cecc0..2c90438f 100644 --- a/src/consensus/consensus.h +++ b/src/consensus/consensus.h @@ -9,11 +9,11 @@ #include /** The maximum allowed size for a serialized block, in bytes (only for buffer size limits) */ -static const unsigned int MAX_BLOCK_SERIALIZED_SIZE = 8000000; +static const unsigned int MAX_BLOCK_SERIALIZED_SIZE = 16000000; /** The maximum allowed weight for a block, see BIP 141 (network rule) */ -static const unsigned int MAX_BLOCK_WEIGHT = 16000000; +static const unsigned int MAX_BLOCK_WEIGHT = 32000000; /** The maximum allowed size for a block excluding witness data, in bytes (network rule) */ -static const unsigned int MAX_BLOCK_BASE_SIZE = 4000000; +static const unsigned int MAX_BLOCK_BASE_SIZE = 8000000; /** The maximum allowed number of signature check operations in a block (network rule) */ static const int64_t MAX_BLOCK_SIGOPS_COST = 640000; /** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */ @@ -29,7 +29,7 @@ static const int HF_V1_2_MULTINODE_VOTING_HEIGHT = 535000; static const int HF_V1_2_MULTINODE_PAYOUT_HEIGHT = 545005; static const int HF_V1_2_SMARTREWARD_HEIGHT = 574100; static const int HF_V1_2_8_SMARNODE_NEW_COLLATERAL_HEIGHT = 910000; -static const int HF_V1_3_SMARTREWARD_WITHOUT_NODE_HEIGHT = 1200000; // TBD - set the right height before release +static const int HF_V1_3_HEIGHT = 1666600; // Round 36 starts 1666600 /* Mainnet payment intervals*/ static const int HF_V1_2_NODES_PER_BLOCK = 10; @@ -40,21 +40,17 @@ static const int HF_V1_2_8_NODES_PER_BLOCK = 1; static const int HF_CHAIN_REWARD_END_HEIGHT = 717499999; /** Testnet payment start blocks*/ -static const int TESTNET_V1_2_PAYMENTS_HEIGHT = 1000; -static const int TESTNET_V1_2_MULTINODE_PAYMENTS_HEIGHT_1 = 1001; // 28500; -static const int TESTNET_V1_2_MULTINODE_PAYMENTS_HEIGHT_2 = 1002; // 30300; -static const int TESTNET_V1_2_MULTINODE_PAYMENTS_HEIGHT_3 = 1003; // 75000; -static const int TESTNET_V1_2_8_SMARNODE_NEW_COLLATERAL_HEIGHT = 1004; // 415000; -static const int HF_V1_3_SMARTREWARD_WITHOUT_NODE_HEIGHT_TESTNET = 35000; +static const int TESTNET_V1_2_8_PAYMENTS_HEIGHT = 201; +static const int TESTNET_V1_3_HEIGHT = 1001; //round start is 10 x 100 block 1000-1100 payments start at 1110 /** Testnet payment intervals*/ -static const int TESTNET_V1_2_NODES_PER_BLOCK_1 = 3; -static const int TESTNET_V1_2_NODES_BLOCK_INTERVAL_1 = 6; -static const int TESTNET_V1_2_NODES_PER_BLOCK_2 = 3; -static const int TESTNET_V1_2_NODES_BLOCK_INTERVAL_2 = 3; -static const int TESTNET_V1_2_NODES_PER_BLOCK_3 = 10; -static const int TESTNET_V1_2_NODES_BLOCK_INTERVAL_3 = 2; -static const int TESTNET_V1_2_8_NODES_PER_BLOCK = 1; +static const int TESTNET_V1_2_8_NODES_PER_BLOCK = 1; +static const int TESTNET_V1_2_8_NODES_BLOCK_INTERVAL = 2; +static const int TESTNET_V1_3_NODES_PER_BLOCK = 1; +static const int TESTNET_V1_3_NODES_BLOCK_INTERVAL = 2; + +/** Minimum number of active SmartNodes required to make SmartNode payments */ +static const int MIN_ACTIVE_SMARTNODES = 3; inline unsigned int MaxBlockSigOps() { diff --git a/src/hash.h b/src/hash.h index ad363ca3..9d617176 100644 --- a/src/hash.h +++ b/src/hash.h @@ -19,7 +19,6 @@ #include typedef uint256 ChainCode; -static bool newHash = false; /** A hasher class for Bitcoin's 256-bit hash (double SHA-256). */ class CHash256 { @@ -163,7 +162,6 @@ class CHashWriter { private: SHA256_CTX ctx; - CHash256 ctxNew; public: int nType; @@ -184,15 +182,9 @@ class CHashWriter // invalidates the object uint256 GetHash() { - if(newHash){ - uint256 result; - ctxNew.Finalize((unsigned char*)&result); - return result; - }else{ uint256 hash1; SHA256_Final((unsigned char*)&hash1, &ctx); return hash1; - } } arith_uint256 GetArith256Hash() { diff --git a/src/init.cpp b/src/init.cpp index 0f5b3468..8b9da3b5 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -607,7 +607,13 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-rpcworkqueue=", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE)); strUsage += HelpMessageOpt("-rpcservertimeout=", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT)); } - + strUsage += HelpMessageGroup(_("SAPI server options:")); + strUsage += HelpMessageOpt("-sapi", _("Enable SmartCash API server and databases. Also enables -addressindex, -spentindex, -depositindex, -instantpayindex.")); + strUsage += HelpMessageOpt("-sapiport=",_("Listen for SAPI requests on (default: 8080)")); + strUsage += HelpMessageOpt("-sapithreads=",_("Set the number of threads for SAPI requests (default: 4)")); + strUsage += HelpMessageOpt("-sapiworkqueue=",_("Set the queue for SAPI requests (default: 16)")); + strUsage += HelpMessageOpt("-sapiservertimeout=",_("Set the seconds before SAPI timeout (default: 30)")); + strUsage += HelpMessageOpt("-sapiwhitelist=",_("Whitelist ip for SAPI")); return strUsage; } @@ -824,10 +830,10 @@ void InitParameterInteraction() LogPrintf("%s: parameter interaction: -whitebind set -> setting -listen=1\n", __func__); } - if (GetBoolArg("-smartnode", false)) { + if (GetBoolArg("-smartnode", true)) { // smartnodes must accept connections from outside - if (SoftSetBoolArg("-listen", true)) - LogPrintf("%s: parameter interaction: -smartnode=1 -> setting -listen=1\n", __func__); + if (SoftSetBoolArg("-listen", true) && SoftSetBoolArg("-sapi", true)) + LogPrintf("%s: parameter interaction: -smartnode=1 -> setting -listen=1 and -sapi=1\n", __func__); } if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) { @@ -1259,6 +1265,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) nTxConfirmTarget = GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); fSendFreeTransactions = GetBoolArg("-sendfreetransactions", DEFAULT_SEND_FREE_TRANSACTIONS); + fUseNewAddressFormat = GetBoolArg("-usenewaddressformat", DEFAULT_USE_NEW_ADDRESS_FORMAT); std::string strWalletFile = GetArg("-wallet", "wallet.dat"); #endif // ENABLE_WALLET @@ -1645,13 +1652,64 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) int64_t nRewardsCache = (GetArg("-rewardsdbcache", nRewardsDefaultDbCache) << 20); LogPrintf("* Using %.1fMiB for smart rewards database\n", nRewardsCache * (1.0 / 1024 / 1024)); + nCacheRewardEntries = GetArg("-rewardsentrycache", REWARDS_CACHE_ENTRIES_DEFAULT); + delete prewards; CSmartRewardsDB *prewardsdb = new CSmartRewardsDB(nRewardsCache, false, false); prewards = new CSmartRewards(prewardsdb); + uiInterface.InitMessage(_("Loading SmartRewards...")); + bool fLoaded = false; + + while (!fLoaded) { + + std::string strLoadError; + + nStart = GetTimeMillis(); + do { + try { + + if( fReindex ){ + delete prewards; + + prewardsdb = new CSmartRewardsDB(nRewardsCache, false, true); + prewards = new CSmartRewards(prewardsdb); + } + + if( !(fLoaded = prewards->Verify()) ) throw std::runtime_error(_("Failed to verify SmartRewards database.")); + + if (fRequestShutdown) break; + + } catch (const std::runtime_error &e) { + if (fDebug) LogPrintf("%s\n", e.what()); + strLoadError = e.what(); + break; + } catch (const std::exception &e) { + if (fDebug) LogPrintf("%s\n", e.what()); + strLoadError = _("Error opening rewards database"); + break; + } catch ( ... ){ + if (fDebug) LogPrintf("Unexpected exception\n"); + strLoadError = _("Unexpected error with the rewards database"); + break; + } + + fLoaded = true; + + } while(false); + + if( !fLoaded ){ + InitWarning(strLoadError + _("\n\nReindexing blockchain data now...")); + fReindex = true; + } + + } + + fLoaded = false; + while (!fLoaded && !fRequestShutdown) { bool fReset = fReindex; std::string strLoadError; @@ -1778,62 +1836,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } LogPrintf(" block index %15dms\n", GetTimeMillis() - nStart); - uiInterface.InitMessage(_("Verifying SmartRewards...")); - - CBlockIndex *pLastIndex = chainActive.Tip(); - bool fResetRewards = fReindex; - fLoaded = false; - - while (!fLoaded) { - - std::string strLoadError; - - nStart = GetTimeMillis(); - do { - try { - - if( fResetRewards ){ - delete prewards; - - prewardsdb = new CSmartRewardsDB(nRewardsCache, false, true); - prewards = new CSmartRewards(prewardsdb); - } - - if( prewards->IsLocked() ) throw std::runtime_error(_("SmartRewards database is incomplete.")); - - if( !(fLoaded = prewards->Verify()) ) throw std::runtime_error(_("Failed to verify SmartRewards database.")); - - if( pLastIndex != NULL && !(fLoaded = (prewards->GetLastHeight() <= pLastIndex->nHeight)) ) throw std::runtime_error(_("SmartRewards database exceeds current chain height.")); - - if (fRequestShutdown) break; - - prewards->Lock(); - - } catch (const std::runtime_error &e) { - if (fDebug) LogPrintf("%s\n", e.what()); - strLoadError = e.what(); - break; - } catch (const std::exception &e) { - if (fDebug) LogPrintf("%s\n", e.what()); - strLoadError = _("Error opening rewards database"); - break; - } catch ( ... ){ - if (fDebug) LogPrintf("Unexpected exception\n"); - strLoadError = _("Unexpected error with the rewards database"); - break; - } - - fLoaded = true; - - } while(false); - - if( !fLoaded ){ - InitWarning(strLoadError + _("\n\nRecreating it now...")); - fResetRewards = true; - } - - } - // As LoadBlockIndex can take several minutes, it's possible the user // requested to kill the GUI during the last operation. If so, exit. // As the program has not fully started yet, Shutdown() is possibly overkill. @@ -2293,5 +2295,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) threadGroup.create_thread(boost::bind(&ThreadSendAlert, boost::ref(connman))); + if( MainNet() ){ + InitError("Mainnet is not available in this beta. You can start the client on Testnet with testnet=1 in the smartcash.conf or -testnet=1 as command line argument."); + StartShutdown(); + } + return !fRequestShutdown; } diff --git a/src/keystore.cpp b/src/keystore.cpp index b04b769f..85efd001 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -88,6 +88,16 @@ bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut) return false; } +std::set CBasicKeyStore::GetCScripts() const +{ + LOCK(cs_KeyStore); + std::set setScripts; + for (const auto& mi : mapScripts) { + setScripts.insert(mi.first); + } + return setScripts; +} + static bool ExtractPubKey(const CScript &dest, CPubKey& pubKeyOut) { //TODO: Use Solver to extract this? diff --git a/src/keystore.h b/src/keystore.h index b423f6a5..7745a00f 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -154,6 +154,7 @@ class CBasicKeyStore : public CKeyStore virtual bool AddCScript(const CScript& redeemScript); virtual bool HaveCScript(const CScriptID &hash) const; virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const; + virtual std::set GetCScripts() const; virtual bool AddWatchOnly(const CScript &dest); virtual bool RemoveWatchOnly(const CScript &dest); diff --git a/src/miner.cpp b/src/miner.cpp index 86d5fa4d..c1e1249b 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -208,7 +208,7 @@ CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn, co pblock->nTime = GetAdjustedTime(); const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); - pblock->nVersion = 2;//ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()); + pblock->nVersion = CBlockHeader::CURRENT_VERSION; // -regtest only: allow overriding block.nVersion with // -blockversion=N to test forking scenarios if (chainparams.MineBlocksOnDemand()) diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 92ffbea6..6b66de88 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -133,7 +133,7 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) if (!Solver(prevScript, whichType, vSolutions)) return false; - if (whichType == TX_SCRIPTHASH) + if (whichType == TX_SCRIPTHASH || whichType == TX_SCRIPTHASHLOCKED) { std::vector > stack; // convert the scriptSig into a stack, so we can inspect the redeemScript diff --git a/src/primitives/block.h b/src/primitives/block.h index c85cd4f7..fb999649 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -23,7 +23,7 @@ class CBlockHeader { public: // header - static const int CURRENT_VERSION = 2; + static const int CURRENT_VERSION = 4; int nVersion; uint256 hashPrevBlock; diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 59a96a63..ff8d6a19 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -5,6 +5,7 @@ #include "primitives/transaction.h" +#include "pubkey.h" #include "hash.h" #include "tinyformat.h" #include "utilstrencodings.h" @@ -72,6 +73,18 @@ uint256 CTxOut::GetHash() const return SerializeHash(*this); } +uint32_t CTxOut::GetLockTime() const +{ + uint32_t nLockTime = 0; + + if( scriptPubKey.IsPayToPublicKeyHashLocked() || scriptPubKey.IsPayToPublicKeyHashLocked() ){ + std::vector vch(scriptPubKey.begin() + 1, scriptPubKey.begin() + 1 + scriptPubKey[0] ); + nLockTime = CScriptNum(vch, false).getint(); + } + + return nLockTime; +} + std::string CTxOut::ToString() const { return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, HexStr(scriptPubKey).substr(0, 30)); @@ -221,17 +234,33 @@ bool CTransaction::IsVoteKeyRegistration() const return false; } -bool CTransaction::IsVoteProof() const +bool CTransaction::IsActivationTx() const { - // Vote proof transactions must contain only 1 input - // which is from the address to register and maximum 2 outputs - // 1. OP_RETURN with the VoteProofData - // 2. change (optional) - if( vin.size() > 1 || vout.size() > 2 ) return false; - - for (std::vector::const_iterator it(vout.begin()); it != vout.end(); ++it) - { - if (it->IsVoteProofData() ) return true; + if (IsCoinBase()) return false; + + // Activation transaction is a Tx back to the issuing address, it should only have one input and one output + if ( (vin.size() == 1) && (vout.size() == 1) ) { + // Activation transaction should be P2PKH, get the hashed pubkey from output script + const CScript &outScript = vout.front().scriptPubKey; + if (!outScript.IsPayToPublicKeyHash()) return false; + uint160 hashBytes = uint160(std::vector(outScript.begin() + 3, outScript.begin() + 23)); + + // Get pubkey from input script signature by deconding ScriptSig + const CScript &scriptSig = vin.front().scriptSig; + CScript::const_iterator pc = scriptSig.begin(); + opcodetype opcode; + vector vch; + + // First Op should be the signature + scriptSig.GetOp(pc, opcode, vch); + if ((opcode < 0) || (opcode > OP_PUSHDATA4) || (vch.size() < 4)) return false; + + // Second Op should be the public key + scriptSig.GetOp(pc, opcode, vch); + uint160 hashedKey = Hash160(vch); + + // If hashed pubkey matches output hash, then the Tx destination matches the source + if (hashBytes == hashedKey) return true; } return false; diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index cfd2ae9c..916d99cf 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -15,14 +15,8 @@ static const int SERIALIZE_TRANSACTION_NO_WITNESS = 0x40000000; // Constants for vote proof transactions -static const CAmount REWARDS_VOTEPROOF_FEE = 0 * COIN; -static const CAmount REWARDS_VOTEPROOF_TX_FEE = 0.002 * COIN; - -static const int REWARDS_VOTEPROOF_O1_SCRIPT_SIZE = 0x28; -static const int REWARDS_VOTEPROOF_O1_DATA_SIZE = 0x26; - -static const int REWARDS_VOTEPROOF_O2_SCRIPT_SIZE = 0x3D; -static const int REWARDS_VOTEPROOF_O2_DATA_SIZE = 0x3B; +static const CAmount REWARDS_ACTIVATION_FEE = 0 * COIN; +static const CAmount REWARDS_ACTIVATION_TX_FEE = 0.001 * COIN; static const int WITNESS_SCALE_FACTOR = 4; @@ -231,10 +225,7 @@ class CTxOut return scriptPubKey.IsVoteKeyData() && nValue == VOTEKEY_REGISTER_FEE; } - bool IsVoteProofData() const - { - return scriptPubKey.IsVoteProofData() && nValue == REWARDS_VOTEPROOF_FEE; - } + uint32_t GetLockTime() const; friend bool operator==(const CTxOut& a, const CTxOut& b) { @@ -473,7 +464,7 @@ class CTransaction bool IsZerocoinMint(const CTransaction& tx) const; bool IsVoteKeyRegistration() const; - bool IsVoteProof() const; + bool IsActivationTx() const; friend bool operator==(const CTransaction& a, const CTransaction& b) { diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index b8cdab07..66da06e4 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -33,13 +33,11 @@ AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode mode, if (!platformStyle->getImagesOnButtons()) { ui->newAddress->setIcon(QIcon()); ui->copyAddress->setIcon(QIcon()); - ui->copyAddressLegacy->setIcon(QIcon()); ui->deleteAddress->setIcon(QIcon()); ui->exportButton->setIcon(QIcon()); } else { ui->newAddress->setIcon(platformStyle->SingleColorIcon(":/icons/add")); ui->copyAddress->setIcon(platformStyle->SingleColorIcon(":/icons/editcopy")); - ui->copyAddressLegacy->setIcon(platformStyle->SingleColorIcon(":/icons/editcopy")); ui->deleteAddress->setIcon(platformStyle->SingleColorIcon(":/icons/remove")); ui->exportButton->setIcon(platformStyle->SingleColorIcon(":/icons/export")); } @@ -79,7 +77,6 @@ AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode mode, } // Context menu actions - QAction *copyAddressActionLegacy = new QAction(tr("&Copy Address (Legacy)"), this); QAction *copyAddressAction = new QAction(tr("&Copy Address"), this); QAction *copyLabelAction = new QAction(tr("Copy &Label"), this); QAction *editAction = new QAction(tr("&Edit"), this); @@ -87,7 +84,6 @@ AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode mode, // Build context menu contextMenu = new QMenu(this); - contextMenu->addAction(copyAddressActionLegacy); contextMenu->addAction(copyAddressAction); contextMenu->addAction(copyLabelAction); contextMenu->addAction(editAction); @@ -96,7 +92,6 @@ AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode mode, contextMenu->addSeparator(); // Connect signals for context menu actions - connect(copyAddressActionLegacy, SIGNAL(triggered()), this, SLOT(on_copyAddressLegacy_clicked())); connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(on_copyAddress_clicked())); connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(onCopyLabelAction())); connect(editAction, SIGNAL(triggered()), this, SLOT(onEditAction())); @@ -142,11 +137,9 @@ void AddressBookPage::setModel(AddressTableModel *model) // Set column widths #if QT_VERSION < 0x050000 ui->tableView->horizontalHeader()->setResizeMode(AddressTableModel::Label, QHeaderView::Stretch); - ui->tableView->horizontalHeader()->setResizeMode(AddressTableModel::AddressLegacy, QHeaderView::ResizeToContents); ui->tableView->horizontalHeader()->setResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents); #else ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Label, QHeaderView::Stretch); - ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::AddressLegacy, QHeaderView::ResizeToContents); ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents); #endif @@ -164,11 +157,6 @@ void AddressBookPage::on_copyAddress_clicked() GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Address); } -void AddressBookPage::on_copyAddressLegacy_clicked() -{ - GUIUtil::copyEntryData(ui->tableView, AddressTableModel::AddressLegacy); -} - void AddressBookPage::onCopyLabelAction() { GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Label); @@ -249,13 +237,11 @@ void AddressBookPage::selectionChanged() break; } ui->copyAddress->setEnabled(true); - ui->copyAddressLegacy->setEnabled(true); } else { ui->deleteAddress->setEnabled(false); ui->copyAddress->setEnabled(false); - ui->copyAddressLegacy->setEnabled(false); } } @@ -297,7 +283,6 @@ void AddressBookPage::on_exportButton_clicked() // name, column, role writer.setModel(proxyModel); writer.addColumn("Label", AddressTableModel::Label, Qt::EditRole); - writer.addColumn("Address Legacy", AddressTableModel::AddressLegacy, Qt::EditRole); writer.addColumn("Address", AddressTableModel::Address, Qt::EditRole); if(!writer.write()) { diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h index 29d06ba9..c22566d4 100644 --- a/src/qt/addressbookpage.h +++ b/src/qt/addressbookpage.h @@ -67,7 +67,6 @@ private Q_SLOTS: void on_newAddress_clicked(); /** Copy address of currently selected address entry to clipboard */ void on_copyAddress_clicked(); - void on_copyAddressLegacy_clicked(); /** Copy label of currently selected address entry to clipboard (no button) */ void onCopyLabelAction(); /** Edit currently selected address entry (no button) */ diff --git a/src/qt/addressconverter.cpp b/src/qt/addressconverter.cpp new file mode 100644 index 00000000..c3433563 --- /dev/null +++ b/src/qt/addressconverter.cpp @@ -0,0 +1,58 @@ +// Copyright (c) 2011-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "addressconverter.h" +#include "ui_addressconverter.h" + +#include "bitcoinunits.h" +#include "coincontroldialog.h" +#include "guiutil.h" +#include "optionsmodel.h" +#include "platformstyle.h" +#include "txmempool.h" +#include "walletmodel.h" +#include "sendcoinsdialog.h" + +#include "wallet/wallet.h" + +#include // for 'map_list_of()' + +#include +#include +#include +#include + + +AddressConverter::AddressConverter(QWidget *parent) : + QDialog(parent), + ui(new Ui::AddressConverter) +{ + ui->setupUi(this); + + connect(ui->input, SIGNAL(textChanged(QString)), this, SLOT(addressInputChanged())); +} + +AddressConverter::~AddressConverter() +{ + delete ui; +} + +void AddressConverter::addressInputChanged() +{ + if( ui->input->text() == "" ){ + ui->output->setText(""); + return; + } + + CSmartAddress address(ui->input->text().toStdString()); + + if(address.IsValid(CChainParams::PUBKEY_ADDRESS) || address.IsValid(CChainParams::SCRIPT_ADDRESS)){ + ui->output->setText(QString::fromStdString(address.ToString(true))); + }else if(address.IsValid(CChainParams::PUBKEY_ADDRESS_V2) || address.IsValid(CChainParams::SCRIPT_ADDRESS_V2)){ + ui->output->setText(QString::fromStdString(address.ToString(false))); + }else{ + ui->output->setText("Invalid SmartCash address"); + } + +} diff --git a/src/qt/addressconverter.h b/src/qt/addressconverter.h new file mode 100644 index 00000000..1ecdfe7f --- /dev/null +++ b/src/qt/addressconverter.h @@ -0,0 +1,30 @@ +// Copyright (c) 2011-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef SMARTCASH_QT_ADDRESSCONVERTER_H +#define SMARTCASH_QT_ADDRESSCONVERTER_H + +#include + +namespace Ui { + class AddressConverter; +} + +class AddressConverter : public QDialog +{ + Q_OBJECT + +public: + explicit AddressConverter(QWidget *parent = 0); + ~AddressConverter(); + +private: + Ui::AddressConverter *ui; + +private Q_SLOTS: + void addressInputChanged(); + +}; + +#endif // SMARTCASH_QT_ADDRESSCONVERTER_H diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index c5bbef53..a8395e9f 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -32,12 +32,11 @@ struct AddressTableEntry Type type; QString label; QString address; - QString addressNew; QString pubcoin; AddressTableEntry() {} - AddressTableEntry(Type type, const QString &label, const QString &address, const QString &addressNew): - type(type), label(label), address(address), addressNew(addressNew) {} + AddressTableEntry(Type type, const QString &label, const QString &address): + type(type), label(label), address(address) {} AddressTableEntry(Type type, const QString &pubcoin): type(type), pubcoin(pubcoin) {} }; @@ -97,8 +96,7 @@ class AddressTablePriv const std::string& strName = item.second.name; cachedAddressTable.append(AddressTableEntry(addressType, QString::fromStdString(strName), - QString::fromStdString(address.ToString()), - QString::fromStdString(address.ToString(true)) + QString::fromStdString(address.ToString()) )); } } @@ -108,7 +106,7 @@ class AddressTablePriv qSort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan()); } - void updateEntry(const QString &address,const QString &addressNew, const QString &label, bool isMine, const QString &purpose, int status) + void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status) { // Find address / label in model QList::iterator lower = qLowerBound( @@ -129,7 +127,7 @@ class AddressTablePriv break; } parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex); - cachedAddressTable.insert(lowerIndex, AddressTableEntry(newEntryType, label, address, addressNew)); + cachedAddressTable.insert(lowerIndex, AddressTableEntry(newEntryType, label, address)); parent->endInsertRows(); break; case CT_UPDATED: @@ -176,7 +174,7 @@ class AddressTablePriv AddressTableModel::AddressTableModel(CWallet *wallet, WalletModel *parent) : QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0) { - columns << tr("Label") << tr("Address Legacy") << tr("Address"); + columns << tr("Label") << tr("Address"); priv = new AddressTablePriv(wallet, this); priv->refreshAddressTable(); } @@ -219,8 +217,6 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const return rec->label; } case Address: - return rec->addressNew; - case AddressLegacy: return rec->address; } } @@ -348,14 +344,14 @@ QModelIndex AddressTableModel::index(int row, int column, const QModelIndex &par } } -void AddressTableModel::updateEntry(const QString &address, const QString &addressNew, +void AddressTableModel::updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status) { // Update address book model from Bitcoin core - priv->updateEntry(address, addressNew, label, isMine, purpose, status); + priv->updateEntry(address, label, isMine, purpose, status); } -QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address) +QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address, int64_t lockTime) { std::string strLabel = label.toStdString(); std::string strAddress = address.toStdString(); @@ -398,7 +394,21 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con return QString(); } } - strAddress = CBitcoinAddress(newKey.GetID()).ToString(type == ReceiveNew); + + CKeyID keyID = newKey.GetID(); + if(lockTime > 0 ) + { + CScript redeemScript = GetLockedScriptForDestination(keyID, lockTime); + strAddress = CBitcoinAddress(CScriptID(redeemScript)).ToString(type == ReceiveNew); + { + LOCK(wallet->cs_wallet); + wallet->AddCScript(redeemScript); + } + } + else + { + strAddress = CBitcoinAddress(keyID).ToString(type == ReceiveNew); + } } else { diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index baf6ddc6..4f35c3cc 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -26,8 +26,7 @@ class AddressTableModel : public QAbstractTableModel enum ColumnIndex { Label = 0, /**< User specified label */ - AddressLegacy = 1, /**< Bitcoin address */ - Address = 2 + Address = 1 /**< Bitcoin address */ }; enum RoleIndex { @@ -64,7 +63,7 @@ class AddressTableModel : public QAbstractTableModel /* Add an address to the model. Returns the added address on success, and an empty string otherwise. */ - QString addRow(const QString &type, const QString &label, const QString &address); + QString addRow(const QString &type, const QString &label, const QString &address, int64_t lockTime = 0); /* Look up label for address in address book, if not found return empty string. */ @@ -90,7 +89,7 @@ class AddressTableModel : public QAbstractTableModel public Q_SLOTS: /* Update address list from core. */ - void updateEntry(const QString &address, const QString &addressNew, const QString &label, bool isMine, const QString &purpose, int status); + void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status); friend class AddressTablePriv; }; diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 9f565a3c..26eaa0ba 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -4,6 +4,7 @@ #include "bitcoingui.h" +#include "addressconverter.h" #include "bitcoinunits.h" #include "clientmodel.h" #include "guiconstants.h" @@ -117,6 +118,7 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *n trayIconMenu(0), notificator(0), rpcConsole(0), + addressConverter(0), helpMessageDialog(0), modalOverlay(0), prevBlocks(0), @@ -154,6 +156,7 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *n #endif rpcConsole = new RPCConsole(platformStyle, 0); + addressConverter = new AddressConverter(0); helpMessageDialog = new HelpMessageDialog(this, HelpMessageDialog::cmdline); #ifdef ENABLE_WALLET if(enableWallet) @@ -273,6 +276,7 @@ BitcoinGUI::~BitcoinGUI() #endif delete rpcConsole; + delete addressConverter; } void BitcoinGUI::createActions() @@ -399,6 +403,9 @@ void BitcoinGUI::createActions() // initially disable the debug window menu item openRPCConsoleAction->setEnabled(false); + openAddressConverterAction = new QAction(tr("&Address converter"), this); + openAddressConverterAction->setStatusTip(tr("Open dialog to convert addresses from/to new address format")); + usedSendingAddressesAction = new QAction(platformStyle->TextColorIcon(":/icons/address-book"), tr("&Sending addresses..."), this); usedSendingAddressesAction->setStatusTip(tr("Show the list of used sending addresses and labels")); usedReceivingAddressesAction = new QAction(platformStyle->TextColorIcon(":/icons/address-book"), tr("&Receiving addresses..."), this); @@ -418,6 +425,7 @@ void BitcoinGUI::createActions() connect(toggleHideAction, SIGNAL(triggered()), this, SLOT(toggleHidden())); connect(showHelpMessageAction, SIGNAL(triggered()), this, SLOT(showHelpMessageClicked())); connect(openRPCConsoleAction, SIGNAL(triggered()), this, SLOT(showDebugWindow())); + connect(openAddressConverterAction, SIGNAL(triggered()), this, SLOT(showAddressConverter())); // Get restart command-line parameters and handle restart connect(rpcConsole, SIGNAL(handleRestart(QStringList)), this, SLOT(handleRestart(QStringList))); @@ -489,6 +497,7 @@ void BitcoinGUI::createMenuBar() { help->addAction(openRPCConsoleAction); } + help->addAction(openAddressConverterAction); help->addAction(showHelpMessageAction); help->addSeparator(); help->addAction(aboutAction); @@ -712,6 +721,14 @@ void BitcoinGUI::showDebugWindow() rpcConsole->activateWindow(); } +void BitcoinGUI::showAddressConverter() +{ + addressConverter->showNormal(); + addressConverter->show(); + addressConverter->raise(); + addressConverter->activateWindow(); +} + void BitcoinGUI::showPeers() { rpcConsole->setTabFocus(RPCConsole::TAB_PEERS); @@ -1083,6 +1100,7 @@ void BitcoinGUI::closeEvent(QCloseEvent *event) { // close rpcConsole in case it was open to make some space for the shutdown window rpcConsole->close(); + addressConverter->close(); QApplication::quit(); } @@ -1263,6 +1281,10 @@ void BitcoinGUI::detectShutdown() { if(rpcConsole) rpcConsole->hide(); + + if(addressConverter) + addressConverter->hide(); + qApp->quit(); } } diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 6f0b9703..5d78249d 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -18,6 +18,7 @@ #include #include +class AddressConverter; class ClientModel; class NetworkStyle; class Notificator; @@ -117,6 +118,7 @@ class BitcoinGUI : public QMainWindow QAction *smartrewardsAction; QAction *smartvotingAction; QAction *openRPCConsoleAction; + QAction *openAddressConverterAction; QAction *openAction; QAction *showHelpMessageAction; @@ -124,6 +126,7 @@ class BitcoinGUI : public QMainWindow QMenu *trayIconMenu; Notificator *notificator; RPCConsole *rpcConsole; + AddressConverter *addressConverter; HelpMessageDialog *helpMessageDialog; ModalOverlay *modalOverlay; @@ -234,6 +237,8 @@ private Q_SLOTS: void aboutClicked(); /** Show debug window */ void showDebugWindow(); + /** Show address converter dialog */ + void showAddressConverter(); /** Show debug window and set focus to the console */ void showDebugWindowActivateConsole(); /** Show help message dialog */ diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 719e9aa1..9435ab49 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -215,13 +216,14 @@ void CoinControlDialog::showMenu(const QPoint &point) if (item->text(COLUMN_TXHASH).length() == 64) // transaction hash is 64 characters (this means its a child node, so its not a parent node in tree mode) { copyTransactionHashAction->setEnabled(true); - if (model->isLockedCoin(uint256S(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt())) - { + + if( item->data(COLUMN_LOCKED, Qt::UserRole).toBool() ){ + lockAction->setEnabled(false); + unlockAction->setEnabled(false); + }else if (model->isLockedCoin(uint256S(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt())){ lockAction->setEnabled(false); unlockAction->setEnabled(true); - } - else - { + }else{ lockAction->setEnabled(true); unlockAction->setEnabled(false); } @@ -699,6 +701,9 @@ void CoinControlDialog::updateView() bool treeMode = ui->radioTreeMode->isChecked(); + int nCurrentHeight = chainActive.Height(); + int64_t nCurrentTime = chainActive.Tip() ? chainActive.Tip()->GetMedianTimePast() : GetTime(); + ui->treeWidget->clear(); ui->treeWidget->setEnabled(false); // performance, otherwise updateLabels would be called for every checked checkbox ui->treeWidget->setAlternatingRowColors(!treeMode); @@ -740,6 +745,7 @@ void CoinControlDialog::updateView() int nInputSum = 0; BOOST_FOREACH(const COutput& out, coins.second) { int nInputSize = 0; + int64_t nLockTime = -1; nSum += out.tx->vout[out.i].nValue; nChildren++; @@ -760,10 +766,29 @@ void CoinControlDialog::updateView() if (!treeMode || (!(sAddress == sWalletAddress))) itemOutput->setText(COLUMN_ADDRESS, sAddress); - CPubKey pubkey; - CKeyID *keyid = boost::get(&outputAddress); - if (keyid && model->getPubKey(*keyid, pubkey) && !pubkey.IsCompressed()) - nInputSize = 29; // 29 = 180 - 151 (public key is 180 bytes, priority free area is 151 bytes) + // Check if output is locked P2PKH, if so then get lock time + if(out.tx->vout[out.i].scriptPubKey.IsPayToScriptHash()) + { + const CScriptID& hash = boost::get(outputAddress); + CScript redeemScript; + if(pwalletMain->GetCScript(hash, redeemScript)) + { + if (redeemScript.IsPayToPublicKeyHashLocked()) + { + int nLockTimeLength = redeemScript[0]; + std::vector lockTimeVch(redeemScript.begin() + 1, + redeemScript.begin() + 1 + nLockTimeLength); + nLockTime = CScriptNum(lockTimeVch, false).getint(); + } + } + } + else + { + CPubKey pubkey; + CKeyID *keyid = boost::get(&outputAddress); + if (keyid && model->getPubKey(*keyid, pubkey) && !pubkey.IsCompressed()) + nInputSize = 29; // 29 = 180 - 151 (public key is 180 bytes, priority free area is 151 bytes) + } } // label @@ -807,17 +832,43 @@ void CoinControlDialog::updateView() // vout index itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(out.i)); + if( nLockTime <= 0 ){ + nLockTime = out.nLockTime; + } + + bool fOutputLocked = nLockTime && ( + ( nLockTime < LOCKTIME_THRESHOLD && nCurrentHeight < nLockTime ) || + ( nLockTime >= LOCKTIME_THRESHOLD && nCurrentTime < nLockTime ) ); + + itemOutput->setData(COLUMN_LOCKED, Qt::UserRole, QVariant(fOutputLocked)); + + COutPoint outpt(txhash, out.i); + + if( fOutputLocked ){ + + model->lockCoin(outpt); + + if( nLockTime < LOCKTIME_THRESHOLD ){ + itemOutput->setText(COLUMN_ADDRESS, QString("Output locked until block %1").arg(nLockTime)); + }else{ + QDateTime timestamp; + timestamp.setTime_t(nLockTime); + itemOutput->setText(COLUMN_ADDRESS, QString("Output locked until %1").arg(timestamp.toString(Qt::SystemLocaleShortDate))); + } + }else if( nLockTime ){ + model->unlockCoin(outpt); + } + // disable locked coins - if (model->isLockedCoin(txhash, out.i)) + if (model->isLockedCoin(outpt.hash, outpt.n) || fOutputLocked) { - COutPoint outpt(txhash, out.i); coinControl->UnSelect(outpt); // just to be sure itemOutput->setDisabled(true); itemOutput->setIcon(COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed")); } // set checkbox - if (coinControl->IsSelected(COutPoint(txhash, out.i))) + if (coinControl->IsSelected(outpt)) itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked); } diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h index 785079b1..3aa3d7b5 100644 --- a/src/qt/coincontroldialog.h +++ b/src/qt/coincontroldialog.h @@ -85,6 +85,7 @@ class CoinControlDialog : public QDialog COLUMN_PRIORITY, COLUMN_TXHASH, COLUMN_VOUT_INDEX, + COLUMN_LOCKED }; friend class CCoinControlWidgetItem; diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index 118fcbdf..3eca8569 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -7,6 +7,8 @@ #include "addresstablemodel.h" #include "guiutil.h" +#include "validation.h" +#include "chainparams.h" #include #include @@ -77,7 +79,8 @@ bool EditAddressDialog::saveCurrentRow() address = model->addRow( mode == NewSendingAddress ? AddressTableModel::Send : AddressTableModel::Receive, ui->labelEdit->text(), - ui->addressEdit->text()); + ui->addressEdit->text(), + ui->timeLockSettings->getLockTime()); break; case EditReceivingAddress: case EditSendingAddress: @@ -142,3 +145,4 @@ void EditAddressDialog::setAddress(const QString &address) this->address = address; ui->addressEdit->setText(address); } + diff --git a/src/qt/forms/addressbookpage.ui b/src/qt/forms/addressbookpage.ui index 9524538e..0c8d1db0 100644 --- a/src/qt/forms/addressbookpage.ui +++ b/src/qt/forms/addressbookpage.ui @@ -6,8 +6,8 @@ 0 0 - 1160 - 480 + 680 + 336 @@ -60,7 +60,7 @@ &New - + :/icons/add:/icons/add @@ -68,23 +68,6 @@ - - - - Copy the currently selected address (legacy) to the system clipboard - - - &Copy (Legacy) - - - - :/icons/editcopy:/icons/editcopy - - - false - - - @@ -94,7 +77,7 @@ &Copy - + :/icons/editcopy:/icons/editcopy @@ -111,7 +94,7 @@ &Delete - + :/icons/remove:/icons/remove @@ -141,7 +124,7 @@ &Export - + :/icons/export:/icons/export @@ -169,8 +152,6 @@ - - - + diff --git a/src/qt/forms/addressconverter.ui b/src/qt/forms/addressconverter.ui new file mode 100644 index 00000000..f877d32e --- /dev/null +++ b/src/qt/forms/addressconverter.ui @@ -0,0 +1,122 @@ + + + AddressConverter + + + + 0 + 0 + 431 + 153 + + + + Address Converter + + + + + + + 0 + 0 + + + + Helps to convert SmartCash addresses from/to the old/new address format. + + + true + + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + Qt::ClickFocus + + + QLineEdit{ +color: black; +border: 1px solid; +background-color: white; +border-color: rgb(118, 118, 119); +} + + + Qt::AlignCenter + + + Enter a SmartCash address + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + + 0 + 25 + + + + + 16777215 + 25 + + + + QLabel{ +color: black; +border: 1px solid; +background-color: white; +border-color: rgb(118, 118, 119); +} + + + + + + Qt::AlignCenter + + + Qt::TextSelectableByMouse + + + + + + + input + + + + diff --git a/src/qt/forms/editaddressdialog.ui b/src/qt/forms/editaddressdialog.ui index c1aea363..a85c1067 100644 --- a/src/qt/forms/editaddressdialog.ui +++ b/src/qt/forms/editaddressdialog.ui @@ -55,6 +55,9 @@ + + + diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index ca0ecf77..b9cfc156 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -20,7 +20,7 @@ - 0 + 1 @@ -163,6 +163,16 @@ + + + + If you disable the spending of unconfirmed change, the change from a transaction cannot be used until that transaction has at least one confirmation. This also affects how your balance is computed. + + + New address format + + + diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui index fd289408..f305d6e7 100644 --- a/src/qt/forms/receivecoinsdialog.ui +++ b/src/qt/forms/receivecoinsdialog.ui @@ -169,6 +169,9 @@ + + + diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 7b958931..289a2e31 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -324,7 +324,7 @@ Qt::ActionsContextMenu - 0.00 BTC + 0.00 SMART Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse @@ -685,29 +685,6 @@ - - - - - 0 - 0 - - - - Clear all fields of the form. - - - Clear &All - - - - :/icons/remove:/icons/remove - - - false - - - @@ -725,6 +702,9 @@ + + + @@ -738,6 +718,7 @@ + Set Delay Qt::Horizontal diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index 703bcace..c0f37c57 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -163,7 +163,7 @@ - + @@ -177,6 +177,13 @@ + + + + U&se Available Balance + + + diff --git a/src/qt/forms/smartrewardentry.ui b/src/qt/forms/smartrewardentry.ui index 82ad4979..57d2a9f1 100755 --- a/src/qt/forms/smartrewardentry.ui +++ b/src/qt/forms/smartrewardentry.ui @@ -19,20 +19,6 @@ Form - - #QSmartRewardEntry{ - - background-color: rgb(247, 245, 248); -} - -#eligiblePage{ - background-color: rgb(247, 245, 248); -} - -#infoPage{ - background-color: rgb(247, 245, 248); -} - @@ -127,6 +113,19 @@ + + + + + 75 + true + + + + color: rgb(60, 179, 113); + + + @@ -204,8 +203,11 @@ true + + color: rgb(173, 47, 19); + - Voting required. Go to the SmartVoting Tab and vote for a proposal + ActivateRewards required. Click button below to activate this address for rewards true diff --git a/src/qt/forms/smartrewardslist.ui b/src/qt/forms/smartrewardslist.ui index cadbe6c7..d6e63183 100644 --- a/src/qt/forms/smartrewardslist.ui +++ b/src/qt/forms/smartrewardslist.ui @@ -32,7 +32,7 @@ - 2 + 0 @@ -59,7 +59,7 @@ - Initialize SmartRewards... + Initializing SmartRewards... Qt::AlignCenter @@ -142,142 +142,6 @@ - - - - - - Qt::Vertical - - - - 20 - 142 - - - - - - - - - 29 - 75 - true - - - - - - - Creating SmartRewards database... - - - Qt::AlignCenter - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 30 - - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 80 - 20 - - - - - - - - - 0 - 0 - - - - QProgressBar { - border: 2px solid black; - border-radius: 5px; - text-align: center; - font-weight: bold; -} - -QProgressBar::chunk { - background-color: #FEC60D; -} - - - 100000 - - - 21 - - - Qt::AlignCenter - - - false - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 80 - 20 - - - - - - - - - - Qt::Vertical - - - - 20 - 142 - - - - - - @@ -325,7 +189,7 @@ QProgressBar::chunk { - SmartRewards round + SmartRewards Round @@ -405,7 +269,7 @@ QProgressBar::chunk { - Current reward + Current Reward Annualized @@ -609,7 +473,7 @@ QProgressBar::chunk { - Send VoteProofs [0] + Activate Rewards [0] diff --git a/src/qt/forms/specialtransactiondialog.ui b/src/qt/forms/specialtransactiondialog.ui index ab47896b..566ce924 100644 --- a/src/qt/forms/specialtransactiondialog.ui +++ b/src/qt/forms/specialtransactiondialog.ui @@ -363,6 +363,9 @@ + + + diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 8c8d1ab7..d2c83329 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -37,6 +37,10 @@ static const bool DEFAULT_SPLASHSCREEN = true; #define COLOR_TX_STATUS_DANGER QColor(200, 100, 100) /* Transaction list -- TX status decoration - default color */ #define COLOR_BLACK QColor(0, 0, 0) +/* SmartRewards Tab -- Bonus text and activated addresses */ +#define COLOR_GREEN QColor(60, 179, 113) +/* SmartRewards Tab -- SmartNode address */ +#define COLOR_YELLOW QColor(255, 199, 15) /* Tooltips longer than this (in characters) are converted into rich text, so that they can be word-wrapped. diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index 454d602f..f24e4c4f 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -4427,7 +4427,7 @@ p, li { white-space: pre-wrap; } - Initialize SmartRewards... + Initializing SmartRewards... diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 20b53725..5b783806 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -164,6 +164,7 @@ void OptionsDialog::setModel(OptionsModel *model) connect(ui->threadsScriptVerif, SIGNAL(valueChanged(int)), this, SLOT(showRestartWarning())); /* Wallet */ connect(ui->spendZeroConfChange, SIGNAL(clicked(bool)), this, SLOT(showRestartWarning())); + connect(ui->newAddressFormatChange, SIGNAL(clicked(bool)), this, SLOT(showRestartWarning())); /* Network */ connect(ui->allowIncoming, SIGNAL(clicked(bool)), this, SLOT(showRestartWarning())); connect(ui->connectSocks, SIGNAL(clicked(bool)), this, SLOT(showRestartWarning())); @@ -183,6 +184,7 @@ void OptionsDialog::setMapper() /* Wallet */ mapper->addMapping(ui->spendZeroConfChange, OptionsModel::SpendZeroConfChange); mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures); + mapper->addMapping(ui->newAddressFormatChange, OptionsModel::UseNewAddressFormat); /* Network */ mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 9ac36870..3c7be7c0 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -108,6 +108,11 @@ void OptionsModel::Init(bool resetSettings) settings.setValue("bSpendZeroConfChange", true); if (!SoftSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool())) addOverriddenOption("-spendzeroconfchange"); + + if (!settings.contains("bUseNewAddressFormat")) + settings.setValue("bUseNewAddressFormat", false); + if (!SoftSetBoolArg("-usenewaddressformat", settings.value("bUseNewAddressFormat").toBool())) + addOverriddenOption("-usenewaddressformat"); #endif // Network @@ -231,6 +236,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const #ifdef ENABLE_WALLET case SpendZeroConfChange: return settings.value("bSpendZeroConfChange"); + case UseNewAddressFormat: + return settings.value("bUseNewAddressFormat"); #endif case DisplayUnit: return nDisplayUnit; @@ -353,7 +360,13 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in settings.setValue("bSpendZeroConfChange", value); setRestartRequired(true); } - break; + break; + case UseNewAddressFormat: + if (settings.value("bUseNewAddressFormat") != value) { + settings.setValue("bUseNewAddressFormat", value); + setRestartRequired(true); + } + break; #endif case DisplayUnit: setDisplayUnit(value); @@ -463,4 +476,4 @@ void OptionsModel::checkAndMigrate() settings.setValue(strSettingsVersionKey, CLIENT_VERSION); } -} \ No newline at end of file +} diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index b23b5f26..4629fcfd 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -46,6 +46,7 @@ class OptionsModel : public QAbstractListModel DatabaseCache, // int SpendZeroConfChange, // bool Listen, // bool + UseNewAddressFormat, // bool OptionIDRowCount, }; diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index 72615f44..4c9fa697 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -14,6 +14,7 @@ #include "receiverequestdialog.h" #include "recentrequeststablemodel.h" #include "walletmodel.h" +#include "validation.h" #include #include @@ -155,7 +156,7 @@ void ReceiveCoinsDialog::on_receiveButton_clicked() }else{ receive = AddressTableModel::Receive; } - address = model->getAddressTableModel()->addRow(receive, label, ""); + address = model->getAddressTableModel()->addRow(receive, label, "", ui->timeLockSettings->getLockTime()); } SendCoinsRecipient info(address, label, ui->reqAmount->value(), ui->reqMessage->text()); @@ -278,3 +279,4 @@ void ReceiveCoinsDialog::copyAmount() { copyColumnToClipboard(RecentRequestsTableModel::Amount); } + diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 49b748b5..65b2f25c 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -15,6 +15,7 @@ #include "sendcoinsentry.h" #include "walletmodel.h" +#include "validation.h" #include "base58.h" #include "coincontrol.h" #include "validation.h" // mempool and minRelayTxFee @@ -22,6 +23,7 @@ #include "util.h" #include "txmempool.h" #include "wallet/wallet.h" +#include "chainparams.h" #include #include @@ -29,7 +31,8 @@ #include #include -#define SEND_CONFIRM_DELAY 3 +#define SEND_CONFIRM_DELAY 3 +#define SEND_CONFIRM_DELAY_LOCKTIME 10 SendCoinsDialog::SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *parent) : QDialog(parent), @@ -44,11 +47,9 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *pa if (!platformStyle->getImagesOnButtons()) { ui->addButton->setIcon(QIcon()); - ui->clearButton->setIcon(QIcon()); ui->sendButton->setIcon(QIcon()); } else { ui->addButton->setIcon(platformStyle->SingleColorIcon(":/icons/add")); - ui->clearButton->setIcon(platformStyle->SingleColorIcon(":/icons/remove")); ui->sendButton->setIcon(platformStyle->SingleColorIcon(":/icons/send")); } @@ -57,7 +58,6 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *pa addEntry(); connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry())); - connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear())); // Coin Control connect(ui->pushButtonCoinControl, SIGNAL(clicked()), this, SLOT(coinControlButtonClicked())); @@ -213,6 +213,7 @@ void SendCoinsDialog::on_sendButton_clicked() QList recipients; bool valid = true; + int64_t nLockTime = ui->timeLockSettings->getLockTime(); for(int i = 0; i < ui->entries->count(); ++i) { @@ -222,6 +223,7 @@ void SendCoinsDialog::on_sendButton_clicked() if(entry->validate()) { recipients.append(entry->getValue()); + recipients.last().nLockTime = nLockTime; } else { @@ -306,7 +308,30 @@ void SendCoinsDialog::on_sendButton_clicked() formatted.append(recipientElement); } - QString questionString = tr("Are you sure you want to send?"); + QString questionString; + + if (nLockTime > 0) { + // Figure out unlocking date and time + QDateTime unlockDateTime; + if (nLockTime < LOCKTIME_THRESHOLD) + { + const int nAvgBlockTime = Params().GetConsensus().nPowTargetSpacing; + unlockDateTime = QDateTime::currentDateTime().addSecs(nLockTime * nAvgBlockTime); + } + else + { + unlockDateTime.setMSecsSinceEpoch(nLockTime * 1000); + } + + questionString.append(""); + questionString.append(tr("This is not a normal transaction. ")); + questionString.append(tr("Do not use this to deposit funds to an exchange. ")); + questionString.append(tr("Funds sent will not be spendable until approximatively ")); + questionString.append(unlockDateTime.toString("MMMM d yy hh:mm:ss")); + questionString.append("

"); + } + + questionString.append(tr("Are you sure you want to send?")); questionString.append("

%1"); if(txFee > 0) @@ -336,7 +361,10 @@ void SendCoinsDialog::on_sendButton_clicked() .arg(alternativeUnits.join(" " + tr("or") + "
"))); SendConfirmationDialog confirmationDialog(tr("Confirm send coins"), - questionString.arg(formatted.join("
")), SEND_CONFIRM_DELAY, this); + questionString.arg(formatted.join("
")), + nLockTime > 0 ? SEND_CONFIRM_DELAY_LOCKTIME : SEND_CONFIRM_DELAY, + nLockTime > 0 ? QMessageBox::Warning : QMessageBox::Question, + this); confirmationDialog.exec(); QMessageBox::StandardButton retval = (QMessageBox::StandardButton)confirmationDialog.result(); @@ -356,6 +384,7 @@ void SendCoinsDialog::on_sendButton_clicked() accept(); CoinControlDialog::coinControl->UnSelectAll(); coinControlUpdateLabels(); + ui->timeLockSettings->reset(); } fNewRecipientAllowed = true; } @@ -434,9 +463,9 @@ QWidget *SendCoinsDialog::setupTabChain(QWidget *prev) } } QWidget::setTabOrder(prev, ui->sendButton); - QWidget::setTabOrder(ui->sendButton, ui->clearButton); - QWidget::setTabOrder(ui->clearButton, ui->addButton); - return ui->addButton; + QWidget::setTabOrder(ui->sendButton, ui->addButton); + QWidget::setTabOrder(ui->addButton, ui->timeLockSettings); + return ui->timeLockSettings; } void SendCoinsDialog::setAddress(const QString &address) @@ -521,7 +550,23 @@ void SendCoinsDialog::updateInstantSend() CoinControlDialog::coinControl->fUseInstantSend = ui->checkUseInstantSend->isChecked(); coinControlUpdateLabels(); } - +/* +void SendCoinsDialog::updatetimelock() + timelockWidget = new QComboBox(this); + if (platformStyle->getUseExtraSpacing()) { + dateWidget->setFixedWidth(21); + } else { + dateWidget->setFixedWidth(20); + } + dateWidget->addItem(tr("Set Delay"), None); + dateWidget->addItem(tr("Delay 1 Week"), 1Week); + dateWidget->addItem(tr("Delay 1 Month"), 1Month); + dateWidget->addItem(tr("Delay 3 Months"), 3Months); + dateWidget->addItem(tr("Delay 6 Months"), 6Months); + dateWidget->addItem(tr("Delay 1 year"), 1Year); + dateWidget->addItem(tr("Custom..."), Custom); +// hlayout->addWidget(timelockWidget); +*/ void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg) { QPair msgParams; @@ -865,8 +910,8 @@ void SendCoinsDialog::coinControlUpdateLabels() } SendConfirmationDialog::SendConfirmationDialog(const QString &title, const QString &text, int secDelay, - QWidget *parent) : - QMessageBox(QMessageBox::Question, title, text, QMessageBox::Yes | QMessageBox::Cancel, parent), secDelay(secDelay) + QMessageBox::Icon icon, QWidget *parent) : + QMessageBox(icon, title, text, QMessageBox::Yes | QMessageBox::Cancel, parent), secDelay(secDelay) { setDefaultButton(QMessageBox::Cancel); yesButton = button(QMessageBox::Yes); diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index e577d9cc..4f1cb2cd 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -110,7 +110,8 @@ class SendConfirmationDialog : public QMessageBox Q_OBJECT public: - SendConfirmationDialog(const QString &title, const QString &text, int secDelay = 0, QWidget *parent = 0); + SendConfirmationDialog(const QString &title, const QString &text, int secDelay = 0, + QMessageBox::Icon icon = QMessageBox::Question, QWidget *parent = 0); int exec(); private Q_SLOTS: diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index d063f2c8..56a08c35 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -42,12 +42,15 @@ SendCoinsEntry::SendCoinsEntry(const PlatformStyle *platformStyle, QWidget *pare // just a label for displaying bitcoin address(es) ui->payTo_is->setFont(GUIUtil::fixedPitchFont()); + ui->horizontalLayoutAmount->insertStretch(-1, 3); + // Connect signals connect(ui->payAmount, SIGNAL(valueChanged()), this, SIGNAL(payAmountChanged())); connect(ui->checkboxSubtractFeeFromAmount, SIGNAL(toggled(bool)), this, SIGNAL(subtractFeeFromAmountChanged())); connect(ui->deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked())); connect(ui->deleteButton_is, SIGNAL(clicked()), this, SLOT(deleteClicked())); connect(ui->deleteButton_s, SIGNAL(clicked()), this, SLOT(deleteClicked())); + connect(ui->useAvailableBalanceButton, SIGNAL(clicked()), this, SLOT(useAvailableBalanceButtonClicked())); } SendCoinsEntry::~SendCoinsEntry() @@ -264,3 +267,15 @@ bool SendCoinsEntry::updateLabel(const QString &address) return false; } + +void SendCoinsEntry::useAvailableBalanceButtonClicked() +{ + CAmount amount = model->getBalance(); + if (amount > 0) { + ui->payAmount->setValue(amount); + ui->checkboxSubtractFeeFromAmount->setCheckState(Qt::Checked); + } else { + ui->payAmount->setValue(0); + } +} + diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index a8be670c..5642b959 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -60,6 +60,7 @@ private Q_SLOTS: void on_addressBookButton_clicked(); void on_pasteButton_clicked(); void updateDisplayUnit(); + void useAvailableBalanceButtonClicked(); private: SendCoinsRecipient recipient; diff --git a/src/qt/smartproposaltab.cpp b/src/qt/smartproposaltab.cpp index b4583229..c6b47421 100644 --- a/src/qt/smartproposaltab.cpp +++ b/src/qt/smartproposaltab.cpp @@ -350,7 +350,7 @@ void SmartProposalTabWidget::publish() questionString.append("

Proposal fee: %1 SMART"); SendConfirmationDialog confirmationDialog(tr("Confirm send proposal fee"), - questionString.arg(CAmountToDouble(SMARTVOTING_PROPOSAL_FEE)), 3, this); + questionString.arg(CAmountToDouble(SMARTVOTING_PROPOSAL_FEE)), 3, QMessageBox::Question, this); confirmationDialog.exec(); QMessageBox::StandardButton retval = (QMessageBox::StandardButton)confirmationDialog.result(); diff --git a/src/qt/smartrewardentry.cpp b/src/qt/smartrewardentry.cpp index 88214399..8c277a27 100755 --- a/src/qt/smartrewardentry.cpp +++ b/src/qt/smartrewardentry.cpp @@ -20,6 +20,7 @@ QSmartRewardEntry::QSmartRewardEntry(const QString& strLabel, const QString& str ui->lblLabel->setText(strLabel); ui->lblAddress->setText(strAddress); + ui->lblBonus->setVisible(false); QAction *copyAddressAction = new QAction(tr("Copy address"), this); QAction *copyLabelAction = new QAction(tr("Copy label"), this); @@ -48,6 +49,11 @@ QSmartRewardEntry::~QSmartRewardEntry() delete ui; } +void QSmartRewardEntry::setMinBalance(CAmount nMinBalance) +{ + this->nMinBalance = nMinBalance; +} + void QSmartRewardEntry::setDisqualifyingTx(const uint256& txHash){ if( disqualifyingTx.IsNull() ){ @@ -82,9 +88,9 @@ void QSmartRewardEntry::setEligible(CAmount nEligible, CAmount nEstimated) ui->lblEstimated->setText(strEstimated + " SMART"); } -void QSmartRewardEntry::setVoted(bool fState) +void QSmartRewardEntry::setActivated(bool fState) { - fVoted = fState; + fActivated = fState; } void QSmartRewardEntry::setIsSmartNode(bool fState) @@ -92,9 +98,26 @@ void QSmartRewardEntry::setIsSmartNode(bool fState) fIsSmartNode = fState; } -void QSmartRewardEntry::setVoteProofConfirmations(int nConfirmations) -{ - nVoteProofConfirmations = nConfirmations; +void QSmartRewardEntry::setBonusText(uint8_t bonusLevel) +{ + switch (bonusLevel) { + case CSmartRewardEntry::TwoMonthsBonus: + ui->lblBonus->setText("2 months 2x bonus"); + ui->lblBonus->setVisible(true); + break; + case CSmartRewardEntry::FourMonthsBonus: + ui->lblBonus->setText("4 months 4x bonus"); + ui->lblBonus->setVisible(true); + break; + case CSmartRewardEntry::SixMonthsBonus: + ui->lblBonus->setText("6 months 6x bonus"); + ui->lblBonus->setVisible(true); + break; + default: + ui->lblBonus->setText(""); + ui->lblBonus->setVisible(false); + break; + } } QString QSmartRewardEntry::Address() const @@ -104,18 +127,12 @@ QString QSmartRewardEntry::Address() const QSmartRewardEntry::State QSmartRewardEntry::CurrentState() { - if( nBalanceAtStart < SMART_REWARDS_MIN_BALANCE ) return LowBalance; + if( nBalanceAtStart < nMinBalance ) return LowBalance; if( fIsSmartNode ) return IsASmartNode; if( !disqualifyingTx.IsNull() ) return OutgoingTransaction; - if( !fVoted ) return VotingRequired; - - if( nVoteProofConfirmations == -1 ) return VoteProofRequired; - - if( nVoteProofConfirmations < Params().GetConsensus().nRewardsConfirmationsRequired ) return VoteProofConfirmationsRequired; - if( nEligible ) return IsEligible; return Unknown; diff --git a/src/qt/smartrewardentry.h b/src/qt/smartrewardentry.h index 2b1fd755..23454a22 100755 --- a/src/qt/smartrewardentry.h +++ b/src/qt/smartrewardentry.h @@ -25,27 +25,24 @@ class QSmartRewardEntry : public QFrame LowBalance, IsASmartNode, OutgoingTransaction, - VotingRequired, - VoteProofRequired, - VoteProofConfirmationsRequired, IsEligible }; + void setMinBalance(CAmount nMinBalance); void setDisqualifyingTx(const uint256& txHash); void setBalance(CAmount nBalance); void setInfoText(const QString& strText, const QColor& color); void setEligible(CAmount nEligible, CAmount nEstimated); void setIsSmartNode(bool fState); - void setVoted(bool fState); - void setVoteProofConfirmations(int nConfirmations); + void setActivated(bool fState); + void setBonusText(uint8_t bonusLevel); QString Address() const; CAmount Balance() const { return nBalance; } CAmount BalanceAtStart() const { return nBalanceAtStart; } CAmount Eligible() const { return nEligible; } bool IsSmartNode() const { return fIsSmartNode; } - bool Voted() const { return fVoted; } - int VoteProofConfirmations() const { return nVoteProofConfirmations; } + bool Activated() const { return fActivated; } State CurrentState(); std::string ToString(); @@ -58,12 +55,12 @@ class QSmartRewardEntry : public QFrame QMenu* contextMenu; + CAmount nMinBalance; CAmount nBalanceAtStart; CAmount nBalance; CAmount nEligible; bool fIsSmartNode; - bool fVoted; - int nVoteProofConfirmations; + bool fActivated; uint256 disqualifyingTx; private Q_SLOTS: diff --git a/src/qt/smartrewardslist.cpp b/src/qt/smartrewardslist.cpp index 3304b72b..35ad5a83 100644 --- a/src/qt/smartrewardslist.cpp +++ b/src/qt/smartrewardslist.cpp @@ -46,13 +46,14 @@ struct QSmartRewardField CAmount reward; uint256 disqualifyingTx; bool fIsSmartNode; - bool fVoted; - int nVoteProofConfirmations; + bool fActivated; + uint8_t bonusLevel; QSmartRewardField() : label(QString()), address(QString()), balance(0), eligible(0),reward(0), disqualifyingTx(), - fIsSmartNode(false), fVoted(false), nVoteProofConfirmations(-1){} + fIsSmartNode(false), fActivated(false), + bonusLevel(CSmartRewardEntry::NoBonus) {} }; struct SortSmartRewardWidgets @@ -128,26 +129,14 @@ void SmartrewardsList::setClientModel(ClientModel *model) void SmartrewardsList::updateOverviewUI(const CSmartRewardRound ¤tRound, const CBlockIndex *tip) { - static int64_t lastUpdate = 0; - int nFirst_1_3_Round = Params().GetConsensus().nRewardsFirst_1_3_Round; - int64_t currentTime = QDateTime::currentMSecsSinceEpoch() / 1000; - - if( !lastUpdate || currentTime - lastUpdate > 5 ){ - lastUpdate = currentTime; - }else{ - return; - } - - if( currentRound.number < nFirst_1_3_Round ){ + if( !currentRound.Is_1_3() ){ ui->btnSendProofs->hide(); }else{ ui->btnSendProofs->show(); } - ui->spinnerWidget->stop(); - QString percentText; - percentText.sprintf("%.2f%%", currentRound.percent * 100); + percentText.sprintf("%.2f%%", currentRound.percent * 100 * 52); ui->percentLabel->setText(percentText); ui->roundLabel->setText(QString::number(currentRound.number)); @@ -176,17 +165,17 @@ void SmartrewardsList::updateOverviewUI(const CSmartRewardRound ¤tRound, c uint64_t minutes = (minutesLeft % 1440) % 60; if( days ){ - roundEndText += QString("%1day%2").arg(days).arg(days > 1 ? "s":""); + roundEndText += QString("%1 day%2").arg(days).arg(days > 1 ? "s":""); } if( hours ){ if( days ) roundEndText += ", "; - roundEndText += QString("%1hour%2").arg(hours).arg(hours > 1 ? "s":""); + roundEndText += QString("%1 hour%2").arg(hours).arg(hours > 1 ? "s":""); } if( !days && minutes ){ if( hours ) roundEndText += ", "; - roundEndText += QString("%1minute%2").arg(minutes).arg(minutes > 1 ? "s":""); + roundEndText += QString("%1 minute%2").arg(minutes).arg(minutes > 1 ? "s":""); } roundEndText += " )"; @@ -194,6 +183,8 @@ void SmartrewardsList::updateOverviewUI(const CSmartRewardRound ¤tRound, c }else{ + int64_t currentTime = QDateTime::currentMSecsSinceEpoch() / 1000; + roundEndText = roundEnd.toString(Qt::SystemLocaleShortDate); if( currentRound.endBlockTime < currentTime ) { @@ -225,7 +216,6 @@ void SmartrewardsList::updateOverviewUI(const CSmartRewardRound ¤tRound, c ui->nextRoundLabel->setText(roundEndText); - CKeyID keyId; int nAvailableForProof = 0; std::map > mapCoins; model->listCoins(mapCoins); @@ -257,65 +247,29 @@ void SmartrewardsList::updateOverviewUI(const CSmartRewardRound ¤tRound, c if (!(sAddress == sWalletAddress)){ // change address QSmartRewardField change; - CSmartRewardEntry reward; + CSmartRewardEntry *reward = nullptr; change.address = sAddress; change.label = tr("(change)"); change.balance = out.tx->vout[out.i].nValue; - if( prewards->GetRewardEntry(CSmartAddress(sAddress.toStdString()),reward) ){ - change.balance = reward.balance; - change.fIsSmartNode = !reward.smartnodePaymentTx.IsNull(); - change.balanceAtStart = reward.balanceAtStart; - change.disqualifyingTx = reward.disqualifyingTx; + if( prewards->GetRewardEntry(CSmartAddress::Legacy(sAddress.toStdString()), reward, false) ){ + change.balance = reward->balance; + change.fIsSmartNode = !reward->smartnodePaymentTx.IsNull(); + change.balanceAtStart = reward->balanceAtStart; + change.disqualifyingTx = reward->disqualifyingTx; + change.fActivated = reward->fActivated; - if( currentRound.number < nFirst_1_3_Round ){ - change.eligible = reward.balanceEligible && reward.disqualifyingTx.IsNull() ? reward.balanceEligible : 0; + if( !currentRound.Is_1_3() ){ + change.eligible = reward->balanceEligible && reward->disqualifyingTx.IsNull() ? reward->balanceEligible : 0; }else{ - change.eligible = reward.IsEligible() ? reward.balanceEligible : 0; + change.eligible = reward->IsEligible() ? reward->balanceEligible : 0; } change.reward = currentRound.percent * change.eligible; - if( reward.id.GetKeyID(keyId) ){ - - LOCK2(cs_main, pwalletMain->cs_wallet); - - change.fVoted = pwalletMain->mapVoted[keyId].find(currentRound.number) != pwalletMain->mapVoted[keyId].end(); - - if( pwalletMain->mapVoteProofs[keyId].find(currentRound.number) != pwalletMain->mapVoteProofs[keyId].end() ){ - - uint256 proofHash = pwalletMain->mapVoteProofs[keyId][currentRound.number]; - - CTransaction tx; - uint256 nBlockHash; - - if( !reward.voteProof.IsNull() ){ - change.nVoteProofConfirmations = Params().GetConsensus().nRewardsConfirmationsRequired; - }else if(!GetTransaction(proofHash, tx, Params().GetConsensus(), nBlockHash, true)){ - change.nVoteProofConfirmations = -1; - }else if(nBlockHash == uint256()) { - change.nVoteProofConfirmations = 0; - }else{ - - if (nBlockHash != uint256()) { - BlockMap::iterator mi = mapBlockIndex.find(nBlockHash); - if (mi != mapBlockIndex.end() && (*mi).second) { - CBlockIndex* pindex = (*mi).second; - if (chainActive.Contains(pindex)) { - change.nVoteProofConfirmations = chainActive.Height() - pindex->nHeight + 1; - } - } - } - } - } - - if( pwalletMain->mapVoted[keyId].find(currentRound.number) != pwalletMain->mapVoted[keyId].end() && - pwalletMain->mapVoteProofs[keyId].find(currentRound.number) == pwalletMain->mapVoteProofs[keyId].end() && - reward.balanceEligible && reward.disqualifyingTx.IsNull() && reward.smartnodePaymentTx.IsNull() ){ - ++nAvailableForProof; - } - + if( currentRound.Is_1_3() && !change.fActivated ){ + ++nAvailableForProof; } } @@ -337,61 +291,26 @@ void SmartrewardsList::updateOverviewUI(const CSmartRewardRound ¤tRound, c if( !rewardField.address.isEmpty() ){ - CSmartRewardEntry reward; + CSmartRewardEntry *reward = nullptr; - if( prewards->GetRewardEntry(CSmartAddress(rewardField.address.toStdString()),reward) ){ - rewardField.balance = reward.balance; - rewardField.fIsSmartNode = !reward.smartnodePaymentTx.IsNull(); - rewardField.balanceAtStart = reward.balanceAtStart; - rewardField.disqualifyingTx = reward.disqualifyingTx; + if( prewards->GetRewardEntry(CSmartAddress::Legacy(rewardField.address.toStdString()), reward, false) ){ + rewardField.balance = reward->balance; + rewardField.fIsSmartNode = !reward->smartnodePaymentTx.IsNull(); + rewardField.balanceAtStart = reward->balanceAtStart; + rewardField.disqualifyingTx = reward->disqualifyingTx; + rewardField.fActivated = reward->fActivated; + rewardField.bonusLevel = reward->bonusLevel; - if( currentRound.number < nFirst_1_3_Round ){ - rewardField.eligible = reward.balanceEligible && reward.disqualifyingTx.IsNull() ? reward.balanceEligible : 0; + if( !currentRound.Is_1_3() ){ + rewardField.eligible = reward->balanceEligible && reward->disqualifyingTx.IsNull() ? reward->balanceEligible : 0; }else{ - rewardField.eligible = reward.IsEligible() ? reward.balanceEligible : 0; + rewardField.eligible = reward->IsEligible() ? reward->balanceEligible : 0; } rewardField.reward = currentRound.percent * rewardField.eligible; - if( reward.id.GetKeyID(keyId) ){ - - LOCK2(cs_main, pwalletMain->cs_wallet); - - rewardField.fVoted = pwalletMain->mapVoted[keyId].find(currentRound.number) != pwalletMain->mapVoted[keyId].end(); - - if( pwalletMain->mapVoteProofs[keyId].find(currentRound.number) != pwalletMain->mapVoteProofs[keyId].end() ){ - - uint256 proofHash = pwalletMain->mapVoteProofs[keyId][currentRound.number]; - - CTransaction tx; - uint256 nBlockHash; - - if( !reward.voteProof.IsNull() ){ - rewardField.nVoteProofConfirmations = Params().GetConsensus().nRewardsConfirmationsRequired; - }else if(!GetTransaction(proofHash, tx, Params().GetConsensus(), nBlockHash, true)){ - rewardField.nVoteProofConfirmations = -1; - }else if(nBlockHash == uint256()) { - rewardField.nVoteProofConfirmations = 0; - }else{ - - if (nBlockHash != uint256()) { - BlockMap::iterator mi = mapBlockIndex.find(nBlockHash); - if (mi != mapBlockIndex.end() && (*mi).second) { - CBlockIndex* pindex = (*mi).second; - if (chainActive.Contains(pindex)) { - rewardField.nVoteProofConfirmations = chainActive.Height() - pindex->nHeight + 1; - } - } - } - } - } - - if( pwalletMain->mapVoted[keyId].find(currentRound.number) != pwalletMain->mapVoted[keyId].end() && - pwalletMain->mapVoteProofs[keyId].find(currentRound.number) == pwalletMain->mapVoteProofs[keyId].end() && - reward.balanceEligible && reward.disqualifyingTx.IsNull() && reward.smartnodePaymentTx.IsNull() ){ - ++nAvailableForProof; - } - + if( currentRound.Is_1_3() && !rewardField.fActivated ){ + ++nAvailableForProof; } } @@ -402,6 +321,24 @@ void SmartrewardsList::updateOverviewUI(const CSmartRewardRound ¤tRound, c int nEligibleAddresses = 0; CAmount rewardSum = 0; + auto entry = vecEntries.begin(); + + while( entry != vecEntries.end() ){ + + auto it = std::find_if(rewardList.begin(), + rewardList.end(), + [entry](QSmartRewardField& field) -> bool { + return (*entry)->Address() == field.address; + }); + + if( it == rewardList.end() ) { + delete *entry; + entry = vecEntries.erase(entry); + }else{ + ++entry; + } + } + BOOST_FOREACH(const QSmartRewardField& field, rewardList) { QSmartRewardEntry* entry; @@ -421,42 +358,39 @@ void SmartrewardsList::updateOverviewUI(const CSmartRewardRound ¤tRound, c entry->setBalance(field.balance); entry->setIsSmartNode(field.fIsSmartNode); - entry->setVoted(field.fVoted); - entry->setVoteProofConfirmations(field.nVoteProofConfirmations); + entry->setActivated(field.fActivated); - if( currentRound.number >= nFirst_1_3_Round ){ + if( currentRound.Is_1_3() ){ - int nConfirmationsRequired = Params().GetConsensus().nRewardsConfirmationsRequired - field.nVoteProofConfirmations; + entry->setMinBalance(SMART_REWARDS_MIN_BALANCE_1_3); + entry->setBonusText(field.bonusLevel); if( field.fIsSmartNode ){ entry->setInfoText("Address belongs to a SmartNode.", COLOR_NEGATIVE); - }else if( field.balanceAtStart < SMART_REWARDS_MIN_BALANCE ){ - entry->setInfoText(QString("Address only held %1 SMART at the round's startblock. Minimum required: %2 SMART").arg(BitcoinUnits::format(BitcoinUnit::SMART, field.balanceAtStart)).arg(SMART_REWARDS_MIN_BALANCE/COIN), COLOR_NEGATIVE); + }else if( field.balanceAtStart < SMART_REWARDS_MIN_BALANCE_1_3 ){ + entry->setInfoText(QString("Qualified balance is only %1 SMART at the round's startblock. Minimum required: %2 SMART. It can be activated now but it will not receive rewards until it has enough funds.").arg(BitcoinUnits::format(BitcoinUnit::SMART, field.balanceAtStart)).arg(SMART_REWARDS_MIN_BALANCE_1_3/COIN), COLOR_NEGATIVE); }else if( !field.disqualifyingTx.IsNull() ){ entry->setDisqualifyingTx(field.disqualifyingTx); - entry->setInfoText(QString("Address disqualified due to an outgoing transaction with the hash %1").arg(QString::fromStdString(field.disqualifyingTx.ToString())), COLOR_NEGATIVE); - }else if( !field.fVoted ){ - entry->setInfoText("Voting required. Go to the \"SmartVote\" tab and vote for a proposal with this address.", COLOR_NEGATIVE); - }else if( field.fVoted && field.nVoteProofConfirmations == -1){ - entry->setInfoText("VoteProof required. Click the button in the bottom bar to send the VoteProof for this address.", COLOR_WARNING); - }else if( field.fVoted && - nConfirmationsRequired > 0){ - entry->setInfoText(QString("%1 more block confirmations required for the VoteProof transaction to become processed.").arg(nConfirmationsRequired), COLOR_WARNING); - }else if( field.fVoted && - nConfirmationsRequired <= 0 && - field.eligible ){ + entry->setInfoText(QString("Address disqualified due to an outgoing transaction with the hash %1. It can be activated now but it will not receive any rewards until it becomes eligible").arg(QString::fromStdString(field.disqualifyingTx.ToString())), COLOR_NEGATIVE); + }else if( field.fActivated && !field.eligible ){ + entry->setInfoText(QString("Address is activated but is not eligible until the next round."), COLOR_WARNING); + }else if( field.fActivated ){ entry->setEligible(field.eligible, field.reward); ++nEligibleAddresses; } - - }else if( field.balanceAtStart < SMART_REWARDS_MIN_BALANCE ){ - entry->setInfoText(QString("Address only held %1 SMART at the round's startblock. Minimum required: %2 SMART").arg(BitcoinUnits::format(BitcoinUnit::SMART, field.balanceAtStart)).arg(SMART_REWARDS_MIN_BALANCE/COIN), COLOR_NEGATIVE); - }else if( !field.disqualifyingTx.IsNull() ){ - entry->setDisqualifyingTx(field.disqualifyingTx); - entry->setInfoText(QString("Address disqualified due to an outgoing transaction with the hash %1").arg(QString::fromStdString(field.disqualifyingTx.ToString())), COLOR_NEGATIVE); }else{ - entry->setEligible(field.eligible, field.reward); - ++nEligibleAddresses; + + entry->setMinBalance(SMART_REWARDS_MIN_BALANCE_1_2); + + if( field.balanceAtStart < SMART_REWARDS_MIN_BALANCE_1_2 ){ + entry->setInfoText(QString("Address only held %1 SMART at the round's startblock. Minimum required: %2 SMART").arg(BitcoinUnits::format(BitcoinUnit::SMART, field.balanceAtStart)).arg(SMART_REWARDS_MIN_BALANCE_1_2/COIN), COLOR_NEGATIVE); + }else if( !field.disqualifyingTx.IsNull() ){ + entry->setDisqualifyingTx(field.disqualifyingTx); + entry->setInfoText(QString("Address disqualified due to an outgoing transaction with the hash %1").arg(QString::fromStdString(field.disqualifyingTx.ToString())), COLOR_NEGATIVE); + }else{ + entry->setEligible(field.eligible, field.reward); + ++nEligibleAddresses; + } } rewardSum += field.reward; @@ -482,24 +416,17 @@ void SmartrewardsList::updateOverviewUI(const CSmartRewardRound ¤tRound, c std::sort(vecEntries.begin(), vecEntries.end(), SortSmartRewardWidgets()); - for( size_t i=0; ismartRewardsList->layout()->addWidget(vecEntries[i]); + for (const auto &entry : vecEntries) { + ui->smartRewardsList->layout()->addWidget(entry); - if( i < vecEntries.size() - 1 ){ - - // Add a horizontal line + // Add a horizontal line unless it's the last entry + if( entry != vecEntries.back() ){ QHBoxLayout* hBox = new QHBoxLayout(); - QSpacerItem* spacerLeft = new QSpacerItem(20, 0, QSizePolicy::Fixed, QSizePolicy::Fixed); - QSpacerItem* spacerRight = new QSpacerItem(20, 0, QSizePolicy::Fixed, QSizePolicy::Fixed); QWidget* lineContainer = new QWidget(); - lineContainer->setStyleSheet("background-color: rgb(247, 245, 248);"); QFrame* line = new QFrame(lineContainer); line->setFrameShape(QFrame::HLine); line->setFrameShadow(QFrame::Plain); - hBox->addSpacerItem(spacerLeft); hBox->addWidget(line); - hBox->addSpacerItem(spacerRight); hBox->setSpacing(0); hBox->setContentsMargins(0, 0, 0, 0); lineContainer->setLayout(hBox); @@ -509,16 +436,16 @@ void SmartrewardsList::updateOverviewUI(const CSmartRewardRound ¤tRound, c } if( nAvailableForProof ){ - ui->btnSendProofs->setText( QString(tr("Send VoteProofs [%1]")).arg(nAvailableForProof) ); + ui->btnSendProofs->setText( QString(tr("Send ActivateRewards [%1]")).arg(nAvailableForProof) ); ui->btnSendProofs->setEnabled(true); }else{ - ui->btnSendProofs->setText( tr("No address ready for a VoteProof") ); + ui->btnSendProofs->setText( tr("No addresses need to ActivateRewards") ); ui->btnSendProofs->setEnabled(false); } ui->lblActiveAddresses->setText(QString::number(vecEntries.size())); ui->lblEligibleAddresses->setText(QString::number(nEligibleAddresses)); - QString strEstimated = QString::fromStdString(strprintf("%d", rewardSum/COIN)); + QString strEstimated = QString::fromStdString(strprintf("%d", (rewardSum + 50000000)/COIN)); AddThousandsSpaces(strEstimated); ui->lblTotalRewards->setText(strEstimated + " SMART"); } @@ -533,24 +460,19 @@ void SmartrewardsList::updateUI() CSmartRewardRound currentRound; CBlockIndex* tip = nullptr; { - LOCK(cs_rewardrounds); - currentRound = prewards->GetCurrentRound(); + LOCK(cs_rewardscache); + currentRound = *prewards->GetCurrentRound(); tip = chainActive.Tip(); } switch(state){ case STATE_INIT: - setState(STATE_PROCESSING); + if( prewards->IsSynced() && !fReindex ){ - break; - case STATE_PROCESSING: + ui->spinnerWidget->stop(); - if( prewards->IsSynced() && !fReindex ){ setState(STATE_OVERVIEW); - }else{ - double progress = prewards->GetProgress() * ui->loadingProgress->maximum(); - ui->loadingProgress->setValue(progress); } break; @@ -588,7 +510,7 @@ void SmartrewardsList::setState(SmartrewardsList::SmartRewardsListState state) void SmartrewardsList::on_btnSendProofs_clicked() { - SpecialTransactionDialog dlg(VOTE_PROOF_TRANSACTIONS, platformStyle); + SpecialTransactionDialog dlg(ACTIVATION_TRANSACTIONS, platformStyle); dlg.setModel(model); dlg.exec(); updateUI(); diff --git a/src/qt/smartrewardslist.h b/src/qt/smartrewardslist.h index c203e951..bf5e154d 100644 --- a/src/qt/smartrewardslist.h +++ b/src/qt/smartrewardslist.h @@ -59,7 +59,6 @@ class SmartrewardsList : public QWidget enum SmartRewardsListState{ STATE_INIT, - STATE_PROCESSING, STATE_OVERVIEW }; diff --git a/src/qt/smartvoting.cpp b/src/qt/smartvoting.cpp index 202ffd57..912dce49 100755 --- a/src/qt/smartvoting.cpp +++ b/src/qt/smartvoting.cpp @@ -20,6 +20,7 @@ #include "castvotesdialog.h" #include "validation.h" #include "voteaddressesdialog.h" +#include "specialtransactiondialog.h" #include // for 'map_list_of()' @@ -160,7 +161,7 @@ void SmartVotingPage::updateUI() void SmartVotingPage::proposalsUpdated(const string &strErr) { if( strErr != ""){ - QMessageBox::warning(this, "Error", QString("Could not update proposal list\n\n%1").arg(QString::fromStdString(strErr))); +// QMessageBox::warning(this, "Error", QString("Could not update proposal list\n\n%1").arg(QString::fromStdString(strErr))); return; } @@ -195,12 +196,12 @@ void SmartVotingPage::castVotes(){ CastVotesDialog dialog(platformStyle, votingManager, walletModel); - connect( &dialog, SIGNAL(votedForAddress(QString&, int, bool)), this, SLOT(voteDone(QString&, int, bool))); dialog.setVoting(mapVoteProposals); dialog.exec(); refreshProposals(true); + uiInterface.NotifySmartRewardUpdate(); } void SmartVotingPage::updateRefreshLock() @@ -247,26 +248,3 @@ void SmartVotingPage::balanceChanged(const CAmount &balance, const CAmount &unco updateUI(); } -void SmartVotingPage::voteDone(QString &address, int nProposalId, bool successful) -{ - if( successful ){ - - LOCK(pwalletMain->cs_wallet); - - CKeyID keyId; - std::string strProposal = strprintf("%d", nProposalId); - uint256 nProposalHash = Hash(strProposal.begin(), strProposal.end()); - - if(CSmartAddress(address.toStdString()).GetKeyID(keyId)){ - - int nCurrentRound = prewards->GetCurrentRound().number; - - if( !pwalletMain->mapVoted[keyId].count(nCurrentRound) ){ - pwalletMain->mapVoted[keyId].insert(std::make_pair(nCurrentRound, nProposalHash)); - pwalletMain->UpdateVotedMap(keyId); - } - - } - } - -} diff --git a/src/qt/smartvoting.h b/src/qt/smartvoting.h index 2ba6323d..09687ab9 100755 --- a/src/qt/smartvoting.h +++ b/src/qt/smartvoting.h @@ -59,7 +59,7 @@ class SmartVotingPage : public QWidget SmartVotingManager *votingManager; std::vector vecProposalWidgets; std::map mapVoteProposals; - + public Q_SLOTS: void updateUI(); void updateProposalUI(); @@ -72,6 +72,5 @@ public Q_SLOTS: void scrollChanged(int value); void balanceChanged(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance, const CAmount& watchOnlyBalance, const CAmount& watchUnconfBalance, const CAmount& watchImmatureBalance); - void voteDone(QString &address, int nProposalId, bool successful); }; #endif // BITCOIN_QT_SMARTVOTING_H diff --git a/src/qt/smartvotingmanager.cpp b/src/qt/smartvotingmanager.cpp index 40d956f7..cf9208b4 100755 --- a/src/qt/smartvotingmanager.cpp +++ b/src/qt/smartvotingmanager.cpp @@ -16,7 +16,7 @@ #include const QString urlHiveVotingPortal = "https://vote.smartcash.cc/api/v1/"; -const QString urlHiveVotingPortalTestnet = "https://votesmartcashtest.azurewebsites.net/api/v1/"; +const QString urlHiveVotingPortalTestnet = "https://testnet-vote.smrt.cash/api/v1/"; SmartHiveRequest::SmartHiveRequest(QString endpoint): QNetworkRequest(), diff --git a/src/qt/specialtransactiondialog.cpp b/src/qt/specialtransactiondialog.cpp index ddfab79e..7ee5ba39 100644 --- a/src/qt/specialtransactiondialog.cpp +++ b/src/qt/specialtransactiondialog.cpp @@ -14,6 +14,7 @@ #include "txmempool.h" #include "walletmodel.h" #include "sendcoinsdialog.h" +#include "guiconstants.h" #include "coincontrol.h" #include "consensus/validation.h" @@ -40,6 +41,7 @@ #include #define SEND_CONFIRM_DELAY 5 +#define MAX_ACTIVATION_TRANSACTIONS 10 bool Error(std::string where, std::string message, QString &strError) { @@ -65,13 +67,13 @@ SpecialTransactionDialog::SpecialTransactionDialog(const SpecialTransactionType ui->labelFeeDesc->setText(strRegistrationFeeDescription); ui->descriptionLabel->setText(strRegistrationDescription); break; - case VOTE_PROOF_TRANSACTIONS: // TBD - nRequiredFee = REWARDS_VOTEPROOF_FEE; - nRequiredNetworkFee = REWARDS_VOTEPROOF_TX_FEE; - this->setWindowTitle(strVoteProofTitle); + case ACTIVATION_TRANSACTIONS: + nRequiredFee = REWARDS_ACTIVATION_FEE; + nRequiredNetworkFee = REWARDS_ACTIVATION_TX_FEE; + this->setWindowTitle(strActivationTxTitle); ui->labelFeeDesc->hide(); ui->labelFeeAmount->hide(); - ui->descriptionLabel->setText(strVoteProofDescription); + ui->descriptionLabel->setText(strActivationTxDescription); break; default: nRequiredFee = -1; @@ -114,6 +116,10 @@ SpecialTransactionDialog::SpecialTransactionDialog(const SpecialTransactionType ui->treeWidget->setColumnHidden(COLUMN_TXHASH, true); // store transaction hash in this column, but don't show it ui->treeWidget->setColumnHidden(COLUMN_VOUT_INDEX, true); // store vout index in this column, but don't show it + ui->legendLabel->setText(QString("Green addresses are already activated. " + "Yellow addresses are SmartNode inputs and do not qualify for SmartRewards.") + .arg(COLOR_GREEN.name()).arg(COLOR_YELLOW.name())); + UpdateElements(); // default view is sorted by amount desc @@ -135,23 +141,16 @@ void SpecialTransactionDialog::setModel(WalletModel *model) } } -QString SpecialTransactionDialog::GetTypeString() -{ - - switch(type){ - case REGISTRATION_TRANSACTIONS: - return "registration transactions"; - case VOTE_PROOF_TRANSACTIONS: - return "vote proof transactions"; - } - - return "Unknown"; -} - // ok button void SpecialTransactionDialog::buttonBoxClicked(QAbstractButton* button) { if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole){ + if ((type == ACTIVATION_TRANSACTIONS) && (mapOutputs.size() > MAX_ACTIVATION_TRANSACTIONS)) { + QMessageBox::warning(this, windowTitle(), + tr("Only %1 activation transactions can be sent at once.").arg(MAX_ACTIVATION_TRANSACTIONS), + QMessageBox::Ok, QMessageBox::Ok); + return; + } int nCount = mapOutputs.size(); CAmount nTotalAmount = nCount * GetRequiredTotal(); @@ -161,7 +160,18 @@ void SpecialTransactionDialog::buttonBoxClicked(QAbstractButton* button) LogPrintf(" %s, out: %s\n", it.first.toStdString(), it.second.ToString()); } - QString strType = GetTypeString(); + QString strType; + switch(type){ + case REGISTRATION_TRANSACTIONS: + strType = nCount > 1 ? "registration transactions" : "registration transaction"; + break; + case ACTIVATION_TRANSACTIONS: + strType = nCount > 1 ? "activation transactions" : "activation transaction"; + break; + default: + strType = "Unknown"; + break; + } QString questionString = QString("Sending %1 %2, %3 each including fee") .arg(nCount) @@ -173,7 +183,7 @@ void SpecialTransactionDialog::buttonBoxClicked(QAbstractButton* button) .arg(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), nTotalAmount))); SendConfirmationDialog confirmationDialog(tr("Confirm send %1").arg(strType), - questionString, SEND_CONFIRM_DELAY, this); + questionString, SEND_CONFIRM_DELAY, QMessageBox::Question, this); confirmationDialog.exec(); QMessageBox::StandardButton retval = (QMessageBox::StandardButton)confirmationDialog.result(); @@ -232,8 +242,8 @@ void SpecialTransactionDialog::buttonBoxClicked(QAbstractButton* button) "They are not derived from the wallet's seed so you are not able to recover them " "with any earlier backup of your wallet.")); break; - case VOTE_PROOF_TRANSACTIONS: - strResult.append(tr("It requires %1 block confirmations for the VoteProof transactions before the address will become eligible in the SmartRewards tab.").arg(Params().GetConsensus().nRewardsConfirmationsRequired)); + case ACTIVATION_TRANSACTIONS: + strResult.append(tr("It requires %1 block confirmation for the activation transactions before the address will become eligible in the SmartRewards tab.").arg(Params().GetConsensus().nRewardsConfirmationsRequired)); break; } @@ -364,7 +374,6 @@ void SpecialTransactionDialog::selectSmallestOutput(QTreeWidgetItem* topLevel) COutPoint outpt(uint256S(smallestItem->text(COLUMN_TXHASH).toStdString()), smallestItem->text(COLUMN_VOUT_INDEX).toUInt()); mapOutputs.insert(std::make_pair(sAddress, outpt)); } - } void SpecialTransactionDialog::SendTransactions(std::vector &vecErrors) @@ -380,9 +389,17 @@ void SpecialTransactionDialog::SendTransactions(std::vector &vecErrors) case REGISTRATION_TRANSACTIONS: fSuccess = SendRegistration(it.first, it.second, strError); break; - case VOTE_PROOF_TRANSACTIONS: - fSuccess = SendVoteProof(it.first, it.second, prewards->GetCurrentRound().number, strError); - break; + case ACTIVATION_TRANSACTIONS:{ + + int nCurrentRound; + + { + LOCK(cs_rewardscache); + nCurrentRound = prewards->GetCurrentRound()->number; + } + + fSuccess = SendActivationTransaction(it.first, it.second, nCurrentRound, strError); + }break; } if( !fSuccess ){ @@ -390,7 +407,6 @@ void SpecialTransactionDialog::SendTransactions(std::vector &vecErrors) continue; } } - } bool SpecialTransactionDialog::SendRegistration(const QString &address, const COutPoint &out, QString &strError) @@ -576,7 +592,7 @@ bool SpecialTransactionDialog::SendRegistration(const QString &address, const CO return true; } -bool SpecialTransactionDialog::SendVoteProof(const QString &address, const COutPoint &out, int nCurrentRound, QString &strError) +bool SpecialTransactionDialog::SendActivationTransaction(const QString &address, const COutPoint &out, int nCurrentRound, QString &strError) { // ** // Check if the unspent output belongs to
or not @@ -586,12 +602,12 @@ bool SpecialTransactionDialog::SendVoteProof(const QString &address, const COutP uint256 blockHash; if( !GetTransaction(out.hash, spendTx, Params().GetConsensus(), blockHash, true) ) - return Error("GenerateVoteProof", + return Error("GenerateActivation", strprintf("TX-Hash %s doesn't belong to a transaction",out.hash.ToString()), strError); if( static_cast(spendTx.vout.size()) - 1 < out.n ) - return Error("GenerateVoteProof", + return Error("GenerateActivation", strprintf("TX-Index %d out of range for TX %s",out.n, out.hash.ToString()), strError); @@ -604,60 +620,36 @@ bool SpecialTransactionDialog::SendVoteProof(const QString &address, const COutP CSmartAddress voteAddress(address.toStdString()); if ( !voteAddress.IsValid() ) - return Error("GenerateVoteProof", + return Error("GenerateActivation", strprintf("Failed to validate address for TX %s, index %s", out.hash.ToString(), out.n), strError); CKeyID voteAddressKeyID; if (!voteAddress.GetKeyID(voteAddressKeyID)) - return Error("GenerateVoteProof", + return Error("GenerateActivation", strprintf("Address does't refer to a key for TX %s, index %s", out.hash.ToString(), out.n), strError); - std::vector addresses; - txnouttype type; - int nRequired; + CTxDestination addressSolved; - if (!ExtractDestinations(utxo.scriptPubKey, type, addresses, nRequired) || addresses.size() != 1) { - return Error("GenerateVoteProof", + if (!ExtractDestination(utxo.scriptPubKey, addressSolved)) { + return Error("GenerateActivation", strprintf("Failed to extract address for output with TX %s, index %s", out.hash.ToString(), out.n), strError); } + CKeyID keyIdSolved; + // Force option 1 - verify the vote address with the input of the register tx - if( !(CSmartAddress(addresses[0]) == voteAddress) ) - return Error("GenerateVoteProof", + if( !CSmartAddress(addressSolved).GetKeyID(keyIdSolved) || keyIdSolved != voteAddressKeyID ){ + return Error("GenerateActivation", strprintf("Failed to force vote proof option one for address %s with TX %s, index %s", voteAddress.ToString(), out.hash.ToString(), out.n), strError); - - // ** - // ** Prepare the VoteProof data - // ** - - if( pwalletMain->mapVoted[voteAddressKeyID].find(nCurrentRound) == pwalletMain->mapVoted[voteAddressKeyID].end() ){ - return Error("GenerateVoteProof", - strprintf("Address %s did not yet vote during the SmartRewards round %d", - voteAddress.ToString(), nCurrentRound), - strError); } - std::vector vecData = { - OP_RETURN_VOTE_PROOF_FLAG, - 0x01 // Proof option 1 - }; - - CDataStream proofData(SER_NETWORK,0); - - proofData << nCurrentRound; - proofData << pwalletMain->mapVoted[voteAddressKeyID][nCurrentRound]; - - vecData.insert(vecData.end(), proofData.begin(), proofData.end()); - - CScript proofScript = CScript() << OP_RETURN << vecData; - // ** // Create the transaction // ** @@ -671,6 +663,20 @@ bool SpecialTransactionDialog::SendVoteProof(const QString &address, const COutP coinControl.Select(output); coinControl.destChange = change; + // Write script to self address + CScript proofScript = GetScriptForDestination(addressSolved); + + + // Figure out how much the output contains + map::const_iterator it = pwalletMain->mapWallet.find(out.hash); + if (it == pwalletMain->mapWallet.end()) + return Error("GenerateActivation", + "Failed to find output transaction in wallet", + strError); + + const CWalletTx &tx = it->second; + CAmount nOutputAmount = tx.vout[out.n].nValue; + // Create and send the transaction CReserveKey reservekey(pwalletMain); CWalletTx proofTx; @@ -679,12 +685,12 @@ bool SpecialTransactionDialog::SendVoteProof(const QString &address, const COutP vector vecSend; int nChangePosRet = -1; - CRecipient recipient = {proofScript, REWARDS_VOTEPROOF_FEE, false}; + CRecipient recipient = {proofScript, nOutputAmount, true}; vecSend.push_back(recipient); if (!pwalletMain->CreateTransaction(vecSend, proofTx, reservekey, nFeeRequired, nChangePosRet, err, &coinControl)) - return Error("GenerateVoteProof", + return Error("GenerateActivation", strprintf("Failed to generate transaction: %s for TX %s, index %d", err, out.hash.ToString(), out.n), strError); @@ -693,19 +699,13 @@ bool SpecialTransactionDialog::SendVoteProof(const QString &address, const COutP CValidationState state; if (!(CheckTransaction(proofTx, state, proofTx.GetHash(), false) || !state.IsValid())) - return Error("GenerateVoteProof", - strprintf("VoteProof transaction invalid for TX %s, index %d: %s", + return Error("GenerateActivation", + strprintf("Activation transaction invalid for TX %s, index %d: %s", out.hash.ToString(), out.n, state.GetRejectReason()), strError); - { - LOCK(pwalletMain->cs_wallet); - pwalletMain->mapVoteProofs[voteAddressKeyID].insert(std::make_pair(nCurrentRound, proofTx.GetHash())); - pwalletMain->UpdateVoteProofs(voteAddressKeyID); - } - if( !pwalletMain->CommitTransaction(proofTx, reservekey, g_connman.get()) ) - return Error("GenerateVoteProof", + return Error("GenerateActivation", strprintf("Failed to send the transaction TX %s", proofTx.ToString()), strError); @@ -825,7 +825,7 @@ void SpecialTransactionDialog::updateView() model->listCoins(mapCoins, false); BOOST_FOREACH(const PAIRTYPE(QString, std::vector)& coins, mapCoins) { - + QBrush lineBrush; QString sWalletAddress = coins.first; QString sWalletLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress); @@ -845,26 +845,30 @@ void SpecialTransactionDialog::updateView() } - if( type == VOTE_PROOF_TRANSACTIONS ){ + if( type == ACTIVATION_TRANSACTIONS ){ CKeyID keyId; - CSmartAddress voteAddress(sWalletAddress.toStdString()); - int nCurrentRound = prewards->GetCurrentRound().number; + CSmartAddress address(CSmartAddress::Legacy(sWalletAddress.toStdString())); + int nCurrentRound = 0; + CSmartRewardEntry *reward = nullptr; - CSmartRewardEntry reward; - - if( voteAddress.GetKeyID(keyId) && prewards->GetRewardEntry(voteAddress, reward) ){ + { + LOCK(cs_rewardscache); + nCurrentRound = prewards->GetCurrentRound()->number; + prewards->GetRewardEntry(address, reward, false); + } - LOCK(pwalletMain->cs_wallet); + if( !address.GetKeyID(keyId) ){ + continue; + } - if( pwalletMain->mapVoted[keyId].find(nCurrentRound) == pwalletMain->mapVoted[keyId].end() || - pwalletMain->mapVoteProofs[keyId].find(nCurrentRound) != pwalletMain->mapVoteProofs[keyId].end() || - reward.balanceEligible == 0 || !reward.disqualifyingTx.IsNull() || !reward.smartnodePaymentTx.IsNull() ){ - // If not yet voted, no eligible balance or already vote proven skip it. - continue; + if (reward) { + if (reward->fActivated) { + // Address is already activated + lineBrush.setColor(COLOR_GREEN); + } else if (!reward->smartnodePaymentTx.IsNull()) { + // Address is linked to a SmartNode + lineBrush.setColor(COLOR_YELLOW); } - - }else{ - continue; } } @@ -889,9 +893,11 @@ void SpecialTransactionDialog::updateView() itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); // label + itemWalletAddress->setForeground(COLUMN_LABEL, lineBrush); itemWalletAddress->setText(COLUMN_LABEL, sWalletLabel); // address + itemWalletAddress->setForeground(COLUMN_ADDRESS, lineBrush); itemWalletAddress->setText(COLUMN_ADDRESS, sWalletAddress); CAmount nSum = 0; @@ -929,13 +935,12 @@ void SpecialTransactionDialog::updateView() { itemOutput->setDisabled(true); } - } itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")"); + itemWalletAddress->setForeground(COLUMN_AMOUNT, lineBrush); itemWalletAddress->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum)); itemWalletAddress->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)nSum)); - } for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) @@ -949,5 +954,4 @@ void SpecialTransactionDialog::updateView() UpdateElements(); connect(ui->treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(viewItemChanged(QTreeWidgetItem*, int))); - } diff --git a/src/qt/specialtransactiondialog.h b/src/qt/specialtransactiondialog.h index 00d7ec8c..e19cdb4e 100644 --- a/src/qt/specialtransactiondialog.h +++ b/src/qt/specialtransactiondialog.h @@ -31,7 +31,7 @@ namespace Ui { enum SpecialTransactionType { REGISTRATION_TRANSACTIONS, - VOTE_PROOF_TRANSACTIONS + ACTIVATION_TRANSACTIONS }; #define ASYMP_UTF8 "\xE2\x89\x88" @@ -71,7 +71,7 @@ class SpecialTransactionDialog : public QDialog void SendTransactions(std::vector &vecErrors); bool SendRegistration(const QString &address, const COutPoint &out, QString &strError); - bool SendVoteProof(const QString &address, const COutPoint &out, int nCurrentRound, QString &strError); + bool SendActivationTransaction(const QString &address, const COutPoint &out, int nCurrentRound, QString &strError); enum { @@ -84,7 +84,6 @@ class SpecialTransactionDialog : public QDialog }; friend class CCoinControlWidgetItem; - QString GetTypeString(); private Q_SLOTS: void showMenu(const QPoint &); void copyAddress(); @@ -103,9 +102,11 @@ static const QString strRegistrationDescription = ( ); static const QString strRegistrationFeeDescription = "Register fee"; -static const QString strVoteProofTitle = "Send VoteProofs"; -static const QString strVoteProofDescription = ( -"" +static const QString strActivationTxTitle = "Activate Rewards"; +static const QString strActivationTxDescription = ( +"Use this form to send an ActivateReward transaction to make your addresses eligible for SmartRewards. " +"A small fee of 0.001 SMART will be taken from outputs you choose.\n\n" +"You can either manually select an input for each address or automatically select the smallest input for each address by clicking the checkbox below." ); #endif // SMARTCASH_QT_SPECIALTRANSACTIONDIALOG_H diff --git a/src/qt/timelocksettingswidget.cpp b/src/qt/timelocksettingswidget.cpp new file mode 100644 index 00000000..c2be526e --- /dev/null +++ b/src/qt/timelocksettingswidget.cpp @@ -0,0 +1,106 @@ +// Copyright (c) 2020 The SmartCash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include "validation.h" +#include "chainparams.h" + +#include "timelocksettingswidget.h" + +#define ONE_MONTH (30.5 * 24 * 60 * 60) +#define ONE_YEAR (365 * 24 * 60 * 60) + +TimeLockSettingsWidget::TimeLockSettingsWidget(QWidget *parent) : + QWidget(parent), + nLockTime(0) +{ + const int nAvgBlockTime = Params().GetConsensus().nPowTargetSpacing; + timeLockItems.emplace_back("Set LockTime", 0); + timeLockItems.emplace_back("1 month", (int)(ONE_MONTH / nAvgBlockTime)); + timeLockItems.emplace_back("2 months", (int)(2 * ONE_MONTH / nAvgBlockTime)); + timeLockItems.emplace_back("3 months", (int)(3 * ONE_MONTH / nAvgBlockTime)); + timeLockItems.emplace_back("6 months", (int)(6 * ONE_MONTH / nAvgBlockTime)); + timeLockItems.emplace_back("1 year", (int)(ONE_YEAR / nAvgBlockTime)); + timeLockItems.emplace_back("Custom (until block)", -1); + timeLockItems.emplace_back("Custom (until date)", -1); + + timeLockCombo = new QComboBox(); + for (const auto &i : timeLockItems) { + timeLockCombo->addItem(i.first); + } + + QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + + timeLockCombo->setSizePolicy(sizePolicy); + timeLockCombo->setToolTip("Lock a transaction to be spent at future time."); + + // Make Timelock feature visible only if supermajority enforced BIP65 + if(!IsSuperMajority(4, chainActive.Tip(), Params().GetConsensus().nMajorityEnforceBlockUpgrade, + Params().GetConsensus())) + { + timeLockCombo->setVisible(false); + } + + timeLockCustomBlocks = new QSpinBox(); + timeLockCustomBlocks->setVisible(false); + timeLockCustomBlocks->setRange(1, 1000000); + timeLockCustomBlocks->setValue(chainActive.Height()); + + timeLockCustomDate = new QDateTimeEdit(); + timeLockCustomDate->setVisible(false); + timeLockCustomDate->setMinimumDateTime(QDateTime::currentDateTime()); + timeLockCustomDate->setCalendarPopup(true); + timeLockCustomDate->setDisplayFormat("MMMM d yy hh:mm:ss"); + + connect(timeLockCustomBlocks, SIGNAL(valueChanged(int)), this, SLOT(timeLockCustomBlocksChanged(int))); + connect(timeLockCustomDate, SIGNAL(dateTimeChanged(const QDateTime&)), this, + SLOT(timeLockCustomDateChanged(const QDateTime&))); + connect(timeLockCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(timeLockComboChanged(int))); + + QHBoxLayout *layout = new QHBoxLayout(this); + layout->addWidget(timeLockCombo); + layout->addWidget(timeLockCustomBlocks); + layout->addWidget(timeLockCustomDate); +} + +void TimeLockSettingsWidget::timeLockComboChanged(int index) +{ + if (timeLockItems[index].first == "Custom (until block)") { + timeLockCustomDate->setVisible(false); + timeLockCustomBlocks->setVisible(true); + nLockTime = timeLockCustomBlocks->value(); + } + else if (timeLockItems[index].first == "Custom (until date)") + { + timeLockCustomDate->setVisible(true); + timeLockCustomBlocks->setVisible(false); + nLockTime = timeLockCustomDate->dateTime().toMSecsSinceEpoch() / 1000; + } + else + { + timeLockCustomDate->setVisible(false); + timeLockCustomBlocks->setVisible(false); + nLockTime = timeLockItems[index].second > 0 ? chainActive.Height() + timeLockItems[index].second : 0; + } +} + +void TimeLockSettingsWidget::timeLockCustomBlocksChanged(int i) +{ + nLockTime = i; +} + +void TimeLockSettingsWidget::timeLockCustomDateChanged(const QDateTime& dt) +{ + nLockTime = dt.toMSecsSinceEpoch() / 1000; +} + +void TimeLockSettingsWidget::reset() +{ + timeLockCombo->setCurrentIndex(0); + timeLockCustomBlocks->setVisible(false); + timeLockCustomDate->setVisible(false); +} diff --git a/src/qt/timelocksettingswidget.h b/src/qt/timelocksettingswidget.h new file mode 100644 index 00000000..ebd7dd05 --- /dev/null +++ b/src/qt/timelocksettingswidget.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 The SmartCash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_QT_TIMELOCKSETTINGSWIDGET_H +#define BITCOIN_QT_TIMELOCKSETTINGSWIDGET_H + +#include +#include + +#include +#include +#include +#include + +class TimeLockSettingsWidget : public QWidget +{ + Q_OBJECT + +public: + explicit TimeLockSettingsWidget(QWidget *parent = 0); + + int64_t getLockTime() { return nLockTime; } + void reset(); + +private: + QComboBox *timeLockCombo; + QSpinBox *timeLockCustomBlocks; + QDateTimeEdit *timeLockCustomDate; + std::vector> timeLockItems; + int64_t nLockTime; + +private Q_SLOTS: + void timeLockComboChanged(int); + void timeLockCustomBlocksChanged(int); + void timeLockCustomDateChanged(const QDateTime&); +}; + +#endif // BITCOIN_QT_TIMELOCKSETTINGSWIDGET_H diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 45d45f62..ea84e9d2 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -174,11 +174,11 @@ void WalletModel::updateTransaction() fForceCheckBalanceChanged = true; } -void WalletModel::updateAddressBook(const QString &address, const QString &addressNew, const QString &label, +void WalletModel::updateAddressBook(const QString &address, const QString &label, bool isMine, const QString &purpose, int status) { if(addressTableModel) - addressTableModel->updateEntry(address, addressNew, label, isMine, purpose, status); + addressTableModel->updateEntry(address, label, isMine, purpose, status); } void WalletModel::updateWatchOnlyFlag(bool fHaveWatchonly) @@ -253,7 +253,16 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact setAddress.insert(rcp.address); ++nAddresses; - CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); + CScript scriptPubKey; + if (rcp.nLockTime > 0) + { + scriptPubKey = GetLockedScriptForDestination(CBitcoinAddress(rcp.address.toStdString()).Get(), + rcp.nLockTime); + } + else + { + scriptPubKey = GetScriptForDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); + } CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount}; vecSend.push_back(recipient); @@ -695,14 +704,12 @@ static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet, const std::string &purpose, ChangeType status) { QString strAddress = QString::fromStdString(CBitcoinAddress(address).ToString()); - QString strAddressNew = QString::fromStdString(CBitcoinAddress(address).ToString(true)); QString strLabel = QString::fromStdString(label); QString strPurpose = QString::fromStdString(purpose); qDebug() << "NotifyAddressBookChanged: " + strAddress + " " + strLabel + " isMine=" + QString::number(isMine) + " purpose=" + strPurpose + " status=" + QString::number(status); QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Qt::QueuedConnection, Q_ARG(QString, strAddress), - Q_ARG(QString, strAddressNew), Q_ARG(QString, strLabel), Q_ARG(bool, isMine), Q_ARG(QString, strPurpose), @@ -845,7 +852,7 @@ void WalletModel::getOutputs(const std::vector& vOutpoints, std::vect if (!wallet->mapWallet.count(outpoint.hash)) continue; int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); if (nDepth < 0) continue; - COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true, true); + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true, true, wallet->mapWallet[outpoint.hash].vout[outpoint.n].GetLockTime()); vOutputs.push_back(out); } } @@ -872,7 +879,7 @@ void WalletModel::listCoins(std::map >& mapCoins, if (!wallet->mapWallet.count(outpoint.hash)) continue; int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); if (nDepth < 0) continue; - COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true, true); + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true, true, wallet->mapWallet[outpoint.hash].vout[outpoint.n].GetLockTime()); if (outpoint.n < out.tx->vout.size() && wallet->IsMine(out.tx->vout[outpoint.n]) == ISMINE_SPENDABLE) vCoins.push_back(out); } @@ -885,7 +892,7 @@ void WalletModel::listCoins(std::map >& mapCoins, while (wallet->IsChange(cout.tx->vout[cout.i]) && cout.tx->vin.size() > 0 && wallet->IsMine(cout.tx->vin[0])) { if (!wallet->mapWallet.count(cout.tx->vin[0].prevout.hash)) break; - cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0, true, true); + cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0, true, true, wallet->mapWallet[cout.tx->vin[0].prevout.hash].vout[cout.tx->vin[0].prevout.n].GetLockTime()); } } CTxDestination address; diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 80cee9de..6f9af55d 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -42,7 +42,7 @@ class SendCoinsRecipient public: explicit SendCoinsRecipient() : amount(0), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) { } explicit SendCoinsRecipient(const QString &addr, const QString &label, const CAmount& amount, const QString &message): - address(addr), label(label), amount(amount), message(message), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) {} + address(addr), label(label), amount(amount), nLockTime(0), message(message), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) {} // If from an unauthenticated payment request, this is used for storing // the addresses, e.g. address-A
address-B
address-C. @@ -57,6 +57,7 @@ class SendCoinsRecipient bool fUseInstantSend; bool fUseNewAddressFormat; CAmount amount; + int64_t nLockTime; // If from a payment request, this is used for storing the memo QString message; @@ -321,7 +322,7 @@ public Q_SLOTS: /* New transaction, or transaction changed status */ void updateTransaction(); /* New, updated or removed address book entry */ - void updateAddressBook(const QString &address, const QString &addressNew, const QString &label, bool isMine, const QString &purpose, int status); + void updateAddressBook(const QString &address, const QString &label, bool isMine, const QString &purpose, int status); /* Watch-only added */ void updateWatchOnlyFlag(bool fHaveWatchonly); /* Current, immature or unconfirmed balance might have changed - emit 'balanceChanged' if so */ diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 8cd8429c..6ef3419c 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -39,6 +39,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "sendtoaddress", 4 }, { "sendtoaddress", 5 }, { "sendtoaddress", 6 }, + { "sendtoaddresslocked", 1 }, + { "sendtoaddresslocked", 2 }, { "instantsendtoaddress", 1 }, { "instantsendtoaddress", 4 }, { "settxfee", 0 }, @@ -143,6 +145,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "getaddressmempool", 0}, { "getaddresses", 0}, { "getaddresses", 1}, + { "getnewaddress", 1}, { "getrandomkeypair", 0}, { "dumpprivkey", 1}, { "dumpwallet", 1} diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index a4f0da20..30196436 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -67,6 +67,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) entry.push_back(Pair("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION))); entry.push_back(Pair("version", tx.nVersion)); entry.push_back(Pair("locktime", (int64_t)tx.nLockTime)); + entry.push_back(Pair("activation", tx.IsActivationTx())); UniValue vin(UniValue::VARR); BOOST_FOREACH(const CTxIn& txin, tx.vin) { UniValue in(UniValue::VOBJ); @@ -168,6 +169,7 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp) " \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\n" " \"version\" : n, (numeric) The version\n" " \"locktime\" : ttt, (numeric) The lock time\n" + " \"activation\" : true|false, (boolean) true if the transaction is a SmartRewards activation transaction\n" " \"vin\" : [ (array of json objects)\n" " {\n" " \"txid\": \"id\", (string) The transaction id\n" diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index a10b987f..7a473d29 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -415,6 +415,7 @@ static const CRPCCommand vRPCCommands[] = { "wallet", "sendfrom", &sendfrom, false }, { "wallet", "sendmany", &sendmany, false }, { "wallet", "sendtoaddress", &sendtoaddress, false }, + { "wallet", "sendtoaddresslocked", &sendtoaddresslocked, false }, { "wallet", "setaccount", &setaccount, true }, { "wallet", "settxfee", &settxfee, true }, { "wallet", "signmessage", &signmessage, true }, diff --git a/src/rpc/server.h b/src/rpc/server.h index 5103f287..420141d4 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -239,6 +239,7 @@ 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 sendtoaddresslocked(const UniValue& params, bool fHelp); extern UniValue signmessage(const UniValue& params, bool fHelp); extern UniValue verifymessage(const UniValue& params, bool fHelp); extern UniValue getreceivedbyaddress(const UniValue& params, bool fHelp); diff --git a/src/rpc/smartrewards.cpp b/src/rpc/smartrewards.cpp index ba656bd6..2084bdaa 100644 --- a/src/rpc/smartrewards.cpp +++ b/src/rpc/smartrewards.cpp @@ -47,7 +47,7 @@ UniValue smartrewards(const UniValue& params, bool fHelp) ); if( !fDebug && !prewards->IsSynced() ) - throw JSONRPCError(RPC_DATABASE_ERROR, strprintf("Rewards database is not up to date. Current progress %d%%",int(prewards->GetProgress() * 100))); + throw JSONRPCError(RPC_DATABASE_ERROR, "Rewards database is not up to date."); TRY_LOCK(cs_rewardsdb, lockRewardsDb); @@ -59,25 +59,25 @@ UniValue smartrewards(const UniValue& params, bool fHelp) { UniValue obj(UniValue::VOBJ); - TRY_LOCK(cs_rewardrounds,roundsLocked); + TRY_LOCK(cs_rewardscache, cacheLocked); - if(!roundsLocked) throw JSONRPCError(RPC_DATABASE_ERROR, "Rewards database is busy..Try it again!"); + if(!cacheLocked) throw JSONRPCError(RPC_DATABASE_ERROR, "Rewards database is busy..Try it again!"); - const CSmartRewardRound& current = prewards->GetCurrentRound(); + const CSmartRewardRound *current = prewards->GetCurrentRound(); - if( !current.number ) throw JSONRPCError(RPC_DATABASE_ERROR, "No active reward round available yet."); + if( !current->number ) throw JSONRPCError(RPC_DATABASE_ERROR, "No active reward round available yet."); - obj.pushKV("rewards_cycle",current.number); - obj.pushKV("start_blockheight",current.startBlockHeight); - obj.pushKV("start_blocktime",current.startBlockTime); - obj.pushKV("end_blockheight",current.endBlockHeight); - obj.pushKV("end_blocktime",current.endBlockTime); - obj.pushKV("eligible_addresses",current.eligibleEntries - current.disqualifiedEntries); - obj.pushKV("eligible_smart",format(current.eligibleSmart - current.disqualifiedSmart)); - obj.pushKV("disqualified_addresses",current.disqualifiedEntries); - obj.pushKV("disqualified_smart",format(current.disqualifiedSmart)); - obj.pushKV("estimated_rewards",format(current.rewards)); - obj.pushKV("estimated_percent",current.percent); + obj.pushKV("rewards_cycle",current->number); + obj.pushKV("start_blockheight",current->startBlockHeight); + obj.pushKV("start_blocktime",current->startBlockTime); + obj.pushKV("end_blockheight",current->endBlockHeight); + obj.pushKV("end_blocktime",current->endBlockTime); + obj.pushKV("eligible_addresses",current->eligibleEntries - current->disqualifiedEntries); + obj.pushKV("eligible_smart",format(current->eligibleSmart - current->disqualifiedSmart)); + obj.pushKV("disqualified_addresses",current->disqualifiedEntries); + obj.pushKV("disqualified_smart",format(current->disqualifiedSmart)); + obj.pushKV("estimated_rewards",format(current->rewards)); + obj.pushKV("estimated_percent",current->percent * 100.0); return obj; } @@ -86,51 +86,45 @@ UniValue smartrewards(const UniValue& params, bool fHelp) { UniValue obj(UniValue::VARR); - TRY_LOCK(cs_rewardrounds,roundsLocked); + TRY_LOCK(cs_rewardscache, cacheLocked); - if(!roundsLocked) throw JSONRPCError(RPC_DATABASE_ERROR, "Rewards database is busy..Try it again!"); + if(!cacheLocked) throw JSONRPCError(RPC_DATABASE_ERROR, "Rewards database is busy..Try it again!"); - const CSmartRewardRoundList& history = prewards->GetRewardRounds(); + const CSmartRewardRoundMap* history = prewards->GetRewardRounds(); int64_t nPayoutDelay = Params().GetConsensus().nRewardsPayoutStartDelay; - if(!history.size()) throw JSONRPCError(RPC_DATABASE_ERROR, "No finished reward round available yet."); + if(!history->size()) throw JSONRPCError(RPC_DATABASE_ERROR, "No finished reward round available yet."); - BOOST_FOREACH(CSmartRewardRound round, history) { + auto round = history->begin(); + + while( round != history->end() ){ UniValue roundObj(UniValue::VOBJ); - roundObj.pushKV("rewards_cycle",round.number); - roundObj.pushKV("start_blockheight",round.startBlockHeight); - roundObj.pushKV("start_blocktime",round.startBlockTime); - roundObj.pushKV("end_blockheight",round.endBlockHeight); - roundObj.pushKV("end_blocktime",round.endBlockTime); - roundObj.pushKV("eligible_addresses",round.eligibleEntries - round.disqualifiedEntries); - roundObj.pushKV("eligible_smart",format(round.eligibleSmart - round.disqualifiedSmart)); - roundObj.pushKV("disqualified_addresses",round.disqualifiedEntries); - roundObj.pushKV("disqualified_smart",format(round.disqualifiedSmart)); - roundObj.pushKV("rewards",format(round.rewards)); - roundObj.pushKV("percent",round.percent); + roundObj.pushKV("rewards_cycle",round->second.number); + roundObj.pushKV("start_blockheight",round->second.startBlockHeight); + roundObj.pushKV("start_blocktime",round->second.startBlockTime); + roundObj.pushKV("end_blockheight",round->second.endBlockHeight); + roundObj.pushKV("end_blocktime",round->second.endBlockTime); + roundObj.pushKV("eligible_addresses",round->second.eligibleEntries - round->second.disqualifiedEntries); + roundObj.pushKV("eligible_smart",format(round->second.eligibleSmart - round->second.disqualifiedSmart)); + roundObj.pushKV("disqualified_addresses",round->second.disqualifiedEntries); + roundObj.pushKV("disqualified_smart",format(round->second.disqualifiedSmart)); + roundObj.pushKV("rewards",format(round->second.rewards)); + roundObj.pushKV("percent",round->second.percent * 100.0); UniValue payObj(UniValue::VOBJ); - int nPayeeCount = round.eligibleEntries - round.disqualifiedEntries; - - if( nPayeeCount ){ + if( round->second.GetPayeeCount() ){ - int nBlockPayees = round.nBlockPayees; - int nPayoutInterval = round.nBlockInterval; - int nRewardBlocks = nPayeeCount / nBlockPayees; - if( nPayeeCount % nBlockPayees ) nRewardBlocks += 1; - int nLastRoundBlock = round.endBlockHeight + nPayoutDelay + ( (nRewardBlocks - 1) * nPayoutInterval ); - - payObj.pushKV("firstBlock", round.endBlockHeight + nPayoutDelay ); - payObj.pushKV("totalBlocks", nRewardBlocks ); - payObj.pushKV("lastBlock", nLastRoundBlock ); - payObj.pushKV("totalPayees", nPayeeCount); - payObj.pushKV("blockPayees", round.nBlockPayees); - payObj.pushKV("lastBlockPayees", nPayeeCount % nBlockPayees ); - payObj.pushKV("blockInterval",round.nBlockInterval); + payObj.pushKV("firstBlock", round->second.endBlockHeight + nPayoutDelay ); + payObj.pushKV("totalBlocks", round->second.GetRewardBlocks() ); + payObj.pushKV("lastBlock", round->second.GetLastRoundBlock() ); + payObj.pushKV("totalPayees", round->second.GetPayeeCount() ); + payObj.pushKV("blockPayees", round->second.nBlockPayees); + payObj.pushKV("lastBlockPayees", round->second.GetPayeeCount() % round->second.nBlockPayees ); + payObj.pushKV("blockInterval", round->second.nBlockInterval); }else{ payObj.pushKV("error", "No payees were eligible for this round"); } @@ -138,6 +132,8 @@ UniValue smartrewards(const UniValue& params, bool fHelp) roundObj.pushKV("payouts", payObj); obj.push_back(roundObj); + + ++round; } return obj; @@ -145,16 +141,16 @@ UniValue smartrewards(const UniValue& params, bool fHelp) if(strCommand == "payouts") { - TRY_LOCK(cs_rewardrounds,roundsLocked); + TRY_LOCK(cs_rewardscache, cacheLocked); - if(!roundsLocked) throw JSONRPCError(RPC_DATABASE_ERROR, "Rewards database is busy..Try it again!"); + if(!cacheLocked) throw JSONRPCError(RPC_DATABASE_ERROR, "Rewards database is busy..Try it again!"); - const CSmartRewardRound& current = prewards->GetCurrentRound(); + const CSmartRewardRound *current = prewards->GetCurrentRound(); - if( !current.number ) throw JSONRPCError(RPC_DATABASE_ERROR, "No active reward round available yet."); + if( !current->number ) throw JSONRPCError(RPC_DATABASE_ERROR, "No active reward round available yet."); int round = 0; - std::string err = strprintf("Past SmartReward round required: 1 - %d ",current.number - 1 ); + std::string err = strprintf("Past SmartReward round required: 1 - %d ",current->number - 1 ); if (params.size() != 2) throw JSONRPCError(RPC_INVALID_PARAMETER, err); @@ -170,16 +166,16 @@ UniValue smartrewards(const UniValue& params, bool fHelp) } catch (...) {} - if(round < 1 || round >= current.number) throw JSONRPCError(RPC_INVALID_PARAMETER, err); + if(round < 1 || round >= current->number) throw JSONRPCError(RPC_INVALID_PARAMETER, err); - CSmartRewardRoundResultList payouts; + CSmartRewardResultEntryList payouts; if( !prewards->GetRewardPayouts(round,payouts) ) throw JSONRPCError(RPC_DATABASE_ERROR, "Couldn't fetch the list from the database."); UniValue obj(UniValue::VARR); - BOOST_FOREACH(CSmartRewardRoundResult payout, payouts) { + BOOST_FOREACH(CSmartRewardResultEntry payout, payouts) { UniValue addrObj(UniValue::VOBJ); addrObj.pushKV("address", payout.entry.id.ToString()); @@ -193,16 +189,16 @@ UniValue smartrewards(const UniValue& params, bool fHelp) if(strCommand == "snapshot") { - TRY_LOCK(cs_rewardrounds,roundsLocked); + TRY_LOCK(cs_rewardscache, cacheLocked); - if(!roundsLocked) throw JSONRPCError(RPC_DATABASE_ERROR, "Rewards database is busy..Try it again!"); + if(!cacheLocked) throw JSONRPCError(RPC_DATABASE_ERROR, "Rewards database is busy..Try it again!"); - const CSmartRewardRound& current = prewards->GetCurrentRound(); + const CSmartRewardRound *current = prewards->GetCurrentRound(); - if( !current.number ) throw JSONRPCError(RPC_DATABASE_ERROR, "No active reward round available yet."); + if( !current->number ) throw JSONRPCError(RPC_DATABASE_ERROR, "No active reward round available yet."); int round = 0; - std::string err = strprintf("Past SmartReward round required: 1 - %d ",current.number - 1 ); + std::string err = strprintf("Past SmartReward round required: 1 - %d ",current->number - 1 ); if (params.size() != 2) throw JSONRPCError(RPC_INVALID_PARAMETER, err); @@ -218,16 +214,16 @@ UniValue smartrewards(const UniValue& params, bool fHelp) } catch (...) {} - if(round < 1 || round >= current.number) throw JSONRPCError(RPC_INVALID_PARAMETER, err); + if(round < 1 || round >= current->number) throw JSONRPCError(RPC_INVALID_PARAMETER, err); - CSmartRewardRoundResultList results; + CSmartRewardResultEntryList results; if( !prewards->GetRewardRoundResults(round, results) ) throw JSONRPCError(RPC_DATABASE_ERROR, "Couldn't fetch the list from the database."); UniValue obj(UniValue::VARR); - BOOST_FOREACH(CSmartRewardRoundResult s, results) { + BOOST_FOREACH(CSmartRewardResultEntry s, results) { UniValue addrObj(UniValue::VOBJ); addrObj.pushKV("address", s.entry.id.ToString()); @@ -243,31 +239,31 @@ UniValue smartrewards(const UniValue& params, bool fHelp) { if (params.size() != 2) throw JSONRPCError(RPC_INVALID_PARAMETER, "SmartCash address required."); - TRY_LOCK(cs_rewardrounds,roundsLocked); + TRY_LOCK(cs_rewardscache, cacheLocked); - if(!roundsLocked) throw JSONRPCError(RPC_DATABASE_ERROR, "Rewards database is busy..Try it again!"); + if(!cacheLocked) throw JSONRPCError(RPC_DATABASE_ERROR, "Rewards database is busy..Try it again!"); - const CSmartRewardRound& current = prewards->GetCurrentRound(); + const CSmartRewardRound *current = prewards->GetCurrentRound(); int nFirst_1_3_Round = Params().GetConsensus().nRewardsFirst_1_3_Round; std::string addressString = params[1].get_str(); - CSmartAddress id = CSmartAddress(addressString); + CSmartAddress id = CSmartAddress::Legacy(addressString); if( !id.IsValid() ) throw JSONRPCError(RPC_DATABASE_ERROR, strprintf("Invalid SmartCash address provided: %s",addressString)); - CSmartRewardEntry entry; + CSmartRewardEntry *entry = nullptr; - if( !prewards->GetRewardEntry(id, entry) ) throw JSONRPCError(RPC_DATABASE_ERROR, "Couldn't find this SmartCash address in the database."); + if( !prewards->GetRewardEntry(id, entry, false) ) throw JSONRPCError(RPC_DATABASE_ERROR, "Couldn't find this SmartCash address in the database."); UniValue obj(UniValue::VOBJ); - obj.pushKV("address",id.ToString()); - obj.pushKV("balance",format(entry.balance)); - obj.pushKV("balance_eligible", format(entry.balanceEligible)); - obj.pushKV("is_smartnode", !entry.smartnodePaymentTx.IsNull()); - obj.pushKV("voted", !entry.voteProof.IsNull()); - obj.pushKV("eligible", current.number < nFirst_1_3_Round ? entry.balanceEligible > 0 : entry.IsEligible()); + obj.pushKV("address", id.ToString()); + obj.pushKV("balance", format(entry->balance)); + obj.pushKV("balance_eligible", format(entry->balanceEligible)); + obj.pushKV("is_smartnode", !entry->smartnodePaymentTx.IsNull()); + obj.pushKV("activated", entry->fActivated); + obj.pushKV("eligible", current->number < nFirst_1_3_Round ? entry->balanceEligible > 0 : entry->IsEligible()); return obj; } diff --git a/src/sapi/sapi.cpp b/src/sapi/sapi.cpp index 8dba3ba5..862ae4f2 100644 --- a/src/sapi/sapi.cpp +++ b/src/sapi/sapi.cpp @@ -67,7 +67,7 @@ static const int DEFAULT_SAPI_THREADS=4; static const int DEFAULT_SAPI_WORKQUEUE=16; static const int DEFAULT_SAPI_SERVER_TIMEOUT=30; -static const int DEFAULT_SAPI_SERVER_PORT=80; +static const int DEFAULT_SAPI_SERVER_PORT=8080; static const int DEFAULT_SAPI_JSON_INDENT=2; diff --git a/src/sapi/sapi.h b/src/sapi/sapi.h index 58bbbf78..e1cc3d6c 100644 --- a/src/sapi/sapi.h +++ b/src/sapi/sapi.h @@ -73,6 +73,9 @@ enum Codes{ TxAlreadyInBlockchain, TxCantRelay, TxNotFound, + TxMissingTxId, + TxMissingVout, + TxInvalidParameter, /* smartreward errors */ RewardsDatabaseBusy = 6000, NoActiveRewardRound, @@ -96,6 +99,9 @@ namespace Keys{ const std::string maxInputs = "maxInputs"; const std::string height = "height"; const std::string hash = "hash"; + const std::string inputs = "inputs"; + const std::string outputs = "outputs"; + const std::string locktime = "locktime"; } namespace Validation{ @@ -189,6 +195,36 @@ namespace Validation{ SAPI::Result Validate(const std::string ¶meter, const UniValue &value) const final; }; + class Array : public Base{ + public: + Array() : Base(UniValue::VARR) {} + SAPI::Result Validate(const std::string ¶meter, const UniValue &value) const override; + }; + + class Object : public Base{ + public: + Object() : Base(UniValue::VOBJ) {} + SAPI::Result Validate(const std::string ¶meter, const UniValue &value) const override; + }; + + class Outputs : public Object{ + public: + Outputs() : Object() {} + SAPI::Result Validate(const std::string ¶meter, const UniValue &value) const final; + }; + + class Transaction : public Object{ + public: + Transaction() : Object() {} + SAPI::Result Validate(const std::string ¶meter, const UniValue &value) const final; + }; + + class Transactions : public Array{ + public: + Transactions() : Array() {} + SAPI::Result Validate(const std::string ¶meter, const UniValue &value) const final; + }; + std::string ResultMessage(SAPI::Codes value); } diff --git a/src/sapi/sapi_smartnodes.cpp b/src/sapi/sapi_smartnodes.cpp index 2cd132d5..326d4a86 100644 --- a/src/sapi/sapi_smartnodes.cpp +++ b/src/sapi/sapi_smartnodes.cpp @@ -167,7 +167,7 @@ static bool smartnodes_check_one(HTTPRequest* req, const std::map &mapPathParams, const UniValue &bodyParameter) { if( !bodyParameter.isArray() || bodyParameter.empty() ) - return SAPI::Error(req, HTTPStatus::BAD_REQUEST, "Addresses are expedted to be a JSON array: [ \"address\", ... ]"); + return SAPI::Error(req, HTTPStatus::BAD_REQUEST, "Addresses are expected to be a JSON array: [ \"address\", ... ]"); std::vector vecResults; std::vector vecInfos; diff --git a/src/sapi/sapi_smartrewards.cpp b/src/sapi/sapi_smartrewards.cpp index 03cd3497..8dfdf4bb 100644 --- a/src/sapi/sapi_smartrewards.cpp +++ b/src/sapi/sapi_smartrewards.cpp @@ -57,17 +57,17 @@ static bool CheckAddresses(HTTPRequest* req, std::vector vecAddr, s vecResults.clear(); - TRY_LOCK(cs_rewardrounds,roundsLocked); + TRY_LOCK(cs_rewardscache,cacheLocked); - if(!roundsLocked) return SAPI::Error(req, SAPI::RewardsDatabaseBusy, "Rewards database is busy..Try it again!"); + if(!cacheLocked) return SAPI::Error(req, SAPI::RewardsDatabaseBusy, "Rewards database is busy..Try it again!"); - const CSmartRewardRound& current = prewards->GetCurrentRound(); + const CSmartRewardRound *current = prewards->GetCurrentRound(); int nFirst_1_3_Round = Params().GetConsensus().nRewardsFirst_1_3_Round; for( auto addrStr : vecAddr ){ - CSmartAddress id = CSmartAddress(addrStr); + CSmartAddress id = CSmartAddress::Legacy(addrStr); if( !id.IsValid() ){ code = SAPI::InvalidSmartCashAddress; @@ -76,9 +76,9 @@ static bool CheckAddresses(HTTPRequest* req, std::vector vecAddr, s continue; } - CSmartRewardEntry entry; + CSmartRewardEntry *entry = nullptr; - if( !prewards->GetRewardEntry(id, entry) ){ + if( !prewards->GetRewardEntry(id, entry, false) ){ code = SAPI::AddressNotFound; std::string message = "Couldn't find this SmartCash address in the database."; errors.push_back(SAPI::Result(code, message)); @@ -88,11 +88,11 @@ static bool CheckAddresses(HTTPRequest* req, std::vector vecAddr, s UniValue obj(UniValue::VOBJ); obj.pushKV("address",id.ToString()); - obj.pushKV("balance",UniValueFromAmount(entry.balance)); - obj.pushKV("balance_eligible", UniValueFromAmount(entry.balanceEligible)); - obj.pushKV("is_smartnode", !entry.smartnodePaymentTx.IsNull()); - obj.pushKV("voted", !entry.voteProof.IsNull()); - obj.pushKV("eligible", current.number < nFirst_1_3_Round ? entry.balanceEligible > 0 : entry.IsEligible()); + obj.pushKV("balance",UniValueFromAmount(entry->balance)); + obj.pushKV("balance_eligible", UniValueFromAmount(entry->balanceEligible)); + obj.pushKV("is_smartnode", !entry->smartnodePaymentTx.IsNull()); + obj.pushKV("activated", entry->fActivated); + obj.pushKV("eligible", current->number < nFirst_1_3_Round ? entry->balanceEligible > 0 : entry->IsEligible()); vecResults.push_back(obj); } @@ -112,25 +112,25 @@ static bool smartrewards_current(HTTPRequest* req, const std::mapGetCurrentRound(); + const CSmartRewardRound *current = prewards->GetCurrentRound(); - if( !current.number ) return SAPI::Error(req, SAPI::NoActiveRewardRound, "No active reward round available yet."); + if( !current->number ) return SAPI::Error(req, SAPI::NoActiveRewardRound, "No active reward round available yet."); - obj.pushKV("rewards_cycle",current.number); - obj.pushKV("start_blockheight",current.startBlockHeight); - obj.pushKV("start_blocktime",current.startBlockTime); - obj.pushKV("end_blockheight",current.endBlockHeight); - obj.pushKV("end_blocktime",current.endBlockTime); - obj.pushKV("eligible_addresses",current.eligibleEntries - current.disqualifiedEntries); - obj.pushKV("eligible_smart",UniValueFromAmount(current.eligibleSmart - current.disqualifiedSmart)); - obj.pushKV("disqualified_addresses",current.disqualifiedEntries); - obj.pushKV("disqualified_smart",UniValueFromAmount(current.disqualifiedSmart)); - obj.pushKV("estimated_rewards",UniValueFromAmount(current.rewards)); - obj.pushKV("estimated_percent",current.percent); + obj.pushKV("rewards_cycle",current->number); + obj.pushKV("start_blockheight",current->startBlockHeight); + obj.pushKV("start_blocktime",current->startBlockTime); + obj.pushKV("end_blockheight",current->endBlockHeight); + obj.pushKV("end_blocktime",current->endBlockTime); + obj.pushKV("eligible_addresses",current->eligibleEntries - current->disqualifiedEntries); + obj.pushKV("eligible_smart",UniValueFromAmount(current->eligibleSmart - current->disqualifiedSmart)); + obj.pushKV("disqualified_addresses",current->disqualifiedEntries); + obj.pushKV("disqualified_smart",UniValueFromAmount(current->disqualifiedSmart)); + obj.pushKV("estimated_rewards",UniValueFromAmount(current->rewards)); + obj.pushKV("estimated_percent",current->percent * 100.0); SAPI::WriteReply(req, obj); @@ -142,52 +142,56 @@ static bool smartrewards_history(HTTPRequest* req, const std::mapGetRewardRounds(); + const CSmartRewardRoundMap* history = prewards->GetRewardRounds(); int64_t nPayoutDelay = Params().GetConsensus().nRewardsPayoutStartDelay; - if(!history.size()) return SAPI::Error(req, SAPI::NoFinishedRewardRound, "No finished reward round available yet."); + if(!history->size()) return SAPI::Error(req, SAPI::NoFinishedRewardRound, "No finished reward round available yet."); - BOOST_FOREACH(CSmartRewardRound round, history) { + auto round = history->begin(); + + while( round != history->end() ){ UniValue roundObj(UniValue::VOBJ); - roundObj.pushKV("rewards_cycle",round.number); - roundObj.pushKV("start_blockheight",round.startBlockHeight); - roundObj.pushKV("start_blocktime",round.startBlockTime); - roundObj.pushKV("end_blockheight",round.endBlockHeight); - roundObj.pushKV("end_blocktime",round.endBlockTime); - roundObj.pushKV("eligible_addresses",round.eligibleEntries - round.disqualifiedEntries); - roundObj.pushKV("eligible_smart",UniValueFromAmount(round.eligibleSmart - round.disqualifiedSmart)); - roundObj.pushKV("disqualified_addresses",round.disqualifiedEntries); - roundObj.pushKV("disqualified_smart",UniValueFromAmount(round.disqualifiedSmart)); - roundObj.pushKV("rewards",UniValueFromAmount(round.rewards)); - roundObj.pushKV("percent",round.percent); + roundObj.pushKV("rewards_cycle",round->second.number); + roundObj.pushKV("start_blockheight",round->second.startBlockHeight); + roundObj.pushKV("start_blocktime",round->second.startBlockTime); + roundObj.pushKV("end_blockheight",round->second.endBlockHeight); + roundObj.pushKV("end_blocktime",round->second.endBlockTime); + roundObj.pushKV("eligible_addresses",round->second.eligibleEntries - round->second.disqualifiedEntries); + roundObj.pushKV("eligible_smart",UniValueFromAmount(round->second.eligibleSmart - round->second.disqualifiedSmart)); + roundObj.pushKV("disqualified_addresses",round->second.disqualifiedEntries); + roundObj.pushKV("disqualified_smart",UniValueFromAmount(round->second.disqualifiedSmart)); + roundObj.pushKV("rewards",UniValueFromAmount(round->second.rewards)); + roundObj.pushKV("percent",round->second.percent * 100.0); UniValue payObj(UniValue::VOBJ); - int nPayeeCount = round.eligibleEntries - round.disqualifiedEntries; - int nBlockPayees = round.nBlockPayees; - int nPayoutInterval = round.nBlockInterval; + int nPayeeCount = round->second.eligibleEntries - round->second.disqualifiedEntries; + int nBlockPayees = round->second.nBlockPayees; + int nPayoutInterval = round->second.nBlockInterval; int nRewardBlocks = nPayeeCount / nBlockPayees; if( nPayeeCount % nBlockPayees ) nRewardBlocks += 1; - int nLastRoundBlock = round.endBlockHeight + nPayoutDelay + ( (nRewardBlocks - 1) * nPayoutInterval ); + int nLastRoundBlock = round->second.endBlockHeight + nPayoutDelay + ( (nRewardBlocks - 1) * nPayoutInterval ); - payObj.pushKV("firstBlock", round.endBlockHeight + nPayoutDelay ); + payObj.pushKV("firstBlock", round->second.endBlockHeight + nPayoutDelay ); payObj.pushKV("totalBlocks", nRewardBlocks ); payObj.pushKV("lastBlock", nLastRoundBlock ); payObj.pushKV("totalPayees", nPayeeCount); - payObj.pushKV("blockPayees", round.nBlockPayees); + payObj.pushKV("blockPayees", round->second.nBlockPayees); payObj.pushKV("lastBlockPayees", nPayeeCount % nBlockPayees ); - payObj.pushKV("blockInterval",round.nBlockInterval); + payObj.pushKV("blockInterval",round->second.nBlockInterval); roundObj.pushKV("payouts", payObj); obj.push_back(roundObj); + + ++round; } SAPI::WriteReply(req, obj); diff --git a/src/sapi/sapi_transaction.cpp b/src/sapi/sapi_transaction.cpp index 2dc017b5..f757e73d 100644 --- a/src/sapi/sapi_transaction.cpp +++ b/src/sapi/sapi_transaction.cpp @@ -14,6 +14,7 @@ extern void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool static bool transaction_send(HTTPRequest* req, const std::map &mapPathParams, const UniValue &bodyParameter); static bool transaction_check(HTTPRequest* req, const std::map &mapPathParams, const UniValue &bodyParameter); +static bool transaction_create(HTTPRequest* req, const std::map &mapPathParams, const UniValue &bodyParameter); SAPI::EndpointGroup transactionEndpoints = { "transaction", @@ -31,6 +32,14 @@ SAPI::EndpointGroup transactionEndpoints = { SAPI::BodyParameter(SAPI::Keys::instantpay, new SAPI::Validation::Bool(), true), SAPI::BodyParameter(SAPI::Keys::overridefees, new SAPI::Validation::Bool(), true) } + }, + { + "create", HTTPRequest::POST, UniValue::VOBJ, transaction_create, + { + SAPI::BodyParameter(SAPI::Keys::inputs, new SAPI::Validation::Transactions()), + SAPI::BodyParameter(SAPI::Keys::outputs, new SAPI::Validation::Outputs()), + SAPI::BodyParameter(SAPI::Keys::locktime, new SAPI::Validation::UInt(), true), + } } } }; @@ -196,3 +205,73 @@ static bool transaction_send(HTTPRequest* req, const std::map &mapPathParams, const UniValue &bodyParameter) +{ + UniValue inputs = bodyParameter[SAPI::Keys::inputs].get_array(); + UniValue sendTo = bodyParameter[SAPI::Keys::outputs].get_obj(); + CMutableTransaction rawTx; + + if (bodyParameter.exists(SAPI::Keys::locktime)) { + rawTx.nLockTime = bodyParameter[SAPI::Keys::locktime].get_int64(); + } + + 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()) + return SAPI::Error(req, SAPI::TxMissingVout, "Invalid parameter, missing vout key"); + int nOutput = vout_v.get_int(); + if (nOutput < 0) + return SAPI::Error(req, SAPI::TxInvalidParameter, "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()) + return SAPI::Error(req, SAPI::TxInvalidParameter, "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) { + if (name_ == "data") { + std::vector data = ParseHexV(sendTo[name_].getValStr(),"Data"); + + CTxOut out(0, CScript() << OP_RETURN << data); + rawTx.vout.push_back(out); + } else { + CBitcoinAddress address(name_); + if (!address.IsValid()) + return SAPI::Error(req, SAPI::TxInvalidParameter, string("Invalid SmartCash address: ") + name_); + + if (setAddress.count(address)) + return SAPI::Error(req, SAPI::TxInvalidParameter, 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); + } + } + + UniValue result(UniValue::VOBJ); + result.pushKV("hex", EncodeHexTx(rawTx)); + SAPI::WriteReply(req, result); + + return true; +} diff --git a/src/sapi/sapi_validation.cpp b/src/sapi/sapi_validation.cpp index 5fac6b24..76593f54 100644 --- a/src/sapi/sapi_validation.cpp +++ b/src/sapi/sapi_validation.cpp @@ -230,6 +230,75 @@ SAPI::Result SAPI::Validation::AmountRange::Validate(const string ¶meter, co return SAPI::Result(code, message); } +SAPI::Result SAPI::Validation::Array::Validate(const std::string ¶meter, const UniValue &value) const +{ + SAPI::Codes code = SAPI::Valid; + return SAPI::Result(code, ResultMessage(code)); +} + +SAPI::Result SAPI::Validation::Object::Validate(const std::string ¶meter, const UniValue &value) const +{ + SAPI::Codes code = SAPI::Valid; + return SAPI::Result(code, ResultMessage(code)); +} + +SAPI::Result SAPI::Validation::Outputs::Validate(const std::string ¶meter, const UniValue &value) const +{ + SAPI::Codes code = SAPI::Valid; + SAPI::Result result = Object::Validate(parameter, value); + if( result != SAPI::Valid ) return result; + + const UniValue &object = value.get_obj(); + vector outputList = object.getKeys(); + BOOST_FOREACH(const string& name_, outputList) { + if( name_ == "data" ) { + result = HexString().Validate(parameter, object[name_]); + if( result != SAPI::Valid ) return result; + } else { + result = SmartCashAddress().Validate(parameter, UniValue(name_)); + if( result != SAPI::Valid ) return result; + result = Amount().Validate(parameter, object[name_]); + if( result != SAPI::Valid ) return result; + } + } + + return SAPI::Result(code, ResultMessage(code)); +} + +SAPI::Result SAPI::Validation::Transaction::Validate(const std::string ¶meter, const UniValue &value) const +{ + SAPI::Codes code = SAPI::Valid; + SAPI::Result result = Object::Validate(parameter, value); + if( result != SAPI::Valid ) return result; + + const UniValue &object = value.get_obj(); + if( !object.exists("txid") ){ + code = SAPI::TxMissingTxId; + return SAPI::Result(code, ResultMessage(code)); + } + if( !object.exists("vout") ){ + code = SAPI::TxMissingVout; + return SAPI::Result(code, ResultMessage(code)); + } + + return SAPI::Result(code, ResultMessage(code)); +} + +SAPI::Result SAPI::Validation::Transactions::Validate(const std::string ¶meter, const UniValue &value) const +{ + SAPI::Codes code = SAPI::Valid; + SAPI::Result result = Array::Validate(parameter, value); + if( result != SAPI::Valid ) return result; + + const UniValue &array = value.get_array(); + for (unsigned int i = 0; i < array.size(); i++) { + result = Transaction().Validate(parameter, array[i]); + if( result != SAPI::Valid ) return result; + } + + return SAPI::Result(code, ResultMessage(code)); +} + std::string SAPI::Validation::ResultMessage(SAPI::Codes value) { switch(value){ @@ -255,7 +324,7 @@ std::string SAPI::Validation::ResultMessage(SAPI::Codes value) case DoubleOutOfRange: return "Double value out of the valid range: %8.8f - %8.8f"; case InvalidSmartCashAddress: - return "Invalid SmartCash"; + return "Invalid SmartCash address"; case EmptyString: return "String is empty"; case InvalidHexString: @@ -293,7 +362,7 @@ std::string SAPI::Validation::ResultMessage(SAPI::Codes value) return "No deposits available"; case NoUtxosAvailble: return "No unspent outpouts available"; - /* transaction errors */ + /* transaction errors */ case TxDecodeFailed: return "Transaction decode failed"; case TxNotSpecified: @@ -310,6 +379,10 @@ std::string SAPI::Validation::ResultMessage(SAPI::Codes value) return "Failed to relay transaction"; case TxNotFound: return "Transaction not found"; + case TxMissingTxId: + return "Missing 'txid' field in transaction"; + case TxMissingVout: + return "Missing 'vout' field in transaction"; /* smartreward errors */ case RewardsDatabaseBusy: return "SmartRewards database busy"; diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 9f29ee78..6ae998f9 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1262,7 +1262,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigne return set_error(serror, SCRIPT_ERR_EVAL_FALSE); // Additional validation for spend-to-script-hash transactions: - if ((flags & SCRIPT_VERIFY_P2SH) && scriptPubKey.IsPayToScriptHash()) + if ((flags & SCRIPT_VERIFY_P2SH) && ( scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToScriptHashLocked() ) ) { // scriptSig must be literals-only or validation fails if (!scriptSig.IsPushOnly()) diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index bf1b48cc..d8fba06e 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -51,18 +51,19 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) case TX_NONSTANDARD: case TX_NULL_DATA: break; - case TX_ZEROCOINMINT: case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); if (keystore.HaveKey(keyID)) return ISMINE_SPENDABLE; break; case TX_PUBKEYHASH: + case TX_PUBKEYHASHLOCKED: keyID = CKeyID(uint160(vSolutions[0])); if (keystore.HaveKey(keyID)) return ISMINE_SPENDABLE; break; case TX_SCRIPTHASH: + case TX_SCRIPTHASHLOCKED: { CScriptID scriptID = CScriptID(uint160(vSolutions[0])); CScript subscript; diff --git a/src/script/script.cpp b/src/script/script.cpp index c7a754e0..53c75602 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -218,6 +218,27 @@ bool CScript::IsPayToPublicKeyHash() const (*this)[24] == OP_CHECKSIG); } +bool CScript::IsPayToPublicKeyHashLocked() const +{ + // Extra-fast test for pay-to-pubkey-hash CScripts with OP_CHECKLOCKTIMEVERIFY: + + if( this->size() < 29 || this->size() > 33){ + return false; + } + + int nLockTimeLength = (*this)[0]; + int nOffset = nLockTimeLength + 1; + + return (nLockTimeLength >= 1 && nLockTimeLength <= 5 && + (*this)[nOffset + 0] == OP_CHECKLOCKTIMEVERIFY && + (*this)[nOffset + 1] == OP_DROP && + (*this)[nOffset + 2] == OP_DUP && + (*this)[nOffset + 3] == OP_HASH160 && + (*this)[nOffset + 4] == 0x14 && + (*this)[nOffset + 25] == OP_EQUALVERIFY && + (*this)[nOffset + 26] == OP_CHECKSIG); +} + bool CScript::IsPayToPublicKey() const { // Extra-fast test for pay-to-pubkey CScripts: @@ -259,6 +280,25 @@ bool CScript::IsPayToScriptHash() const (*this)[22] == OP_EQUAL); } +bool CScript::IsPayToScriptHashLocked() const +{ + // Extra-fast test for pay-to-script-hash CScripts with OP_CHECKLOCKTIMEVERIFY: + + if( this->size() < 26 || this->size() > 31){ + return false; + } + + int nLockTimeLength = (*this)[0]; + int nOffset = nLockTimeLength + 1; + + return (nLockTimeLength >= 1 && nLockTimeLength <= 5 && + (*this)[nOffset + 0] == OP_CHECKLOCKTIMEVERIFY && + (*this)[nOffset + 1] == OP_DROP && + (*this)[nOffset + 2] == OP_HASH160 && + (*this)[nOffset + 3] == 0x14 && + (*this)[nOffset + 24] == OP_EQUAL); +} + bool CScript::IsPayToWitnessScriptHash() const { // Extra-fast test for pay-to-witness-script-hash CScripts: @@ -313,29 +353,6 @@ bool CScript::IsVoteKeyData() const { (*this)[4] == 0x02); } -bool CScript::IsVoteProofData() const { - - return (this->size() == REWARDS_VOTEPROOF_O1_SCRIPT_SIZE && - (*this)[0] == OP_RETURN && - (*this)[1] == REWARDS_VOTEPROOF_O1_DATA_SIZE && - (*this)[2] == OP_RETURN_VOTE_PROOF_FLAG && - (*this)[3] == 0x01); - - /* - return (this->size() == REWARDS_VOTEPROOF_O1_SCRIPT_SIZE && - (*this)[0] == OP_RETURN && - (*this)[1] == REWARDS_VOTEPROOF_O1_DATA_SIZE && - (*this)[2] == OP_RETURN_VOTE_PROOF_FLAG && - (*this)[3] == 0x01) || - - (this->size() == REWARDS_VOTEPROOF_O2_SCRIPT_SIZE && - (*this)[0] == OP_RETURN && - (*this)[1] == REWARDS_VOTEPROOF_O2_DATA_SIZE && - (*this)[2] == OP_RETURN_VOTE_PROOF_FLAG && - (*this)[3] == 0x02); - */ -} - bool CScript::HasCanonicalPushes() const { const_iterator pc = begin(); diff --git a/src/script/script.h b/src/script/script.h index eaaf23e2..ccf62e1a 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -631,18 +631,19 @@ class CScript : public CScriptBase unsigned int GetSigOpCount(const CScript& scriptSig) const; bool IsPayToPublicKeyHash() const; + bool IsPayToPublicKeyHashLocked() const; bool IsPayToPublicKey() const; bool IsNormalPaymentScript() const; bool IsPayToScriptHash() const; + bool IsPayToScriptHashLocked() const; bool IsPayToWitnessScriptHash() const; bool IsWitnessProgram(int& version, std::vector& program) const; - + bool IsZerocoinMint() const; bool IsZerocoinSpend() const; bool IsVoteKeyData() const; - bool IsVoteProofData() const; // Called by IsStandardTx. bool HasCanonicalPushes() const; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index b9c61f6c..e87d0ebc 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -59,7 +59,7 @@ static bool SignN(const vector& multisigdata, const BaseSignatureCreato /** * Sign scriptPubKey using signature made with creator. * Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed), - * unless whichTypeRet is TX_SCRIPTHASH, in which case scriptSigRet is the redemption script. + * unless whichTypeRet is TX_SCRIPTHASH or TX_SCRIPTHASHLOCKED, in which case scriptSigRet is the redemption script. * Returns false if scriptPubKey could not be completely satisfied. */ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptPubKey, @@ -77,11 +77,11 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP case TX_NONSTANDARD: case TX_NULL_DATA: return false; - case TX_ZEROCOINMINT: case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); return Sign1(keyID, creator, scriptPubKey, scriptSigRet); case TX_PUBKEYHASH: + case TX_PUBKEYHASHLOCKED: keyID = CKeyID(uint160(vSolutions[0])); if (!Sign1(keyID, creator, scriptPubKey, scriptSigRet)) return false; @@ -93,6 +93,7 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP } return true; case TX_SCRIPTHASH: + case TX_SCRIPTHASHLOCKED: return creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptSigRet); case TX_MULTISIG: @@ -108,7 +109,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu if (!SignStep(creator, fromPubKey, scriptSig, whichType)) return false; - if (whichType == TX_SCRIPTHASH) + if (whichType == TX_SCRIPTHASH || whichType == TX_SCRIPTHASHLOCKED) { // Solver returns the subscript that need to be evaluated; // the final scriptSig is the signatures from that @@ -223,14 +224,15 @@ static CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatur if (sigs1.size() >= sigs2.size()) return PushAll(sigs1); return PushAll(sigs2); - case TX_ZEROCOINMINT: case TX_PUBKEY: case TX_PUBKEYHASH: + case TX_PUBKEYHASHLOCKED: // Signatures are bigger than placeholders or empty scripts: if (sigs1.empty() || sigs1[0].empty()) return PushAll(sigs2); return PushAll(sigs1); case TX_SCRIPTHASH: + case TX_SCRIPTHASHLOCKED: if (sigs1.empty() || sigs1.back().empty()) return PushAll(sigs2); else if (sigs2.empty() || sigs2.back().empty()) diff --git a/src/script/standard.cpp b/src/script/standard.cpp index ca2d71bf..7a41636c 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -28,10 +28,11 @@ const char* GetTxnOutputType(txnouttype t) case TX_NONSTANDARD: return "nonstandard"; case TX_PUBKEY: return "pubkey"; case TX_PUBKEYHASH: return "pubkeyhash"; + case TX_PUBKEYHASHLOCKED: return "pubkeyhashlocked"; case TX_SCRIPTHASH: return "scripthash"; + case TX_SCRIPTHASHLOCKED: return "scripthashlocked"; case TX_MULTISIG: return "multisig"; case TX_NULL_DATA: return "nulldata"; - case TX_ZEROCOINMINT: return "zerocoinmint"; } return NULL; } @@ -57,6 +58,17 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector hashBytes(scriptPubKey.begin() + nOffset, scriptPubKey.begin() + nOffset + 20); + vSolutionsRet.push_back(hashBytes); + return true; + } + // Shortcut for pay-to-script-hash, which are more constrained than the other types: // it is always OP_HASH160 20 [20 byte hash] OP_EQUAL if (scriptPubKey.IsPayToScriptHash()) @@ -67,11 +79,13 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector 150) return false; - vector hashBytes(scriptPubKey.begin()+2, scriptPubKey.end()); + int nOffset = scriptPubKey[0] + 5; + + typeRet = TX_SCRIPTHASHLOCKED; + vector hashBytes(scriptPubKey.begin() + nOffset, scriptPubKey.begin() + nOffset + 20); vSolutionsRet.push_back(hashBytes); return true; } @@ -187,12 +201,12 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) addressRet = pubKey.GetID(); return true; } - else if (whichType == TX_PUBKEYHASH) + else if (whichType == TX_PUBKEYHASH || whichType == TX_PUBKEYHASHLOCKED) { addressRet = CKeyID(uint160(vSolutions[0])); return true; } - else if (whichType == TX_SCRIPTHASH) + else if (whichType == TX_SCRIPTHASH || whichType == TX_SCRIPTHASHLOCKED) { addressRet = CScriptID(uint160(vSolutions[0])); return true; @@ -269,6 +283,36 @@ class CScriptVisitor : public boost::static_visitor }; } + +namespace +{ +class CLockedScriptVisitor : public boost::static_visitor +{ +private: + CScript *script; + int nLockTime; +public: + CLockedScriptVisitor(CScript *scriptin, int nLockTime) : script(scriptin), nLockTime(nLockTime) { } + + bool operator()(const CNoDestination &dest) const { + script->clear(); + return false; + } + + bool operator()(const CKeyID &keyID) const { + script->clear(); + *script << nLockTime << OP_CHECKLOCKTIMEVERIFY << OP_DROP << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG; + return true; + } + + bool operator()(const CScriptID &scriptID) const { + script->clear(); + *script << nLockTime << OP_CHECKLOCKTIMEVERIFY << OP_DROP << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL; + return true; + } +}; +} + CScript GetScriptForDestination(const CTxDestination& dest) { CScript script; @@ -277,6 +321,14 @@ CScript GetScriptForDestination(const CTxDestination& dest) return script; } +CScript GetLockedScriptForDestination(const CTxDestination& dest, int nLockTime) +{ + CScript script; + + boost::apply_visitor(CLockedScriptVisitor(&script, nLockTime), dest); + return script; +} + CScript GetScriptForRawPubKey(const CPubKey& pubKey) { return CScript() << std::vector(pubKey.begin(), pubKey.end()) << OP_CHECKSIG; diff --git a/src/script/standard.h b/src/script/standard.h index 385ab73d..4374baa0 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -48,10 +48,11 @@ enum txnouttype // 'standard' transaction types: TX_PUBKEY, TX_PUBKEYHASH, + TX_PUBKEYHASHLOCKED, TX_SCRIPTHASH, + TX_SCRIPTHASHLOCKED, TX_MULTISIG, - TX_NULL_DATA, - TX_ZEROCOINMINT + TX_NULL_DATA }; class CNoDestination { @@ -76,6 +77,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector& addressRet, int& nRequiredRet); CScript GetScriptForDestination(const CTxDestination& dest); +CScript GetLockedScriptForDestination(const CTxDestination& dest, int nLockTime); CScript GetScriptForRawPubKey(const CPubKey& pubkey); CScript GetScriptForMultisig(int nRequired, const std::vector& keys); CScript GetScriptForWitness(const CScript& redeemscript); diff --git a/src/smarthive/hive.cpp b/src/smarthive/hive.cpp index 20318f5d..5264ee0a 100644 --- a/src/smarthive/hive.cpp +++ b/src/smarthive/hive.cpp @@ -16,47 +16,67 @@ void SmartHive::Init() if( init ) return; addressesMainnet = { - { SmartHive::ProjectTreasury, new CSmartAddress("SXun9XDHLdBhG4Yd1ueZfLfRpC9kZgwT1b") }, - { SmartHive::Support, new CSmartAddress("SW2FbVaBhU1Www855V37auQzGQd8fuLR9x") }, - { SmartHive::Development, new CSmartAddress("SPusYr5tUdUyRXevJg7pnCc9Sm4HEzaYZF") }, - { SmartHive::Outreach, new CSmartAddress("Siim7T5zMH3he8xxtQzhmHs4CQSuMrCV1M") }, - { SmartHive::SmartRewards, new CSmartAddress("SU5bKb35xUV8aHG5dNarWHB3HBVjcCRjYo") }, - { SmartHive::Outreach2, new CSmartAddress("SNxFyszmGEAa2n2kQbzw7gguHa5a4FC7Ay") }, - { SmartHive::Web, new CSmartAddress("Sgq5c4Rznibagv1aopAfPA81jac392scvm") }, - { SmartHive::Quality, new CSmartAddress("Sc61Gc2wivtuGd6recqVDqv4R38TcHqFS8") } + { SmartHive::ProjectTreasury_Legacy, new CSmartAddress("SXun9XDHLdBhG4Yd1ueZfLfRpC9kZgwT1b") }, + { SmartHive::Support_Legacy, new CSmartAddress("SW2FbVaBhU1Www855V37auQzGQd8fuLR9x") }, + { SmartHive::Development_Legacy, new CSmartAddress("SPusYr5tUdUyRXevJg7pnCc9Sm4HEzaYZF") }, + { SmartHive::Outreach_Legacy, new CSmartAddress("Siim7T5zMH3he8xxtQzhmHs4CQSuMrCV1M") }, + { SmartHive::SmartRewards_Legacy, new CSmartAddress("SU5bKb35xUV8aHG5dNarWHB3HBVjcCRjYo") }, + { SmartHive::Outreach2_Legacy, new CSmartAddress("SNxFyszmGEAa2n2kQbzw7gguHa5a4FC7Ay") }, + { SmartHive::Web_Legacy, new CSmartAddress("Sgq5c4Rznibagv1aopAfPA81jac392scvm") }, + { SmartHive::Quality_Legacy, new CSmartAddress("Sc61Gc2wivtuGd6recqVDqv4R38TcHqFS8") }, + + { SmartHive::Support, new CSmartAddress("TBD") }, + { SmartHive::Development, new CSmartAddress("TBD") }, + { SmartHive::Outreach, new CSmartAddress("TBD") }, + { SmartHive::SmartHub, new CSmartAddress("TBD") }, }; addressesTestnet = { - { SmartHive::ProjectTreasury, new CSmartAddress("TTpGqTr2PBeVx4vvNRJ9iTq4NwpTCbSSwy") }, - { SmartHive::Support, new CSmartAddress("THypUznpFaDHaE7PS6yAc4pHNjC2BnWzUv") }, - { SmartHive::Development, new CSmartAddress("TDJVZE5oCYYbJQyizU4FgB2KpnKVdebnxg") }, - { SmartHive::Outreach, new CSmartAddress("TSziXCdaBcPk3Dt94BbTH9BZDH18K6sWsc") }, - { SmartHive::SmartRewards, new CSmartAddress("TLn1PGAVccBBjF8JuhQmATCR8vxhmamJg8") }, - { SmartHive::Outreach2, new CSmartAddress("TCi1wcVbkmpUiTcG277o5Y3VeD3zgtsHRD") }, - { SmartHive::Web, new CSmartAddress("TBWBQ1rCXm16huegLWvSz5TCs5KzfoYaNB") }, - { SmartHive::Quality, new CSmartAddress("TVuTV7d5vBKyfg5j45RnnYgdo9G3ET2t2f") } + { SmartHive::ProjectTreasury_Legacy, new CSmartAddress("TTpGqTr2PBeVx4vvNRJ9iTq4NwpTCbSSwy") }, + { SmartHive::Support_Legacy, new CSmartAddress("THypUznpFaDHaE7PS6yAc4pHNjC2BnWzUv") }, + { SmartHive::Development_Legacy, new CSmartAddress("TDJVZE5oCYYbJQyizU4FgB2KpnKVdebnxg") }, + { SmartHive::Outreach_Legacy, new CSmartAddress("TSziXCdaBcPk3Dt94BbTH9BZDH18K6sWsc") }, + { SmartHive::SmartRewards_Legacy, new CSmartAddress("TLn1PGAVccBBjF8JuhQmATCR8vxhmamJg8") }, + { SmartHive::Outreach2_Legacy, new CSmartAddress("TCi1wcVbkmpUiTcG277o5Y3VeD3zgtsHRD") }, + { SmartHive::Web_Legacy, new CSmartAddress("TBWBQ1rCXm16huegLWvSz5TCs5KzfoYaNB") }, + { SmartHive::Quality_Legacy, new CSmartAddress("TVuTV7d5vBKyfg5j45RnnYgdo9G3ET2t2f") }, + + { SmartHive::Support, new CSmartAddress("6Tr3PdsFSm3DfN2b8vQ4Eqo7LzvZ238yXt") }, + { SmartHive::Development, new CSmartAddress("6VE4Qzox3pEXtPLYhroepY9oiMS8YAgmJ9") }, + { SmartHive::Outreach, new CSmartAddress("6WNuCbGoM9ZeMYdW7uXwxNV7u4mgmBKmVY") }, + { SmartHive::SmartHub, new CSmartAddress("6bF1bs7A9eth2zuZqNQmCGB2jeap7fZnUE") }, }; scriptsMainnet = { - { SmartHive::ProjectTreasury, new CScript(std::move(addressesMainnet.at(SmartHive::ProjectTreasury)->GetScript())) }, // SmartHive treasure - { SmartHive::Support, new CScript(std::move(addressesMainnet.at(SmartHive::Support)->GetScript())) }, // Support hive - { SmartHive::Development, new CScript(std::move(addressesMainnet.at(SmartHive::Development)->GetScript())) }, // Development hive - { SmartHive::Outreach, new CScript(std::move(addressesMainnet.at(SmartHive::Outreach)->GetScript())) }, // Outreach hive - { SmartHive::SmartRewards, new CScript(std::move(addressesMainnet.at(SmartHive::SmartRewards)->GetScript())) }, // Legacy smartrewards - { SmartHive::Outreach2, new CScript(std::move(addressesMainnet.at(SmartHive::Outreach2)->GetScript())) }, // New hive 1 - { SmartHive::Web, new CScript(std::move(addressesMainnet.at(SmartHive::Web)->GetScript())) }, // New hive 2 - { SmartHive::Quality, new CScript(std::move(addressesMainnet.at(SmartHive::Quality)->GetScript())) } // New hive 3 + { SmartHive::ProjectTreasury_Legacy, new CScript(std::move(addressesMainnet.at(SmartHive::ProjectTreasury_Legacy)->GetScript())) }, // SmartHive treasury + { SmartHive::Support_Legacy, new CScript(std::move(addressesMainnet.at(SmartHive::Support_Legacy)->GetScript())) }, // Support hive + { SmartHive::Development_Legacy, new CScript(std::move(addressesMainnet.at(SmartHive::Development_Legacy)->GetScript())) }, // Development hive + { SmartHive::Outreach_Legacy, new CScript(std::move(addressesMainnet.at(SmartHive::Outreach_Legacy)->GetScript())) }, // Outreach hive + { SmartHive::SmartRewards_Legacy, new CScript(std::move(addressesMainnet.at(SmartHive::SmartRewards_Legacy)->GetScript())) }, // Legacy smartrewards + { SmartHive::Outreach2_Legacy, new CScript(std::move(addressesMainnet.at(SmartHive::Outreach2_Legacy)->GetScript())) }, // New hive 1 + { SmartHive::Web_Legacy, new CScript(std::move(addressesMainnet.at(SmartHive::Web_Legacy)->GetScript())) }, // New hive 2 + { SmartHive::Quality_Legacy, new CScript(std::move(addressesMainnet.at(SmartHive::Quality_Legacy)->GetScript())) }, // New hive 3 + + { SmartHive::Support, new CScript(std::move(addressesMainnet.at(SmartHive::Support)->GetScript())) }, // Support hive multisig + { SmartHive::Development, new CScript(std::move(addressesMainnet.at(SmartHive::Development)->GetScript())) }, // Development hive multisig + { SmartHive::Outreach, new CScript(std::move(addressesMainnet.at(SmartHive::Outreach)->GetScript())) }, // Outreach hive multisig + { SmartHive::SmartHub, new CScript(std::move(addressesMainnet.at(SmartHive::SmartHub)->GetScript())) }, // Outreach hive multisig }; scriptsTestnet = { - { SmartHive::ProjectTreasury, new CScript(std::move(addressesTestnet.at(SmartHive::ProjectTreasury)->GetScript())) }, // SmartHive treasure - { SmartHive::Support, new CScript(std::move(addressesTestnet.at(SmartHive::Support)->GetScript())) }, // Support hive - { SmartHive::Development, new CScript(std::move(addressesTestnet.at(SmartHive::Development)->GetScript())) }, // Development hive - { SmartHive::Outreach, new CScript(std::move(addressesTestnet.at(SmartHive::Outreach)->GetScript())) }, // Outreach hive - { SmartHive::SmartRewards, new CScript(std::move(addressesTestnet.at(SmartHive::SmartRewards)->GetScript())) }, // Legacy smartrewards - { SmartHive::Outreach2, new CScript(std::move(addressesTestnet.at(SmartHive::Outreach2)->GetScript())) }, // New hive 1 - { SmartHive::Web, new CScript(std::move(addressesTestnet.at(SmartHive::Web)->GetScript())) }, // New hive 2 - { SmartHive::Quality, new CScript(std::move(addressesTestnet.at(SmartHive::Quality)->GetScript())) } // New hive 3 + { SmartHive::ProjectTreasury_Legacy, new CScript(std::move(addressesTestnet.at(SmartHive::ProjectTreasury_Legacy)->GetScript())) }, // SmartHive treasure + { SmartHive::Support_Legacy, new CScript(std::move(addressesTestnet.at(SmartHive::Support_Legacy)->GetScript())) }, // Support hive + { SmartHive::Development_Legacy, new CScript(std::move(addressesTestnet.at(SmartHive::Development_Legacy)->GetScript())) }, // Development hive + { SmartHive::Outreach_Legacy, new CScript(std::move(addressesTestnet.at(SmartHive::Outreach_Legacy)->GetScript())) }, // Outreach hive + { SmartHive::SmartRewards_Legacy, new CScript(std::move(addressesTestnet.at(SmartHive::SmartRewards_Legacy)->GetScript())) }, // Legacy smartrewards + { SmartHive::Outreach2_Legacy, new CScript(std::move(addressesTestnet.at(SmartHive::Outreach2_Legacy)->GetScript())) }, // New hive 1 + { SmartHive::Web_Legacy, new CScript(std::move(addressesTestnet.at(SmartHive::Web_Legacy)->GetScript())) }, // New hive 2 + { SmartHive::Quality_Legacy, new CScript(std::move(addressesTestnet.at(SmartHive::Quality_Legacy)->GetScript())) }, // New hive 3 + + { SmartHive::Support, new CScript(std::move(addressesTestnet.at(SmartHive::Support)->GetScript())) }, // Support hive multisig + { SmartHive::Development, new CScript(std::move(addressesTestnet.at(SmartHive::Development)->GetScript())) }, // Development hive multisig + { SmartHive::Outreach, new CScript(std::move(addressesTestnet.at(SmartHive::Outreach)->GetScript())) }, // Outreach hive multisig + { SmartHive::SmartHub, new CScript(std::move(addressesTestnet.at(SmartHive::SmartHub)->GetScript())) }, // Outreach hive multisig }; init = true; @@ -108,3 +128,29 @@ const CSmartAddress& SmartHive::Address(SmartHive::Payee payee) return *ptr->at(payee); } + +CSmartAddress CSmartAddress::Legacy(const CSmartAddress &address) +{ + if( address.IsValid(CChainParams::PUBKEY_ADDRESS_V2) ){ + return CSmartAddress(address.ToString(false)); + } + if( address.IsValid(CChainParams::SCRIPT_ADDRESS_V2) ){ + return CSmartAddress(address.ToString(false)); + } + + return address; +} + +CSmartAddress CSmartAddress::Legacy(const std::string &strAddress) +{ + CSmartAddress address(strAddress); + + if( address.IsValid(CChainParams::PUBKEY_ADDRESS_V2) ){ + return CSmartAddress(address.ToString(false)); + } + if( address.IsValid(CChainParams::SCRIPT_ADDRESS_V2) ){ + return CSmartAddress(address.ToString(false)); + } + + return address; +} diff --git a/src/smarthive/hive.h b/src/smarthive/hive.h index 684e696e..904410b4 100644 --- a/src/smarthive/hive.h +++ b/src/smarthive/hive.h @@ -37,19 +37,26 @@ struct CSmartAddress : public CBitcoinAddress } CScript GetScript() const { return GetScriptForDestination(Get()); } + + static CSmartAddress Legacy(const CSmartAddress &address); + static CSmartAddress Legacy(const std::string &strAddress); }; namespace SmartHive{ enum Payee{ + Development_Legacy, //Deprecated with 1.3 + Outreach_Legacy, //Deprecated with 1.3 + Support_Legacy, //Deprecated with 1.3 + SmartRewards_Legacy, //Deprecated with 1.2 + ProjectTreasury_Legacy, //Deprecated with 1.3 + Outreach2_Legacy, //Deprecated with 1.3 + Web_Legacy, //Deprecated with 1.3 + Quality_Legacy, //Deprecated with 1.3 Development, Outreach, Support, - SmartRewards, //Deprecated with 1.2 - ProjectTreasury, - Outreach2, - Web, - Quality + SmartHub, }; const CScript* ScriptPtr(SmartHive::Payee payee); diff --git a/src/smarthive/hivepayments.cpp b/src/smarthive/hivepayments.cpp index 58f90c38..f2dd0372 100644 --- a/src/smarthive/hivepayments.cpp +++ b/src/smarthive/hivepayments.cpp @@ -13,10 +13,12 @@ static CSmartHiveSplit *hiveSplitInitial = nullptr; static CSmartHiveSplit *hiveSplit_1_0 = nullptr; static CSmartHiveSplit *hiveSplit_1_1 = nullptr; static CSmartHiveSplit *hiveSplit_1_2 = nullptr; +static CSmartHiveSplit *hiveSplit_1_3 = nullptr; static CSmartHiveSplit *hiveSplitDisabled = nullptr; static CSmartHiveSplit *hiveSplitInvalid_1_0 = nullptr; static int nPayoutInterval_1_2; +static int nPayoutInterval_1_3; void SmartHivePayments::Init() { @@ -26,50 +28,64 @@ void SmartHivePayments::Init() hiveSplitInitial = new CSmartHiveClassicSplit { 95, // Split 95% of the block reward as followed. { - new CSmartHiveClassic(SmartHive::Outreach, 0.08), - new CSmartHiveClassic(SmartHive::Support, 0.08), - new CSmartHiveClassic(SmartHive::Development, 0.08), - new CSmartHiveClassic(SmartHive::SmartRewards, 0.15), - new CSmartHiveClassic(SmartHive::ProjectTreasury, 0.56) + new CSmartHiveClassic(SmartHive::Outreach_Legacy, 0.08), + new CSmartHiveClassic(SmartHive::Support_Legacy, 0.08), + new CSmartHiveClassic(SmartHive::Development_Legacy, 0.08), + new CSmartHiveClassic(SmartHive::SmartRewards_Legacy, 0.15), + new CSmartHiveClassic(SmartHive::ProjectTreasury_Legacy, 0.56) } }; hiveSplit_1_0 = new CSmartHiveRotationSplit( 95, // Split 95% of the block reward as followed. { - new CSmartHiveRotation(SmartHive::Outreach, 0,7), - new CSmartHiveRotation(SmartHive::Support, 8,15), - new CSmartHiveRotation(SmartHive::Development, 16,23), - new CSmartHiveRotation(SmartHive::SmartRewards, 24,38), - new CSmartHiveRotation(SmartHive::ProjectTreasury, 39,94) + new CSmartHiveRotation(SmartHive::Outreach_Legacy, 0,7), + new CSmartHiveRotation(SmartHive::Support_Legacy, 8,15), + new CSmartHiveRotation(SmartHive::Development_Legacy, 16,23), + new CSmartHiveRotation(SmartHive::SmartRewards_Legacy, 24,38), + new CSmartHiveRotation(SmartHive::ProjectTreasury_Legacy, 39,94) } ); hiveSplit_1_1 = new CSmartHiveRotationSplit( 85, // Split 85% of the block reward as followed. { - new CSmartHiveRotation(SmartHive::Outreach, 0,7), - new CSmartHiveRotation(SmartHive::Support, 8,15), - new CSmartHiveRotation(SmartHive::Development, 16,23), - new CSmartHiveRotation(SmartHive::SmartRewards, 24,38), - new CSmartHiveRotation(SmartHive::ProjectTreasury, 39,84) + new CSmartHiveRotation(SmartHive::Outreach_Legacy, 0,7), + new CSmartHiveRotation(SmartHive::Support_Legacy, 8,15), + new CSmartHiveRotation(SmartHive::Development_Legacy, 16,23), + new CSmartHiveRotation(SmartHive::SmartRewards_Legacy, 24,38), + new CSmartHiveRotation(SmartHive::ProjectTreasury_Legacy, 39,84) } ); if(MainNet()) nPayoutInterval_1_2 = 1000; else nPayoutInterval_1_2 = 25; + if(MainNet()) nPayoutInterval_1_3 = 10000; + else nPayoutInterval_1_3 = 50; + hiveSplit_1_2 = new CSmartHiveBatchSplit( 70, // Split 70% of the block reward as followed. nPayoutInterval_1_2, // Trigger the payouts every n blocks { - new CSmartHiveClassic(SmartHive::Outreach, 0.04), - new CSmartHiveClassic(SmartHive::Support, 0.04), - new CSmartHiveClassic(SmartHive::Development, 0.04), - new CSmartHiveClassic(SmartHive::Outreach2, 0.04), - new CSmartHiveClassic(SmartHive::Web, 0.04), - new CSmartHiveClassic(SmartHive::Quality, 0.04), - new CSmartHiveClassic(SmartHive::ProjectTreasury, 0.46), + new CSmartHiveClassic(SmartHive::Outreach_Legacy, 0.04), + new CSmartHiveClassic(SmartHive::Support_Legacy, 0.04), + new CSmartHiveClassic(SmartHive::Development_Legacy, 0.04), + new CSmartHiveClassic(SmartHive::Outreach2_Legacy, 0.04), + new CSmartHiveClassic(SmartHive::Web_Legacy, 0.04), + new CSmartHiveClassic(SmartHive::Quality_Legacy, 0.04), + new CSmartHiveClassic(SmartHive::ProjectTreasury_Legacy, 0.46), + } + ); + + hiveSplit_1_3 = new CSmartHiveBatchSplit( + 29, // Split 29% of the block reward as followed. + nPayoutInterval_1_3, // Trigger the payouts every n blocks + { + new CSmartHiveClassic(SmartHive::Outreach, 0.0725), + new CSmartHiveClassic(SmartHive::Support, 0.0725), + new CSmartHiveClassic(SmartHive::Development, 0.0725), + new CSmartHiveClassic(SmartHive::SmartHub, 0.0725), } ); @@ -79,78 +95,6 @@ void SmartHivePayments::Init() init = true; } -const CSmartHiveSplit * Get_1_2_Split(int nHeight) -{ - static int64_t nLastPayNewHives = -1; - static CSmartHiveSplit* sporked_Split_1_2 = nullptr; - - int64_t nPayNewHives = 0; - int64_t nPayOutreachSpork = sporkManager.GetSporkValue(SPORK_18_PAY_OUTREACH2); - int64_t nPayWebSpork = sporkManager.GetSporkValue(SPORK_19_PAY_WEB); - int64_t nPayQualitySpork = sporkManager.GetSporkValue(SPORK_20_PAY_QUALITY); - - if( !nPayOutreachSpork || nHeight < nPayOutreachSpork ){ - nPayNewHives |= SmartHivePayments::OUTREACH2_ENABLED; - } - - if( !nPayWebSpork || nHeight < nPayWebSpork ){ - nPayNewHives |= SmartHivePayments::WEB_ENABLED; - } - - if( !nPayQualitySpork || nHeight < nPayQualitySpork ){ - nPayNewHives |= SmartHivePayments::QUALITY_ENABLED; - } - - // If one of the hives is disabled. - if( (nPayNewHives & SmartHivePayments::NEW_HIVES_ENABLED) != SmartHivePayments::NEW_HIVES_ENABLED ){ - - if( nLastPayNewHives == -1 || nLastPayNewHives != nPayNewHives ){ - - nLastPayNewHives = nPayNewHives; - - delete sporked_Split_1_2; - - std::vector sporkedHives_1_2; - - if( nPayNewHives & SmartHivePayments::OUTREACH2_ENABLED ){ - sporkedHives_1_2.push_back(new CSmartHiveClassic(SmartHive::Outreach, 0.04)); - sporkedHives_1_2.push_back(new CSmartHiveClassic(SmartHive::Outreach2, 0.04)); - }else{ - sporkedHives_1_2.push_back(new CSmartHiveClassic(SmartHive::Outreach, 0.08)); - } - - if( nPayNewHives & SmartHivePayments::WEB_ENABLED ){ - sporkedHives_1_2.push_back(new CSmartHiveClassic(SmartHive::Development, 0.04)); - sporkedHives_1_2.push_back(new CSmartHiveClassic(SmartHive::Web, 0.04)); - }else{ - sporkedHives_1_2.push_back(new CSmartHiveClassic(SmartHive::Development, 0.08)); - } - - if( nPayNewHives & SmartHivePayments::QUALITY_ENABLED ){ - sporkedHives_1_2.push_back(new CSmartHiveClassic(SmartHive::Support, 0.04)); - sporkedHives_1_2.push_back(new CSmartHiveClassic(SmartHive::Quality, 0.04)); - }else{ - sporkedHives_1_2.push_back(new CSmartHiveClassic(SmartHive::Support, 0.08)); - } - - sporkedHives_1_2.push_back(new CSmartHiveClassic(SmartHive::ProjectTreasury, 0.46)); - - sporked_Split_1_2 = new CSmartHiveBatchSplit( - 70, // Split 70% of the block reward as followed. - nPayoutInterval_1_2, // Trigger the payouts every n blocks - sporkedHives_1_2 - ); - - } - - return sporked_Split_1_2; - } - - // Else us the normal 1.2 split. - return hiveSplit_1_2; -} - - const CSmartHiveSplit * GetHiveSplit(int nHeight, int64_t blockTime) { @@ -165,18 +109,22 @@ const CSmartHiveSplit * GetHiveSplit(int nHeight, int64_t blockTime) return hiveSplit_1_0; }else if ( nHeight >= HF_V1_1_SMARTNODE_HEIGHT && nHeight < HF_V1_2_SMARTREWARD_HEIGHT ) { return hiveSplit_1_1; - }else if ( (nHeight >= HF_V1_2_SMARTREWARD_HEIGHT) && nHeight < HF_CHAIN_REWARD_END_HEIGHT ) { - return Get_1_2_Split(nHeight); + }else if ( (nHeight >= HF_V1_2_SMARTREWARD_HEIGHT) && nHeight < HF_V1_3_HEIGHT ) { + return hiveSplit_1_2; + }else if ( (nHeight >= HF_V1_3_HEIGHT) && nHeight < HF_CHAIN_REWARD_END_HEIGHT ) { + return hiveSplit_1_3; }else if(nHeight <= 1 || nHeight >= HF_CHAIN_REWARD_END_HEIGHT){ return hiveSplitDisabled; } }else{ - if ( nHeight <= TESTNET_V1_2_PAYMENTS_HEIGHT ) { + if ( nHeight < TESTNET_V1_2_8_PAYMENTS_HEIGHT ) { return hiveSplit_1_1; - }else if ( nHeight > TESTNET_V1_2_PAYMENTS_HEIGHT && nHeight < HF_CHAIN_REWARD_END_HEIGHT ) { - return Get_1_2_Split(nHeight); + }else if ( nHeight >= TESTNET_V1_2_8_PAYMENTS_HEIGHT && nHeight < TESTNET_V1_3_HEIGHT ) { + return hiveSplit_1_2; + }else if ( nHeight >= TESTNET_V1_3_HEIGHT && nHeight < HF_CHAIN_REWARD_END_HEIGHT ) { + return hiveSplit_1_3; }else if(nHeight >= HF_CHAIN_REWARD_END_HEIGHT){ return hiveSplitDisabled; } diff --git a/src/smartmining/miningpayments.cpp b/src/smartmining/miningpayments.cpp index dce135c4..8a91bb64 100644 --- a/src/smartmining/miningpayments.cpp +++ b/src/smartmining/miningpayments.cpp @@ -220,7 +220,12 @@ static bool CheckSignature(const CBlock &block, const CBlockIndex *pindex) CAmount GetMiningReward(CBlockIndex * pindex, CAmount blockReward) { + if( pindex->nHeight < HF_V1_3_HEIGHT ){ return blockReward / 20; // 5% + } + else { + return blockReward / 100; // 1% + } } void SmartMining::FillPayment(CMutableTransaction& coinbaseTx, int nHeight, CBlockIndex * pindexPrev, CAmount blockReward, CTxOut &outSignature, const CSmartAddress &signingAddress) diff --git a/src/smartnode/smartnode.cpp b/src/smartnode/smartnode.cpp index 76b431bd..068ca446 100644 --- a/src/smartnode/smartnode.cpp +++ b/src/smartnode/smartnode.cpp @@ -938,9 +938,11 @@ void ThreadSmartnode(CConnman& connman) mnodeman.DoFullVerificationStep(connman); } + /* WIP-VOTING uncomment if(nTick % (60 * 5) == 0) { smartVoting.DoMaintenance(connman); } + */ } } diff --git a/src/smartnode/smartnodepayments.cpp b/src/smartnode/smartnodepayments.cpp index 1ac1e043..8a1e0e2d 100644 --- a/src/smartnode/smartnodepayments.cpp +++ b/src/smartnode/smartnodepayments.cpp @@ -48,14 +48,10 @@ int SmartNodePayments::PayoutsPerBlock(int nHeight) }else{ - if(nHeight >= TESTNET_V1_2_MULTINODE_PAYMENTS_HEIGHT_1 && nHeight < TESTNET_V1_2_MULTINODE_PAYMENTS_HEIGHT_2) - return TESTNET_V1_2_NODES_PER_BLOCK_1; - if(nHeight >= TESTNET_V1_2_MULTINODE_PAYMENTS_HEIGHT_2 && nHeight < TESTNET_V1_2_MULTINODE_PAYMENTS_HEIGHT_3) - return TESTNET_V1_2_NODES_PER_BLOCK_2; - if(nHeight >= TESTNET_V1_2_MULTINODE_PAYMENTS_HEIGHT_3 && nHeight < TESTNET_V1_2_8_SMARNODE_NEW_COLLATERAL_HEIGHT) - return TESTNET_V1_2_NODES_PER_BLOCK_3; - if(nHeight >= TESTNET_V1_2_8_SMARNODE_NEW_COLLATERAL_HEIGHT) + if(nHeight >= TESTNET_V1_2_8_PAYMENTS_HEIGHT && nHeight < TESTNET_V1_3_HEIGHT) return TESTNET_V1_2_8_NODES_PER_BLOCK; + if(nHeight >= TESTNET_V1_3_HEIGHT) + return TESTNET_V1_3_NODES_PER_BLOCK; } @@ -73,12 +69,11 @@ int SmartNodePayments::PayoutInterval(int nHeight) } }else{ - if(nHeight >= TESTNET_V1_2_MULTINODE_PAYMENTS_HEIGHT_1 && nHeight < TESTNET_V1_2_MULTINODE_PAYMENTS_HEIGHT_2) - return TESTNET_V1_2_NODES_BLOCK_INTERVAL_1; - if(nHeight >= TESTNET_V1_2_MULTINODE_PAYMENTS_HEIGHT_2 && nHeight < TESTNET_V1_2_MULTINODE_PAYMENTS_HEIGHT_3) - return TESTNET_V1_2_NODES_BLOCK_INTERVAL_2; - if(nHeight >= TESTNET_V1_2_MULTINODE_PAYMENTS_HEIGHT_3) - return TESTNET_V1_2_NODES_BLOCK_INTERVAL_3; + + if(nHeight >= TESTNET_V1_2_8_PAYMENTS_HEIGHT && nHeight < TESTNET_V1_3_HEIGHT) + return TESTNET_V1_2_8_NODES_BLOCK_INTERVAL; + if(nHeight >= TESTNET_V1_3_HEIGHT) + return TESTNET_V1_3_NODES_BLOCK_INTERVAL; } @@ -110,12 +105,9 @@ CAmount SmartNodePayments::Payment(int nHeight) }else{ - if( nHeight < TESTNET_V1_2_PAYMENTS_HEIGHT ){ + if( nHeight < TESTNET_V1_2_8_PAYMENTS_HEIGHT ){ blockValue = 0; - }else if( nHeight >= TESTNET_V1_2_PAYMENTS_HEIGHT && - nHeight < TESTNET_V1_2_MULTINODE_PAYMENTS_HEIGHT_1 ){ - blockValue = GetBlockValue(nHeight,0,INT_MAX); - }else if(nHeight >= TESTNET_V1_2_MULTINODE_PAYMENTS_HEIGHT_1){ + }else if( nHeight >= TESTNET_V1_2_8_PAYMENTS_HEIGHT ){ int interval = SmartNodePayments::PayoutInterval(nHeight); @@ -161,13 +153,9 @@ bool SmartNodePayments::IsPaymentValid(const CTransaction& txNew, int nHeight, C }else{ - if( nHeight < TESTNET_V1_2_PAYMENTS_HEIGHT ){ - return true; - }else if( nHeight >= TESTNET_V1_2_PAYMENTS_HEIGHT && - nHeight < TESTNET_V1_2_MULTINODE_PAYMENTS_HEIGHT_1 ){ + if( nHeight < TESTNET_V1_2_8_PAYMENTS_HEIGHT ){ return true; - }else if(nHeight >= TESTNET_V1_2_MULTINODE_PAYMENTS_HEIGHT_1){ - + }else if( nHeight >= TESTNET_V1_2_8_PAYMENTS_HEIGHT ){ if( nHeight % SmartNodePayments::PayoutInterval(nHeight) ) return true; } @@ -243,6 +231,8 @@ void CSmartnodePayments::FillBlockPayee(CMutableTransaction& txNew, int nHeight, { vxoutSmartNodes.clear(); + if( mnodeman.size() < MIN_ACTIVE_SMARTNODES ) return; + if( MainNet() ){ if( nHeight < HF_V1_1_SMARTNODE_HEIGHT ){ @@ -277,12 +267,9 @@ void CSmartnodePayments::FillBlockPayee(CMutableTransaction& txNew, int nHeight, }else{ - if( nHeight < TESTNET_V1_2_PAYMENTS_HEIGHT ){ - return; - }else if( nHeight >= TESTNET_V1_2_PAYMENTS_HEIGHT && - nHeight < TESTNET_V1_2_MULTINODE_PAYMENTS_HEIGHT_1 ){ + if( nHeight < TESTNET_V1_2_8_PAYMENTS_HEIGHT ){ return; - }else if(nHeight >= TESTNET_V1_2_MULTINODE_PAYMENTS_HEIGHT_1){ + }else if(nHeight >= TESTNET_V1_2_8_PAYMENTS_HEIGHT){ if( nHeight % SmartNodePayments::PayoutInterval(nHeight) ) return; if( !SmartNodePayments::PayoutsPerBlock(nHeight) ) return; } diff --git a/src/smartrewards/rewards.cpp b/src/smartrewards/rewards.cpp index caeb527e..4919e3ea 100644 --- a/src/smartrewards/rewards.cpp +++ b/src/smartrewards/rewards.cpp @@ -2,929 +2,881 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "smartrewards/rewards.h" #include "consensus/consensus.h" #include "init.h" -#include "smartrewards/rewards.h" -#include "smartrewards/rewardspayments.h" +#include "rewards.h" +#include "script/standard.h" #include "smarthive/hive.h" -#include "smartnode/spork.h" #include "smartnode/smartnodepayments.h" +#include "smartnode/spork.h" +#include "smartrewards/rewardspayments.h" #include "ui_interface.h" #include "validation.h" -#include -#include #include +#include +#include -CSmartRewards *prewards = NULL; +#define REWARDS_MAX_CACHE 400000000UL // 400MB + +CSmartRewards* prewards = NULL; CCriticalSection cs_rewardsdb; -CCriticalSection cs_rewardrounds; +CCriticalSection cs_rewardscache; + +size_t nCacheRewardEntries; // Used for time conversions. boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1)); -// Estimate or return the current block height. -int GetBlockHeight(const CBlockIndex *index) -{ - int64_t syncDiff = std::time(0) - index->GetBlockTime(); - int64_t firstTxDiff; - if( MainNet() ) firstTxDiff = std::time(0) - nStartRewardTime; // Diff from the reward blocks start till now on mainnet. - else firstTxDiff = std::time(0) - nFirstTxTimestamp_Testnet; // Diff from the first transaction till now on testnet. - return syncDiff > 1200 ? firstTxDiff / 55 : index->nHeight; // If we are 20 minutes near now use the current height. -} - -int ParseScript(const CScript &script, std::vector &ids){ - - std::vector addresses; - txnouttype type; - int nRequired; - - if (!ExtractDestinations(script, type, addresses, nRequired)) { - return 0; +struct CompareRewardScore { + bool operator()(const std::pair& s1, + const std::pair& s2) const + { + return (s1.first != s2.first) ? (s1.first < s2.first) : (s1.second->entry.balance < s2.second->entry.balance); } +}; - BOOST_FOREACH(const CTxDestination &d, addresses) +struct ComparePaymentPrtList { + bool operator()(const CSmartRewardResultEntry* p1, + const CSmartRewardResultEntry* p2) const { - ids.push_back(CSmartAddress(d)); + return *p1 < *p2; } +}; - return nRequired; +// Estimate or return the current block height. +int GetBlockHeight(const CBlockIndex* index) +{ + int64_t syncDiff = std::time(0) - index->GetBlockTime(); + int64_t firstTxDiff; + if (MainNet()) + firstTxDiff = std::time(0) - nStartRewardTime; // Diff from the reward blocks start till now on mainnet. + else + firstTxDiff = std::time(0) - nFirstTxTimestamp_Testnet; // Diff from the first transaction till now on testnet. + return syncDiff > 1200 ? firstTxDiff / 55 : index->nHeight; // If we are 20 minutes near now use the current height. } -void CalculateRewardRatio(CSmartRewardRound &round) +bool ExtractDestination(const CScript& scriptPubKey, CSmartAddress& idRet) { - int64_t time = GetTime(); - int64_t start = round.startBlockHeight; - round.rewards = 0; - - while( start <= round.endBlockHeight) round.rewards += GetBlockValue(start++,0,time) * 0.15; + vector> vSolutions; + txnouttype whichType; + if (!Solver(scriptPubKey, whichType, vSolutions)) + return false; - CAmount nCalcSmart = round.eligibleSmart - round.disqualifiedSmart; + if (whichType == TX_PUBKEY) { + CPubKey pubKey(vSolutions[0]); + if (!pubKey.IsValid()) + return false; - if( nCalcSmart ){ - round.percent = double(round.rewards) / nCalcSmart; - }else{ - round.percent = 0; + idRet = CSmartAddress(pubKey.GetID()); + return true; + } else if (whichType == TX_PUBKEYHASH) { + idRet = CSmartAddress(CKeyID(uint160(vSolutions[0]))); + return true; + } else if (whichType == TX_SCRIPTHASH) { + idRet = CSmartAddress(CScriptID(uint160(vSolutions[0]))); + return true; + } else if (whichType == TX_PUBKEYHASHLOCKED) { + idRet = CSmartAddress(CKeyID(uint160(vSolutions[0]))); + return true; + } else if (whichType == TX_SCRIPTHASHLOCKED) { + idRet = CSmartAddress(CScriptID(uint160(vSolutions[0]))); + return true; } + // Multisig txns have more than one address... + return false; } bool CSmartRewards::Verify() { LOCK(cs_rewardsdb); - return pdb->Verify(rewardHeight); + int nHeight = cache.GetCurrentBlock()->nHeight; + return pdb->Verify(nHeight); } +bool CSmartRewards::NeedsCacheWrite() +{ + return cache.NeedsSync(); +} -void CSmartRewards::UpdatePayoutParameter(CSmartRewardRound &round) +void CSmartRewards::UpdateRoundPayoutParameter() { + AssertLockHeld(cs_rewardscache); - int64_t nPayeeCount = round.eligibleEntries - round.disqualifiedEntries; - int nFirst_1_3_Round = Params().GetConsensus().nRewardsFirst_1_3_Round; + const CSmartRewardRound* round = cache.GetCurrentRound(); + int64_t nBlockPayees = round->nBlockPayees, nBlockInterval; - if( round.number < nFirst_1_3_Round ){ - round.nBlockPayees = Params().GetConsensus().nRewardsPayouts_1_2_BlockPayees; - round.nBlockInterval = Params().GetConsensus().nRewardsPayouts_1_2_BlockInterval; - }else if( nPayeeCount ){ + int64_t nPayeeCount = round->eligibleEntries - round->disqualifiedEntries; + int nFirst_1_3_Round = Params().GetConsensus().nRewardsFirst_1_3_Round; + if ( round->number < nFirst_1_3_Round ) { + nBlockPayees = Params().GetConsensus().nRewardsPayouts_1_2_BlockPayees; + nBlockInterval = Params().GetConsensus().nRewardsPayouts_1_2_BlockInterval; + } else if ( nPayeeCount ) { int64_t nBlockStretch = Params().GetConsensus().nRewardsPayouts_1_3_BlockStretch; int64_t nBlocksPerRound = Params().GetConsensus().nRewardsBlocksPerRound_1_3; - int64_t nBlockPayees = Params().GetConsensus().nRewardsPayouts_1_3_BlockPayees; + int64_t nTempBlockPayees = Params().GetConsensus().nRewardsPayouts_1_3_BlockPayees; - round.nBlockPayees = std::max(nBlockPayees, (nPayeeCount / nBlockStretch * nBlockPayees) + 1); - - if( nPayeeCount > round.nBlockPayees ){ + nBlockPayees = std::max(nTempBlockPayees, (nPayeeCount / nBlockStretch * nTempBlockPayees) + 1); + if (nPayeeCount > nBlockPayees) { int64_t nStartDelayBlocks = Params().GetConsensus().nRewardsPayoutStartDelay; int64_t nBlocksTarget = nStartDelayBlocks + nBlocksPerRound; - round.nBlockInterval = ((nBlockStretch * round.nBlockPayees) / nPayeeCount) + 1; - int64_t nStretchedLength = nPayeeCount / round.nBlockPayees * (round.nBlockInterval); + nBlockInterval = ((nBlockStretch * nBlockPayees) / nPayeeCount) + 1; + int64_t nStretchedLength = nPayeeCount / nBlockPayees * (nBlockInterval); - if( nStretchedLength > nBlocksTarget ){ - round.nBlockInterval--; - }else if( nStretchedLength < nBlockStretch ){ - round.nBlockInterval++; + if (nStretchedLength > nBlocksTarget) { + nBlockInterval--; + } else if (nStretchedLength < nBlockStretch) { + nBlockInterval++; } - }else{ + } else { // If its only one block to pay - round.nBlockInterval = 1; + nBlockInterval = 1; } - }else{ + } else { // If there are no eligible smartreward entries - round.nBlockPayees = 0; - round.nBlockInterval = 0; + nBlockPayees = 0; + nBlockInterval = 0; } + cache.UpdateRoundPayoutParameter(nBlockPayees, nBlockInterval); } -void CSmartRewards::EvaluateRound(CSmartRewardRound ¤t, CSmartRewardRound &next, CSmartRewardEntryList &entries, CSmartRewardRoundResultList &results) +void CSmartRewards::UpdatePercentage() { - LOCK(cs_rewardsdb); - results.clear(); + AssertLockHeld(cs_rewardscache); + + const CSmartRewardRound *round = cache.GetCurrentRound(); - UpdatePayoutParameter(current); + double nPercent = 0.0; + if ( (round->eligibleSmart - round->disqualifiedSmart) > 0 ) { + nPercent = double(round->rewards) / (round->eligibleSmart - round->disqualifiedSmart); + }else{ + nPercent = 0; + } + + cache.UpdateRoundPercent(nPercent); +} + +bool CSmartRewards::Is_1_3(uint16_t round) +{ + if (round >= Params().GetConsensus().nRewardsFirst_1_3_Round) { + return 1; + } else { + return 0; + } +} + +void CSmartRewards::EvaluateRound(CSmartRewardRound &next) +{ + LOCK(cs_rewardscache); + + UpdateRoundPayoutParameter(); + + const CSmartRewardRound *round = cache.GetCurrentRound(); int nFirst_1_3_Round = Params().GetConsensus().nRewardsFirst_1_3_Round; + CAmount nMinBalance = next.number < nFirst_1_3_Round ? SMART_REWARDS_MIN_BALANCE_1_2 : SMART_REWARDS_MIN_BALANCE_1_3; + + CSmartRewardsRoundResult *pResult = new CSmartRewardsRoundResult(); + + pResult->round = *round; + pResult->round.UpdatePayoutParameter(); CAmount nReward; + CSmartRewardEntryMap tmpEntries; + pdb->ReadRewardEntries(tmpEntries); - BOOST_FOREACH(CSmartRewardEntry &entry, entries) { + for( auto itDb = tmpEntries.begin(); itDb != tmpEntries.end(); ++itDb){ - if( current.number ){ + auto itCache = cache.GetEntries()->find(itDb->first); - if( current.number < nFirst_1_3_Round ){ - nReward = entry.balanceEligible > 0 && entry.disqualifyingTx.IsNull() ? CAmount(entry.balanceEligible * current.percent) : 0; - }else{ - nReward = entry.IsEligible() ? CAmount(entry.balanceEligible * current.percent) : 0; + if( itCache == cache.GetEntries()->end() ){ + cache.AddEntry(itDb->second); + }else{ + delete itDb->second; + } + } + + if( cache.GetCurrentRound()->number >= nFirst_1_3_Round ) { + next.eligibleSmart = 0; + next.eligibleEntries = 0; + + int64_t nTime = GetTime(); + int64_t nStartHeight = next.startBlockHeight - (next.endBlockHeight - next.startBlockHeight); + double dBlockReward = 0.60; + next.rewards = 0; + + while( nStartHeight <= next.startBlockHeight) next.rewards += GetBlockValue(nStartHeight++, 0, nTime) * dBlockReward; + std::list eligibleAddresses; + auto entry = cache.GetEntries()->begin(); + while(entry != cache.GetEntries()->end() ) { + if( entry->second->balanceAtStart >= nMinBalance && !SmartHive::IsHive(entry->second->id) && !entry->second->fDisqualifyingTx && entry->second->fActivated ) { + entry->second->balanceEligible = entry->second->balance; + next.eligibleSmart += entry->second->balanceEligible; + ++next.eligibleEntries; + eligibleAddresses.push_back(entry->first); + } else { + entry->second->balanceEligible = 0; } + entry->second->balanceAtStart = entry->second->balance; + ++entry; + } + + // Check back all the previous rounds for adding weighted balance if applicable + if (cache.GetCurrentRound()->number - 8 >= nFirst_1_3_Round && !eligibleAddresses.empty()) { + int roundNumber = cache.GetCurrentRound()->number - 1; + while (roundNumber >= nFirst_1_3_Round) { + CSmartRewardResultEntryList results; + if (!GetRewardRoundResults(roundNumber, results)) { + break; + } + + // Iterate over all still eligible addresses + auto address = eligibleAddresses.begin(); + while (address != eligibleAddresses.end()) { + // Look for address in the round results + auto addressResult = std::find_if(results.begin(), results.end(), + [&address](const CSmartRewardResultEntry& e) -> bool { + return *address == e.entry.id; + }); + + // If address was not in last round result => remove from list + if (addressResult == results.end()) { + address = eligibleAddresses.erase(address); + continue; + } + + // If the address balance was not eligible => remove from list + if (!addressResult->entry.balanceEligible) { + address = eligibleAddresses.erase(address); + continue; + } + + // If we are at a "special round", add to the eligible balance with proper weight + CAmount toAdd = 0; + CSmartRewardEntry::BonusLevel bonus = CSmartRewardEntry::NoBonus; + if (roundNumber == cache.GetCurrentRound()->number - 8) { + toAdd = addressResult->entry.balance; + bonus = CSmartRewardEntry::TwoMonthsBonus; + } else if (roundNumber == cache.GetCurrentRound()->number - 16) { + toAdd = 2 * addressResult->entry.balance; + bonus = CSmartRewardEntry::FourMonthsBonus; + } else if (roundNumber == cache.GetCurrentRound()->number - 26) { + toAdd = 2 * addressResult->entry.balance; + bonus = CSmartRewardEntry::SixMonthsBonus; + } + + if (toAdd > 0) { + auto &cacheEntry = cache.GetEntries()->at(*address); + cacheEntry->balanceEligible += toAdd; + cacheEntry->bonusLevel = bonus; + next.eligibleSmart += toAdd; + } + + address++; + } + + // If there are no more eligible addresses to check, exit the loop + if (eligibleAddresses.empty()) { + break; + } - results.push_back(CSmartRewardRoundResult(entry, nReward)); + roundNumber--; + } } - entry.balanceAtStart = entry.balance; + double rpercent = next.eligibleSmart > 10000 ? ( (double)next.rewards / (double)next.eligibleSmart ) : 0; + entry = cache.GetEntries()->begin(); + while(entry != cache.GetEntries()->end() ) { + nReward = entry->second->IsEligible() ? CAmount(entry->second->balanceEligible * rpercent) : 0; + pResult->results.push_back(new CSmartRewardResultEntry(entry->second, nReward)); + if( nReward ){ + pResult->payouts.push_back(pResult->results.back()); + } + + // Calculate rewards for next cycle + next.rewards = 0; + nStartHeight = next.startBlockHeight; + while( nStartHeight <= next.endBlockHeight) next.rewards += GetBlockValue(nStartHeight++, 0, nTime) * dBlockReward; + + // Reset outgoing transaction with every cycle. + entry->second->disqualifyingTx.SetNull(); + entry->second->fDisqualifyingTx = false; + + // Reset SmartNode payment tx with every cycle in case a node was shut down during the cycle. + entry->second->smartnodePaymentTx.SetNull(); + entry->second->fSmartnodePaymentTx = false; - if( entry.balance >= SMART_REWARDS_MIN_BALANCE && !SmartHive::IsHive(entry.id) ){ - entry.balanceEligible = entry.balance; + // Reset the vote proof tx 2 cycles before the first 1.3 round. + ++entry; } - // Reset outgoing transaction with every cycle. - entry.disqualifyingTx.SetNull(); - // Reset SmartNode payment tx with every cycle in case a node was shut down during the cycle. - entry.smartnodePaymentTx.SetNull(); - // Reset the vote proof tx with every cycle to force a new vote for eligibility - entry.voteProof.SetNull(); + if( pResult->payouts.size() ){ + uint256 blockHash; + if(!GetBlockHash(blockHash, pResult->round.startBlockHeight)) { + throw std::runtime_error(strprintf("CSmartRewards::EvaluateRound -- ERROR: GetBlockHash() failed at nBlockHeight %d\n", round->startBlockHeight)); + } + + std::vector> vecScores; + // Since we use payouts stretched out over a week better to have some "random" sort here + // based on a score calculated with the round start's blockhash. + CSmartRewardResultEntryPtrList::iterator it = pResult->payouts.begin(); + + while( it != pResult->payouts.end() ){ + arith_uint256 nScore = (*it)->CalculateScore(blockHash); + vecScores.push_back(std::make_pair(nScore,*it)); + ++it; + } - if( next.number < nFirst_1_3_Round && entry.balanceEligible ){ - ++next.eligibleEntries; - next.eligibleSmart += entry.balanceEligible; + std::sort(vecScores.begin(), vecScores.end(), CompareRewardScore()); + + pResult->payouts.clear(); + + for(auto s : vecScores) + pResult->payouts.push_back(s.second); + } + if( pResult->round.number ){ + cache.AddFinishedRound(pResult->round); } - } -} -bool CSmartRewards::StartFirstRound(const CSmartRewardRound &first, const CSmartRewardEntryList &entries) -{ - LOCK(cs_rewardsdb); - return pdb->StartFirstRound(first,entries); + cache.SetResult(pResult); + cache.SetCurrentRound(next); + + } else if( cache.GetCurrentRound()->number && ( cache.GetCurrentRound()->number < nFirst_1_3_Round )){ + auto entry = cache.GetEntries()->begin(); + while(entry != cache.GetEntries()->end() ) { + + nReward = entry->second->balanceEligible > 0 && !entry->second->fDisqualifyingTx ? CAmount(entry->second->balanceEligible * round->percent) : 0; + + pResult->results.push_back(new CSmartRewardResultEntry(entry->second, nReward)); + + if( nReward ){ + pResult->payouts.push_back(pResult->results.back()); + } + + entry->second->balanceAtStart = entry->second->balance; + + if( entry->second->balance >= nMinBalance && !SmartHive::IsHive(entry->second->id) ){ + entry->second->balanceEligible = entry->second->balance; + }else{ + entry->second->balanceEligible = 0; + } + + // Reset outgoing transaction with every cycle. + entry->second->disqualifyingTx.SetNull(); + entry->second->fDisqualifyingTx = false; + + // Reset SmartNode payment tx with every cycle in case a node was shut down during the cycle. + entry->second->smartnodePaymentTx.SetNull(); + entry->second->fSmartnodePaymentTx = false; + + if( entry->second->balanceEligible ){ + ++next.eligibleEntries; + next.eligibleSmart += entry->second->balanceEligible; + } + + // Reset activations and reset eligible before 1.3 round starts. + if( next.number == (nFirst_1_3_Round) ){ + entry->second->activationTx.SetNull(); + entry->second->fActivated = false; + next.eligibleEntries = 0; + next.eligibleSmart = 0; + } + ++entry; + } + + if( pResult->payouts.size() ){ + // Sort it to make sure the slices are the same network wide. + std::sort(pResult->payouts.begin(), pResult->payouts.end(), ComparePaymentPrtList() ); + } + + // Calculate the current rewards percentage + int64_t nTime = GetTime(); + int64_t nStartHeight = next.startBlockHeight; + double dBlockReward = next.number < nFirst_1_3_Round ? 0.15 : 0.60; + next.rewards = 0; + while( nStartHeight <= next.endBlockHeight) next.rewards += GetBlockValue(nStartHeight++, 0, nTime) * dBlockReward; + + if( pResult->round.number ){ + cache.AddFinishedRound(pResult->round); + } + cache.SetResult(pResult); + cache.SetCurrentRound(next); + } else { + // Calculate the current total smartrewards amount + int64_t nTime = GetTime(); + int64_t nStartHeight = next.startBlockHeight; + double dBlockReward = 0.15; + next.rewards = 0; + while( nStartHeight <= next.endBlockHeight) next.rewards += GetBlockValue(nStartHeight++, 0, nTime) * dBlockReward; + + cache.SetResult(pResult); + cache.SetCurrentRound(next); + } } -bool CSmartRewards::FinalizeRound(const CSmartRewardRound ¤t, const CSmartRewardRound &next, const CSmartRewardEntryList &entries, const CSmartRewardRoundResultList &results) +bool CSmartRewards::GetRewardRoundResults(const int16_t round, CSmartRewardResultEntryList& results) { LOCK(cs_rewardsdb); - return pdb->FinalizeRound(current, next, entries, results); + return pdb->ReadRewardRoundResults(round, results); } -bool CSmartRewards::UndoFinalizeRound(const CSmartRewardRound ¤t, const CSmartRewardRoundResultList &results) +bool CSmartRewards::GetRewardRoundResults(const int16_t round, CSmartRewardResultEntryPtrList& results) { LOCK(cs_rewardsdb); - return pdb->UndoFinalizeRound(current, results); + return pdb->ReadRewardRoundResults(round, results); } -bool CSmartRewards::GetRewardRoundResults(const int16_t round, CSmartRewardRoundResultList &results) +const CSmartRewardsRoundResult* CSmartRewards::GetLastRoundResult() { - LOCK(cs_rewardsdb); - return pdb->ReadRewardRoundResults(round, results); + return cache.GetLastRoundResult(); } -bool CSmartRewards::GetRewardPayouts(const int16_t round, CSmartRewardRoundResultList &payouts) +bool CSmartRewards::GetRewardPayouts(const int16_t round, CSmartRewardResultEntryList& payouts) { LOCK(cs_rewardsdb); return pdb->ReadRewardPayouts(round, payouts); } -bool CSmartRewards::GetRewardPayouts(const int16_t round, CSmartRewardRoundResultPtrList &payouts) +bool CSmartRewards::GetRewardPayouts(const int16_t round, CSmartRewardResultEntryPtrList& payouts) { LOCK(cs_rewardsdb); return pdb->ReadRewardPayouts(round, payouts); } -bool CSmartRewards::GetCachedRewardEntry(const CSmartAddress &id, CSmartRewardEntry *&entry) +bool CSmartRewards::GetRewardEntry(const CSmartAddress& id, CSmartRewardEntry*& entry, bool fCreate) { - LOCK(cs_rewardsdb); + LOCK(cs_rewardscache); // Return the entry if its already in cache. - auto findResult = rewardEntries.find(id); + auto it = cache.GetEntries()->find(id); - if( findResult != rewardEntries.end() ){ - entry = findResult->second; + if (it != cache.GetEntries()->end()) { + entry = it->second; return true; } - return false; -} - -bool CSmartRewards::ReadRewardEntry(const CSmartAddress &id, CSmartRewardEntry &entry) -{ - LOCK(cs_rewardsdb); - return pdb->ReadRewardEntry(id,entry); -} - -bool CSmartRewards::GetRewardEntry(const CSmartAddress &id, CSmartRewardEntry &entry) -{ - LOCK(cs_rewardsdb); + entry = new CSmartRewardEntry(id); - CSmartRewardEntry * pReadEntry; + // Return the entry if its already in db. + if (pdb->ReadRewardEntry(id, *entry)) { + cache.AddEntry(entry); + return true; + } - if( GetCachedRewardEntry(id,pReadEntry) ){ - entry = *pReadEntry; + if (fCreate) { + cache.AddEntry(entry); return true; } - return ReadRewardEntry(id,entry); + delete entry; + entry = nullptr; + + return false; } -bool CSmartRewards::GetRewardEntries(CSmartRewardEntryList &entries) +bool CSmartRewards::GetRewardEntries(CSmartRewardEntryMap& entries) { LOCK(cs_rewardsdb); return pdb->ReadRewardEntries(entries); } -bool CSmartRewards::SyncCached(bool fUndo) +bool CSmartRewards::SyncCached() { - return SyncCached(CSmartRewardBlock(), fUndo); -} + LOCK2(cs_rewardscache, cs_rewardsdb); -bool CSmartRewards::SyncCached(const CSmartRewardBlock &block, bool fUndo) -{ - LOCK2(cs_rewardsdb, cs_rewardrounds); + int nTimeStart = GetTimeMicros(); + int nEntriesPre = cache.GetEntries()->size(); + int nSizePre = cache.EstimatedSize(); - bool ret = pdb->SyncCached(block, currentRound, rewardEntries, transactionEntries, fUndo); + bool ret = pdb->SyncCached(cache); - for( std::pair it : rewardEntries ){ - delete it.second; - } + cache.Clear(); + + int nTimeDone = GetTimeMicros(); - rewardEntries.clear(); - transactionEntries.clear(); + int nEntriesPost = cache.GetEntries()->size(); + int nSizePost = cache.EstimatedSize(); + + LogPrint("smartrewards-bench", "CSmartRewards::SyncCached size before/after %dMB/%dMB, entries before/after %d/%d, time %.2fms", nSizePre / 1000000, nSizePost / 1000000, nEntriesPre, nEntriesPost, (nTimeDone - nTimeStart) * 0.001); return ret; } bool CSmartRewards::IsSynced() { - int nSyncDistance = MainNet() ? nRewardsSyncDistance : nRewardsSyncDistance_Testnet; - return (chainHeight - rewardHeight) <= nSyncDistance - 1; -} + static bool fSynced = false; -double CSmartRewards::GetProgress() -{ - int nSyncDistance = MainNet() ? nRewardsSyncDistance : nRewardsSyncDistance_Testnet; - double progress = chainHeight > nSyncDistance ? double(rewardHeight) / double(chainHeight - nSyncDistance) : 0.0; - return progress > 1 ? 1 : progress; -} + if (fSynced) + return true; -int CSmartRewards::GetLastHeight() -{ - return rewardHeight; + fSynced = (GetTime() - cache.GetCurrentBlock()->nTime) <= nRewardsSyncDistance; + + return fSynced; } int CSmartRewards::GetBlocksPerRound(const int nRound) { - LOCK(cs_rewardrounds); - const CChainParams& chainparams = Params(); - if( nRound < chainparams.GetConsensus().nRewardsFirst_1_3_Round ){ + if (nRound < chainparams.GetConsensus().nRewardsFirst_1_3_Round) { return chainparams.GetConsensus().nRewardsBlocksPerRound_1_2; - }else{ + } else { return chainparams.GetConsensus().nRewardsBlocksPerRound_1_3; } - } -void CSmartRewards::AddTransaction(const CSmartRewardTransaction &transaction) +CSmartRewards::CSmartRewards(CSmartRewardsDB* prewardsdb) : pdb(prewardsdb) { - LOCK(cs_rewardsdb); - transactionEntries.push_back(transaction); -} + LOCK2(cs_rewardscache, cs_rewardsdb); -CSmartRewards::CSmartRewards(CSmartRewardsDB *prewardsdb) : pdb(prewardsdb) -{ - LOCK(cs_rewardsdb); + CSmartRewardBlock block; + CSmartRewardRound round; + CSmartRewardRoundMap rounds; + CSmartRewardResultEntryList results; - // Get the last written block of the rewards database. - if(!pdb->ReadLastBlock(currentBlock)){ - // If there is no one available yet - // Use 0 to get 1 as start below. - currentBlock.nHeight = 0; - currentBlock.blockHash = uint256(); - currentBlock.blockTime = 0; - } + pdb->ReadLastBlock(block); + pdb->ReadCurrentRound(round); + pdb->ReadRounds(rounds); - pdb->ReadRounds(finishedRounds); + cache.Load(block, round, rounds); - std::sort(finishedRounds.begin(), finishedRounds.end()); + CSmartRewardsRoundResult* pResult = new CSmartRewardsRoundResult(); - if( finishedRounds.size() ){ - lastRound = finishedRounds.back(); - } + if (round.number > 1) { + pResult->fSynced = true; + pResult->round = rounds[round.number - 1]; + pdb->ReadRewardRoundResults(round.number - 1, pResult->results); - pdb->ReadCurrentRound(currentRound); -} + for (auto it : pResult->results) { + if (it->reward) { + pResult->payouts.push_back(it); + } + } + } -void CSmartRewards::Lock() -{ - LOCK(cs_rewardsdb); - pdb->Lock(); -} + cache.SetResult(pResult); -bool CSmartRewards::IsLocked() -{ - LOCK(cs_rewardsdb); - return pdb->IsLocked(); + LogPrintf("CSmartRewards::CSmartRewards\n Last block %s\n Current Round %s\n Rounds: %d", block.ToString(), round.ToString(), rounds.size()); } -bool CSmartRewards::GetLastBlock(CSmartRewardBlock &block) +bool CSmartRewards::GetLastBlock(CSmartRewardBlock& block) { LOCK(cs_rewardsdb); // Read the last block stored in the rewards database. return pdb->ReadLastBlock(block); } -bool CSmartRewards::GetTransaction(const uint256 hash, CSmartRewardTransaction &transaction) +bool CSmartRewards::GetTransaction(const uint256 nHash, CSmartRewardTransaction& transaction) { + LOCK2(cs_rewardscache, cs_rewardsdb); // If the transaction is already in the cache use this one. - BOOST_FOREACH(CSmartRewardTransaction t, transactionEntries) { - if(t.hash == hash){ - transaction = t; - return true; - } - } - - return pdb->ReadTransaction(hash, transaction); -} - -const CSmartRewardRound& CSmartRewards::GetCurrentRound() -{ - return currentRound; -} + auto it = cache.GetAddedTransactions()->find(nHash); -const CSmartRewardRound& CSmartRewards::GetLastRound() -{ - return lastRound; -} + if (it != cache.GetAddedTransactions()->end()) { + transaction = it->second; + return true; + } -const CSmartRewardRoundList& CSmartRewards::GetRewardRounds() -{ - return finishedRounds; + return pdb->ReadTransaction(nHash, transaction); } -void CSmartRewards::UpdateHeights(const int nHeight, const int nRewardHeight) +const CSmartRewardRound* CSmartRewards::GetCurrentRound() { - chainHeight = nHeight; - rewardHeight = nRewardHeight; + return cache.GetCurrentRound(); } -void CSmartRewards::StartBlock() +const CSmartRewardRoundMap* CSmartRewards::GetRewardRounds() { - rewardEntries.clear(); - transactionEntries.clear(); + return cache.GetRounds(); } -void CSmartRewards::ProcessTransaction(CBlockIndex* pIndex, const CTransaction& tx, CCoinsViewCache& coins, const CChainParams& chainparams, CSmartRewardsUpdateResult &result) +void CSmartRewards::ProcessInput(const CTransaction& tx, const CTxOut& in, uint16_t nCurrentRound, CSmartRewardsUpdateResult& result) { - LogPrint("smartrewards-tx", "CSmartRewards::ProcessTransaction - %s", tx.GetHash().ToString()); - - CSmartRewardEntry *rEntry = nullptr; - int nFirst_1_3_Round = chainparams.GetConsensus().nRewardsFirst_1_3_Round; - - int nCurrentRound; - - { - LOCK(cs_rewardrounds); - nCurrentRound = currentRound.number; - } - - int nHeight = pIndex->nHeight; + uint16_t nFirst_1_3_Round = Params().GetConsensus().nRewardsFirst_1_3_Round; + CSmartRewardEntry* rEntry = nullptr; + CSmartAddress id; - if(nHeight > sporkManager.GetSporkValue(SPORK_15_SMARTREWARDS_BLOCKS_ENABLED)){ + if (!ExtractDestination(in.scriptPubKey, id)) { + LogPrint("smartrewards-tx", "CSmartRewards::ProcessInput - Could't parse CSmartAddress: %s\n", in.ToString()); return; } - int nTime1 = GetTimeMicros(); - - CSmartRewardTransaction testTx; - - // First check if the transaction hash did already come up in the past. - if( GetTransaction(tx.GetHash(), testTx) ){ - - // If yes we want to ignore it! There are some double appearing transactions in the history due to zerocoin exploits. - LogPrint("smartrewards-tx", "CSmartRewards::ProcessTransaction - [%s] Double appearance! First in %d - Now in %d\n", testTx.hash.ToString(), testTx.blockHeight, pIndex->nHeight); + if (!GetRewardEntry(id, rEntry, false)) { + LogPrint("smartrewards-tx", "CSmartRewards::ProcessInput - Spend without previous receive - %s", tx.ToString()); return; - - }else{ - // If not save add it to the database. - AddTransaction(CSmartRewardTransaction(pIndex->nHeight, tx.GetHash())); } - CSmartAddress *voteProofCheck = nullptr; - CAmount nVoteProofIn = 0; - unsigned char cProofOption = 0; + if (nCurrentRound >= nFirst_1_3_Round && tx.IsActivationTx() && !rEntry->fActivated) { + rEntry->activationTx = tx.GetHash(); + rEntry->fActivated = true; + } - // No reason to check the input here for new coins. - if( !tx.IsCoinBase() ){ + rEntry->balance -= in.nValue; - BOOST_FOREACH(const CTxIn &in, tx.vin) { + if (nCurrentRound >= nFirst_1_3_Round && !tx.IsActivationTx() && !rEntry->fDisqualifyingTx) { + if( rEntry->IsEligible() ){ + result.disqualifiedEntries++; + result.disqualifiedSmart += rEntry->balanceEligible; + } + rEntry->disqualifyingTx = tx.GetHash(); + rEntry->fDisqualifyingTx = true; + } - if( in.scriptSig.IsZerocoinSpend() ) continue; + if (nCurrentRound >= nFirst_1_3_Round && rEntry->fActivated && !tx.IsActivationTx()) { + rEntry->activationTx.SetNull(); + rEntry->fActivated = false; + } - const Coin &coin = coins.AccessCoin(in.prevout); - const CTxOut &rOut = coin.out; + if (nCurrentRound < nFirst_1_3_Round && !rEntry->fDisqualifyingTx) { - std::vector ids; + rEntry->disqualifyingTx = tx.GetHash(); + rEntry->fDisqualifyingTx = true; - int required = ParseScript(rOut.scriptPubKey ,ids); + if( rEntry->balanceEligible ){ + result.disqualifiedEntries++; + result.disqualifiedSmart += rEntry->balanceEligible; + } - if( !required || required > 1 || ids.size() > 1 ){ - LogPrint("smartrewards-tx", "CSmartRewards::ProcessTransaction - Process Inputs: Could't parse CSmartAddress: %s\n",rOut.ToString()); - continue; - } + } - if(!GetCachedRewardEntry(ids.at(0),rEntry)){ + if (rEntry->balance < 0) { + rEntry->balance = 0; + } +} - rEntry = new CSmartRewardEntry(ids.at(0)); +void CSmartRewards::ProcessOutput(const CTransaction& tx, const CTxOut& out, uint16_t nCurrentRound, int nHeight, CSmartRewardsUpdateResult& result) +{ + CSmartRewardEntry* rEntry = nullptr; + CSmartAddress id; - if(!ReadRewardEntry(rEntry->id, *rEntry)){ - delete rEntry; - LogPrint("smartrewards-tx", "CSmartRewards::ProcessTransaction - Spend without previous receive - %s", tx.ToString()); - continue; + if (!ExtractDestination(out.scriptPubKey, id)) { + LogPrint("smartrewards-tx", "CSmartRewards::ProcessOutput - Could't parse CSmartAddress: %s\n", out.ToString()); + return; + } else { + if (GetRewardEntry(id, rEntry, true)) { + if (tx.IsActivationTx() && Is_1_3(nCurrentRound) && !SmartHive::IsHive(rEntry->id)) { + if (!rEntry->fActivated) { + rEntry->activationTx = tx.GetHash(); + rEntry->fActivated = true; + if ( rEntry->IsEligible() ) { + result.qualifiedEntries++; + result.qualifiedSmart += rEntry->balanceEligible; + } } - - rewardEntries.insert(make_pair(ids.at(0), rEntry)); - } - - // If its a voteproof transaction not instantly make the - // balance ineligible. First check if the change is sent back - // to the address or not to avoid exploiting fund sending - // with voteproof transactions - if( nCurrentRound >= nFirst_1_3_Round && tx.IsVoteProof() && voteProofCheck == nullptr ){ - voteProofCheck = new CSmartAddress(rEntry->id); - nVoteProofIn += rOut.nValue; } + } - rEntry->balance -= rOut.nValue; - - // If its a voteproof transaction not instantly make the - // balance ineligible. First check if the change is sent back - // to the address or not to avoid exploiting fund sending - // with voteproof transactions - if( nCurrentRound >= nFirst_1_3_Round && voteProofCheck == nullptr && rEntry->disqualifyingTx.IsNull() ){ - if( rEntry->IsEligible() ){ - result.disqualifiedEntries++; - result.disqualifiedSmart += rEntry->balanceEligible; + rEntry->balance += out.nValue; + + if (Is_1_3(nCurrentRound) && tx.IsCoinBase()) { + int nInterval = SmartNodePayments::PayoutInterval(nHeight); + int nPayoutsPerBlock = SmartNodePayments::PayoutsPerBlock(nHeight); + // Just to avoid potential zero divisions + nPayoutsPerBlock = std::max(1, nPayoutsPerBlock); + + CAmount nNodeReward = SmartNodePayments::Payment(nHeight) / nPayoutsPerBlock; + // If we have an interval check if this is a node payout block + if (nInterval && !(nHeight % nInterval)) { + // If the amount matches and the entry is not yet marked as node do it + if (abs(out.nValue - nNodeReward) < 2) { + if (!rEntry->fSmartnodePaymentTx) { + // If it is currently eligible adjust the round's results + if (rEntry->IsEligible() ) { + ++result.disqualifiedEntries; + result.disqualifiedSmart += rEntry->balanceEligible; + rEntry->activationTx.SetNull(); + rEntry->fActivated = false; + } + rEntry->smartnodePaymentTx = tx.GetHash(); + rEntry->fSmartnodePaymentTx = true; + } } + } + } + } +} - rEntry->disqualifyingTx = tx.GetHash(); - - }else if( nCurrentRound < nFirst_1_3_Round && rEntry->disqualifyingTx.IsNull() ){ - - rEntry->disqualifyingTx = tx.GetHash(); +bool CSmartRewards::ProcessTransaction(CBlockIndex* pIndex, const CTransaction& tx, int nCurrentRound) +{ + LogPrint("smartrewards-tx", "CSmartRewards::ProcessTransaction - %s", tx.GetHash().ToString()); - if( rEntry->balanceEligible ){ - result.disqualifiedEntries++; - result.disqualifiedSmart += rEntry->balanceEligible; - } + int nHeight = pIndex->nHeight; - } + if (nHeight == 0 || nHeight > sporkManager.GetSporkValue(SPORK_15_SMARTREWARDS_BLOCKS_ENABLED)) { + return false; + } - if(rEntry->balance < 0 ){ - LogPrint("smartrewards-tx", "CSmartRewards::ProcessTransaction - Negative amount?! - %s", rEntry->ToString()); - rEntry->balance = 0; - } + CSmartRewardTransaction testTx; + // For the first 4 rounds we have zerocoin exploits and we don't want to add them to the rewards db. + if (nCurrentRound <= 4) { + // First check if the transaction hash did already come up in the past. + if (GetTransaction(tx.GetHash(), testTx)) { + // If yes we want to ignore it! There are some double appearing transactions in the history due to zerocoin exploits. + LogPrint("smartrewards-tx", "CSmartRewards::ProcessTransaction - [%s] Double appearance! First in %d - Now in %d\n", testTx.hash.ToString(), testTx.blockHeight, pIndex->nHeight); + return false; + } else { + // If not save add it to the cache. + cache.AddTransaction(CSmartRewardTransaction(pIndex->nHeight, tx.GetHash())); } } - int nTime2 = GetTimeMicros(); - - BOOST_FOREACH(const CTxOut &out, tx.vout) { + return true; +} - if(out.scriptPubKey.IsZerocoinMint() ) continue; +void CSmartRewards::UndoInput(const CTransaction& tx, const CTxOut& in, uint16_t nCurrentRound, CSmartRewardsUpdateResult& result) +{ + uint16_t nFirst_1_3_Round = Params().GetConsensus().nRewardsFirst_1_3_Round; + CSmartRewardEntry* rEntry = nullptr; + CSmartAddress id; - std::vector ids; - int required = ParseScript(out.scriptPubKey ,ids); + if (!ExtractDestination(in.scriptPubKey, id)) { + LogPrint("smartrewards-tx", "CSmartRewards::UndoInput - Process Inputs: Could't parse CSmartAddress: %s\n", in.ToString()); + return; + } - if( !required || required > 1 || ids.size() > 1 ){ - LogPrint("smartrewards-tx", "CSmartRewards::ProcessTransaction - Process Outputs: Could't parse CSmartAddress: %s\n",out.ToString()); - continue; - }else{ + if (!GetRewardEntry(id, rEntry, true)) { + LogPrint("smartrewards-tx", "CSmartRewards::UndoInput - Spend without previous receive - %s", tx.ToString()); + return; + } - if(!GetCachedRewardEntry(ids.at(0),rEntry)){ - rEntry = new CSmartRewardEntry(ids.at(0)); - ReadRewardEntry(rEntry->id, *rEntry); - rewardEntries.insert(make_pair(ids.at(0), rEntry)); - } + rEntry->balance += in.nValue; - if( voteProofCheck ){ + if ( nCurrentRound >= nFirst_1_3_Round && rEntry->disqualifyingTx == tx.GetHash()) { + rEntry->disqualifyingTx.SetNull(); + rEntry->fDisqualifyingTx = false; - if( !out.IsVoteProofData() && - !(*voteProofCheck == rEntry->id) ){ + if (rEntry->IsEligible()) { + --result.disqualifiedEntries; + result.disqualifiedSmart -= rEntry->balanceEligible; + } - CSmartRewardEntry *vkEntry = nullptr; + } else if (nCurrentRound < nFirst_1_3_Round && rEntry->disqualifyingTx == tx.GetHash()) { + rEntry->disqualifyingTx.SetNull(); + rEntry->fDisqualifyingTx = false; - if(!GetCachedRewardEntry(*voteProofCheck,vkEntry)){ - vkEntry = new CSmartRewardEntry(*voteProofCheck); - ReadRewardEntry(vkEntry->id, *vkEntry); - rewardEntries.insert(make_pair(vkEntry->id, vkEntry)); - } + if (rEntry->balanceEligible) { + --result.disqualifiedEntries; + result.disqualifiedSmart -= rEntry->balanceEligible; + } + if(rEntry->balance < 0 ){ + LogPrint("smartrewards-tx", "CSmartRewards::UndoInput - Negative amount?! - %s", rEntry->ToString()); + rEntry->balance = 0; + } + } +} - if( vkEntry->IsEligible() ){ - result.disqualifiedEntries++; - result.disqualifiedSmart += vkEntry->balanceEligible; - } +void CSmartRewards::UndoOutput(const CTransaction& tx, const CTxOut& out, uint16_t nCurrentRound, CSmartRewardsUpdateResult& result) +{ + CSmartRewardEntry* rEntry = nullptr; + CSmartAddress id; - // Finally invalidate the balance since the change was not sent - // back to the sender! We don't want to allow - // a exploit to send around funds withouht breaking smartrewards. - vkEntry->disqualifyingTx = tx.GetHash(); - - }else if( !out.IsVoteProofData() && - (*voteProofCheck == rEntry->id) ){ - - CSmartRewardEntry *proofEntry = nullptr; - unsigned char cAddressType = 0; - uint32_t nProofRound; - uint160 addressHash; - uint256 nProposalHash; // Placeholder only for now - CSmartAddress proofAddress; - - BOOST_FOREACH(const CTxOut &outData, tx.vout) { - - if( outData.IsVoteProofData() ){ - - std::vector scriptData; - scriptData.insert(scriptData.end(), outData.scriptPubKey.begin() + 3, outData.scriptPubKey.end()); - CDataStream ss(scriptData, SER_NETWORK, 0); - - ss >> cProofOption; - ss >> nProofRound; - ss >> nProposalHash; - - if( cProofOption == 0x01 && - voteProofCheck->ToString(false) != Params().GetConsensus().strRewardsGlobalVoteProofAddress){ - proofAddress = *voteProofCheck; - proofEntry = rEntry; - }else if( cProofOption == 0x02 && - voteProofCheck->ToString(false) == Params().GetConsensus().strRewardsGlobalVoteProofAddress ){ - - ss >> cAddressType; - ss >> addressHash; - - if( cAddressType == 0x01 ){ - proofAddress = CSmartAddress(CKeyID(addressHash)); - }else if( cAddressType == 0x02){ - proofAddress = CSmartAddress(CScriptID(addressHash)); - }else{ - proofAddress = CSmartAddress(); // Invalid address type - } - - if(!GetCachedRewardEntry(proofAddress, proofEntry)){ - proofEntry = new CSmartRewardEntry(proofAddress); - ReadRewardEntry(proofEntry->id, *proofEntry); - rewardEntries.insert(make_pair(proofEntry->id, proofEntry)); - } - - }else{ - proofAddress = CSmartAddress(); // Invalid option - } - } - } - - if( proofAddress.IsValid() && proofEntry != nullptr && !SmartHive::IsHive(*voteProofCheck) ){ - - if( cProofOption == 0x01 && proofEntry->balanceEligible ){ - proofEntry->balanceEligible -= nVoteProofIn - tx.GetValueOut(); - - if( proofEntry->balanceEligible < 0 ){ - proofEntry->balanceEligible = 0; - } - } - - if( proofEntry->voteProof.IsNull() ){ - - if( nProofRound == currentRound.number ){ - proofEntry->voteProof = tx.GetHash(); - } - - // If the entry is eligible now after the vote proof update the results - if( proofEntry->IsEligible() ){ - result.qualifiedEntries++; - result.qualifiedSmart += proofEntry->balanceEligible; - } - - } - } + if (!ExtractDestination(out.scriptPubKey, id)) { + LogPrint("smartrewards-tx", "CSmartRewards::UndoOutput - Process Outputs: Could't parse CSmartAddress: %s\n", out.ToString()); + return; + } else { + GetRewardEntry(id, rEntry, true); + if (!tx.IsActivationTx() && !rEntry->fActivated) { + rEntry->activationTx.SetNull(); + rEntry->fActivated = false; + if (rEntry->disqualifyingTx == tx.GetHash()) { + rEntry->disqualifyingTx.SetNull(); + rEntry->fDisqualifyingTx = false; + if (rEntry->IsEligible()) { + --result.disqualifiedEntries; + result.disqualifiedSmart -= rEntry->balanceEligible; } - - delete voteProofCheck; } - - rEntry->balance += out.nValue; - - // If we are in the 1.3 cycles check for node rewards to remove node addresses from lists - if( nCurrentRound >= nFirst_1_3_Round && tx.IsCoinBase() ){ - - int nInterval = SmartNodePayments::PayoutInterval(nHeight); - int nPayoutsPerBlock = SmartNodePayments::PayoutsPerBlock(nHeight); - // Just to avoid potential zero divisions - nPayoutsPerBlock = std::max(1,nPayoutsPerBlock); - - CAmount nNodeReward = SmartNodePayments::Payment(nHeight) / nPayoutsPerBlock; - - // If we have an interval check if this is a node payout block - if( nInterval && !(nHeight % nInterval) ){ - - // If the amount matches and the entry is not yet marked as node do it - if( abs(out.nValue - nNodeReward ) < 2 ){ - - if( rEntry->smartnodePaymentTx.IsNull() ){ - - // If it is currently eligible adjust the round's results - if( rEntry->IsEligible() ){ - ++result.disqualifiedEntries; - result.disqualifiedSmart += rEntry->balanceEligible; - } - - rEntry->smartnodePaymentTx = tx.GetHash(); - } - } + } else if (!tx.IsActivationTx() && rEntry->fActivated && !SmartHive::IsHive(rEntry->id)) { + if (rEntry->activationTx == tx.GetHash()) { + rEntry->activationTx.SetNull(); + rEntry->fActivated = false; + --result.qualifiedEntries; + result.qualifiedSmart -= rEntry->balanceEligible; + } + } + rEntry->balance -= out.nValue; + + // If we are in the 1.3 cycles check for node rewards to remove node addresses from lists + if (Is_1_3(nCurrentRound) && tx.IsCoinBase()) { + if (rEntry->smartnodePaymentTx == tx.GetHash()) { + rEntry->smartnodePaymentTx.SetNull(); + rEntry->fSmartnodePaymentTx = false; + // If it is eligible now adjust the round's results + if (rEntry->IsEligible()) { + --result.disqualifiedEntries; + result.disqualifiedSmart -= rEntry->balanceEligible; } } } } - - int nTime3 = GetTimeMicros(); - int nTimeTx = nTime3 - nTime1; - - if( LogAcceptCategory("smartrewards-tx") ){ - LogPrint("smartrewards-tx", "CSmartRewards::ProcessTransaction - TX %s - %.2fms\n",HexStr(tx.GetHash()), nTimeTx * 0.001); - LogPrint("smartrewards-tx", " inputs - %.2fms\n", (nTime2 - nTime1) * 0.001); - LogPrint("smartrewards-tx", " outputs - %.2fms\n", (nTime3 - nTime2) * 0.001); + if (rEntry->balance < 0) { + LogPrint("smartrewards-tx", "CSmartRewards::UndoInput - Negative amount?! - %s", rEntry->ToString()); + rEntry->balance = 0; } } -void CSmartRewards::UndoTransaction(CBlockIndex* pIndex, const CTransaction& tx, CCoinsViewCache& coins, const CChainParams& chainparams, CSmartRewardsUpdateResult &result) +void CSmartRewards::UndoTransaction(CBlockIndex* pIndex, const CTransaction& tx, CCoinsViewCache& coins, const CChainParams& chainparams, CSmartRewardsUpdateResult& result) { - LogPrint("smartrewards-tx", "CSmartRewards::UndoTransaction - %s", tx.GetHash().ToString()); - - CSmartRewardEntry *rEntry = nullptr; - int nFirst_1_3_Round = chainparams.GetConsensus().nRewardsFirst_1_3_Round; - int nCurrentRound; { - LOCK(cs_rewardrounds); - nCurrentRound = currentRound.number; + LOCK(cs_rewardscache); + nCurrentRound = cache.GetCurrentRound()->number; } int nHeight = pIndex->nHeight; - if(nHeight > sporkManager.GetSporkValue(SPORK_15_SMARTREWARDS_BLOCKS_ENABLED)){ + if (nHeight > sporkManager.GetSporkValue(SPORK_15_SMARTREWARDS_BLOCKS_ENABLED)) { return; } int nTime1 = GetTimeMicros(); + CSmartRewardTransaction testTx; - // First check if the transaction hash did already come up in the past. - if( GetTransaction(tx.GetHash(), testTx) && testTx.blockHeight == pIndex->nHeight ){ - AddTransaction(testTx); - }else{ + if (GetTransaction(tx.GetHash(), testTx) && testTx.blockHeight == pIndex->nHeight) { + cache.RemoveTransaction(testTx); + } else if (nCurrentRound <= 4) { return; } - CSmartAddress *voteProofCheck = nullptr; - unsigned char cProofOption = 0; - CAmount nVoteProofIn = 0, nVoteProofOut = 0; - - if( nCurrentRound >= nFirst_1_3_Round && tx.IsVoteProof() ){ - - const Coin &coin = coins.AccessCoin(tx.vin[0].prevout); - const CTxOut &rOut = coin.out; - - std::vector ids; - - int required = ParseScript(rOut.scriptPubKey ,ids); - - if( !required || required > 1 || ids.size() > 1 ){ - LogPrint("smartrewards-tx", "CSmartRewards::UndoTransaction - Process VoteProof: Could't parse CSmartAddress: %s\n",rOut.ToString()); - return; - } - - nVoteProofIn = rOut.nValue; - voteProofCheck = new CSmartAddress(ids[0]); - } - - BOOST_REVERSE_FOREACH(const CTxOut &out, tx.vout) { - - if(out.scriptPubKey.IsZerocoinMint() ) continue; - - std::vector ids; - int required = ParseScript(out.scriptPubKey ,ids); - - if( !required || required > 1 || ids.size() > 1 ){ - LogPrint("smartrewards-tx", "CSmartRewards::UndoTransaction - Process Outputs: Could't parse CSmartAddress: %s\n",out.ToString()); + BOOST_REVERSE_FOREACH (const CTxOut& out, tx.vout) { + if (out.scriptPubKey.IsZerocoinMint()) continue; - }else{ - - if(!GetCachedRewardEntry(ids.at(0),rEntry)){ - rEntry = new CSmartRewardEntry(ids.at(0)); - ReadRewardEntry(rEntry->id, *rEntry); - rewardEntries.insert(make_pair(ids.at(0), rEntry)); - } - - if( voteProofCheck ){ - - nVoteProofOut += out.nValue; - - if( !out.IsVoteProofData() && - !(*voteProofCheck == rEntry->id) ){ - - CSmartRewardEntry *vkEntry = nullptr; - - if(!GetCachedRewardEntry(*voteProofCheck,vkEntry)){ - vkEntry = new CSmartRewardEntry(*voteProofCheck); - ReadRewardEntry(vkEntry->id, *vkEntry); - rewardEntries.insert(make_pair(vkEntry->id, vkEntry)); - } - - if( vkEntry->disqualifyingTx == tx.GetHash() ){ - - vkEntry->disqualifyingTx.SetNull(); - - if( vkEntry->IsEligible() ){ - --result.disqualifiedEntries; - result.disqualifiedSmart -= vkEntry->balanceEligible; - } - } - - }else if( !out.IsVoteProofData() && - (*voteProofCheck == rEntry->id) ){ - - CSmartRewardEntry *proofEntry = nullptr; - unsigned char cAddressType = 0; - uint32_t nProofRound; - uint160 addressHash; - uint256 nProposalHash; // Placeholder only for now - CSmartAddress proofAddress; - - BOOST_FOREACH(const CTxOut &outData, tx.vout) { - - if( outData.IsVoteProofData() ){ - - std::vector scriptData; - scriptData.insert(scriptData.end(), outData.scriptPubKey.begin() + 3, outData.scriptPubKey.end()); - CDataStream ss(scriptData, SER_NETWORK, 0); - - ss >> cProofOption; - ss >> nProofRound; - ss >> nProposalHash; - - if( cProofOption == 0x01 && - voteProofCheck->ToString(false) != Params().GetConsensus().strRewardsGlobalVoteProofAddress){ - proofAddress = *voteProofCheck; - proofEntry = rEntry; - }else if( cProofOption == 0x02 && - voteProofCheck->ToString(false) == Params().GetConsensus().strRewardsGlobalVoteProofAddress ){ - - ss >> cAddressType; - ss >> addressHash; - - if( cAddressType == 0x01 ){ - proofAddress = CSmartAddress(CKeyID(addressHash)); - }else if( cAddressType == 0x02){ - proofAddress = CSmartAddress(CScriptID(addressHash)); - }else{ - proofAddress = CSmartAddress(); // Invalid address type - } - - if(!GetCachedRewardEntry(proofAddress, proofEntry)){ - proofEntry = new CSmartRewardEntry(proofAddress); - ReadRewardEntry(proofEntry->id, *proofEntry); - rewardEntries.insert(make_pair(proofEntry->id, proofEntry)); - } - - }else{ - proofAddress = CSmartAddress(); // Invalid option - } - } - } - - if( proofAddress.IsValid() && proofEntry != nullptr && !SmartHive::IsHive(*voteProofCheck) ){ - - if( proofEntry->voteProof == tx.GetHash() ){ - - proofEntry->voteProof.SetNull(); - --result.qualifiedEntries; - result.qualifiedSmart -= proofEntry->balanceEligible; - - if( cProofOption == 0x01 ){ - proofEntry->balanceEligible += nVoteProofIn - nVoteProofOut; - } - - } - } - } - - delete voteProofCheck; - } - - rEntry->balance -= out.nValue; - - // If we are in the 1.3 cycles check for node rewards to remove node addresses from lists - if( nCurrentRound >= nFirst_1_3_Round && tx.IsCoinBase() ){ - - if( rEntry->smartnodePaymentTx == tx.GetHash() ){ - - rEntry->smartnodePaymentTx.SetNull(); - - // If it is eligible now adjust the round's results - if( rEntry->IsEligible() ){ - --result.disqualifiedEntries; - result.disqualifiedSmart -= rEntry->balanceEligible; - } - } - } - } + UndoOutput(tx, out, nCurrentRound, result); } int nTime2 = GetTimeMicros(); // No reason to check the input here for new coins. - if( !tx.IsCoinBase() ){ - - BOOST_REVERSE_FOREACH(const CTxIn &in, tx.vin) { - - if( in.scriptSig.IsZerocoinSpend() ) continue; - - const Coin &coin = coins.AccessCoin(in.prevout); - const CTxOut &rOut = coin.out; - - std::vector ids; - - int required = ParseScript(rOut.scriptPubKey ,ids); - - if( !required || required > 1 || ids.size() > 1 ){ - LogPrint("smartrewards-tx", "CSmartRewards::UndoTransaction - Process Inputs: Could't parse CSmartAddress: %s\n",rOut.ToString()); + if (!tx.IsCoinBase()) { + BOOST_REVERSE_FOREACH (const CTxIn& in, tx.vin) { + if (in.scriptSig.IsZerocoinSpend()) continue; - } - - if(!GetCachedRewardEntry(ids.at(0),rEntry)){ - - rEntry = new CSmartRewardEntry(ids.at(0)); - - if(!ReadRewardEntry(rEntry->id, *rEntry)){ - delete rEntry; - LogPrint("smartrewards-tx", "CSmartRewards::UndoTransaction - Spend without previous receive - %s", tx.ToString()); - continue; - } - - rewardEntries.insert(make_pair(ids.at(0), rEntry)); - } - - rEntry->balance += rOut.nValue; - - // If its a voteproof transaction not instantly make the - // balance ineligible. First check if the change is sent back - // to the address or not to avoid exploiting fund sending - // with voteproof transactions - if( nCurrentRound >= nFirst_1_3_Round && voteProofCheck == nullptr && rEntry->disqualifyingTx == tx.GetHash() ){ - - rEntry->disqualifyingTx.SetNull(); - - if( rEntry->IsEligible() ){ - --result.disqualifiedEntries; - result.disqualifiedSmart -= rEntry->balanceEligible; - } - - }else if( nCurrentRound < nFirst_1_3_Round && rEntry->disqualifyingTx == tx.GetHash() ){ - - rEntry->disqualifyingTx.SetNull(); - - if( rEntry->balanceEligible ){ - --result.disqualifiedEntries; - result.disqualifiedSmart -= rEntry->balanceEligible; - } - } - - if(rEntry->balance < 0 ){ - LogPrint("smartrewards-tx", "CSmartRewards::UndoTransaction - Negative amount?! - %s", rEntry->ToString()); - rEntry->balance = 0; - } + const Coin& coin = coins.AccessCoin(in.prevout); + const CTxOut& rOut = coin.out; + UndoInput(tx, rOut, nCurrentRound, result); } } int nTime3 = GetTimeMicros(); - int nTimeTx = nTime3 - nTime1; + double dProcessingTime = (nTime3 - nTime1) * 0.001; - if( LogAcceptCategory("smartrewards-tx") ){ - LogPrint("smartrewards-tx", "CSmartRewards::UndoTransaction - TX %s - %.2fms\n",HexStr(tx.GetHash()), nTimeTx * 0.001); + if (dProcessingTime > 10.0 && LogAcceptCategory("smartrewards-tx")) { + LogPrint("smartrewards-tx", "CSmartRewards::UndoTransaction - TX %s - %.2fms\n", tx.GetHash().ToString(), dProcessingTime); LogPrint("smartrewards-tx", " outputs - %.2fms\n", (nTime2 - nTime1) * 0.001); LogPrint("smartrewards-tx", " inputs - %.2fms\n", (nTime3 - nTime2) * 0.001); } @@ -934,29 +886,31 @@ bool CSmartRewards::CommitBlock(CBlockIndex* pIndex, const CSmartRewardsUpdateRe { int nTime1 = GetTimeMicros(); - if(pIndex && pIndex->nHeight > sporkManager.GetSporkValue(SPORK_15_SMARTREWARDS_BLOCKS_ENABLED)){ + if (pIndex && pIndex->nHeight > sporkManager.GetSporkValue(SPORK_15_SMARTREWARDS_BLOCKS_ENABLED)) { return true; } - if(!pIndex || pIndex->nHeight != currentBlock.nHeight + 1 ){ + LOCK(cs_rewardscache); + + const CSmartRewardRound* round = cache.GetCurrentRound(); + + if (!pIndex || pIndex->nHeight != cache.GetCurrentBlock()->nHeight + 1) { LogPrintf("CSmartRewards::CommitBlock - Invalid next block!"); return false; } - if(!SyncCached(result.block, false)){ - LogPrintf("CSmartRewards::CommitBlock - Failed to sync cache - Initial!"); - return false; + if (cache.GetLastRoundResult() && + cache.GetLastRoundResult()->fSynced && + pIndex->nHeight > cache.GetLastRoundResult()->round.GetLastRoundBlock()) { + cache.ClearResult(); } - // Update the current block to the processed one - currentBlock = result.block; + cache.ApplyRoundUpdateResult(result); // For the first round we have special parameter.. - if( !currentRound.number ){ - - if( (MainNet() && pIndex->GetBlockTime() > nFirstRoundStartTime) || - (TestNet() && pIndex->nHeight >= nFirstRoundStartBlock_Testnet) ){ - + if (!round->number) { + if ((MainNet() && pIndex->GetBlockTime() > nFirstRoundStartTime) || + (TestNet() && pIndex->nHeight >= nFirstRoundStartBlock_Testnet)) { // Create the very first smartrewards round. CSmartRewardRound first; first.number = 1; @@ -966,219 +920,381 @@ bool CSmartRewards::CommitBlock(CBlockIndex* pIndex, const CSmartRewardsUpdateRe // Estimate the block, gets updated on the end of the round to the real one. first.endBlockHeight = MainNet() ? nFirstRoundEndBlock : nFirstRoundEndBlock_Testnet; - CSmartRewardEntryList entries; - CSmartRewardRoundResultList results; - - // Get the current entries - if( !GetRewardEntries(entries) ){ - LogPrintf("CSmartRewards::CommitBlock - Failed to read all reward entries!"); - return false; - } - // Evaluate the round and update the next rounds parameter. - EvaluateRound(currentRound, first, entries, results ); - - CalculateRewardRatio(first); - - if( !StartFirstRound(first, entries) ){ - LogPrintf("CSmartRewards::CommitBlock - Failed to finalize round!"); - return false; - } - - currentRound = first; + EvaluateRound(first); } - - }else if( result.disqualifiedEntries || result.disqualifiedSmart || - result.qualifiedEntries || result.qualifiedSmart ){ - - // If there were disqualification during the last block processing - // update the current round stats. - - currentRound.disqualifiedEntries += result.disqualifiedEntries; - currentRound.disqualifiedSmart += result.disqualifiedSmart; - - currentRound.eligibleEntries += result.qualifiedEntries; - currentRound.eligibleSmart += result.qualifiedSmart; - - CalculateRewardRatio(currentRound); } // If just hit the next round threshold - if( ( MainNet() && currentRound.number < nRewardsFirstAutomatedRound - 1 && pIndex->GetBlockTime() > currentRound.endBlockTime ) || - ( ( TestNet() || currentRound.number >= nRewardsFirstAutomatedRound - 1 ) && pIndex->nHeight == currentRound.endBlockHeight ) ){ + if ((MainNet() && round->number < nRewardsFirstAutomatedRound - 1 && pIndex->GetBlockTime() > round->endBlockTime) || + ((TestNet() || round->number >= nRewardsFirstAutomatedRound - 1) && pIndex->nHeight == round->endBlockHeight)) { + UpdatePercentage(); - // Write the round to the history - currentRound.endBlockHeight = pIndex->nHeight; - currentRound.endBlockTime = pIndex->GetBlockTime(); - - CSmartRewardEntryList entries; - CSmartRewardRoundResultList results; + cache.UpdateRoundEnd(pIndex->nHeight, pIndex->GetBlockTime()); // Create the next round. CSmartRewardRound next; - next.number = currentRound.number + 1; - next.startBlockTime = currentRound.endBlockTime; - next.startBlockHeight = currentRound.endBlockHeight + 1; + next.number = round->number + 1; + next.startBlockTime = round->endBlockTime; + next.startBlockHeight = round->endBlockHeight + 1; int nBlocksPerRound = GetBlocksPerRound(next.number); time_t startTime = (time_t)next.startBlockTime; - if( MainNet() ){ - - if( next.number == nRewardsFirstAutomatedRound - 1 ){ + if (MainNet()) { + if (next.number == nRewardsFirstAutomatedRound - 1) { // Let the round 12 end at height 574099 so that round 13 starts at 574100 next.endBlockHeight = HF_V1_2_SMARTREWARD_HEIGHT - 1; - next.endBlockTime = startTime + ( (next.endBlockHeight - next.startBlockHeight) * 55 ); - }else if(next.number < nRewardsFirstAutomatedRound){ - + next.endBlockTime = startTime + ((next.endBlockHeight - next.startBlockHeight) * 55); + } else if (next.number < nRewardsFirstAutomatedRound) { boost::gregorian::date endDate = boost::posix_time::from_time_t(startTime).date(); endDate += boost::gregorian::months(1); // End date at 00:00:00 + 25200 seconds (7 hours) to match the date at 07:00 UTC next.endBlockTime = time_t((boost::posix_time::ptime(endDate, boost::posix_time::seconds(25200)) - epoch).total_seconds()); - next.endBlockHeight = next.startBlockHeight + ( (next.endBlockTime - next.startBlockTime) / 55 ); - }else{ + next.endBlockHeight = next.startBlockHeight + ((next.endBlockTime - next.startBlockTime) / 55); + } else { next.endBlockHeight = next.startBlockHeight + nBlocksPerRound - 1; next.endBlockTime = startTime + nBlocksPerRound * 55; } - }else{ + } else { next.endBlockHeight = next.startBlockHeight + nBlocksPerRound - 1; next.endBlockTime = startTime + nBlocksPerRound * 55; } - // Get the current entries - if( !GetRewardEntries(entries) ){ - LogPrintf("CSmartRewards::CommitBlock - Failed to read all reward entries!"); - return false; - } - - CalculateRewardRatio(currentRound); - // Evaluate the round and update the next rounds parameter. - EvaluateRound(currentRound, next, entries, results); - - CalculateRewardRatio(next); - - if( !FinalizeRound(currentRound, next, entries, results) ){ - LogPrintf("CSmartRewards::CommitBlock - Failed to finalize round!"); - return false; - } - - LOCK(cs_rewardrounds); - - finishedRounds.push_back(currentRound); - lastRound = currentRound; - currentRound = next; + EvaluateRound(next); } - if(!SyncCached()){ - LogPrintf("CSmartRewards::CommitBlock - Failed to sync cached - Processed!"); - return false; - } + UpdatePercentage(); - prewards->UpdateHeights(GetBlockHeight(pIndex), currentBlock.nHeight); + cache.UpdateHeights(GetBlockHeight(pIndex), cache.GetCurrentBlock()->nHeight); - int nTime2 = GetTimeMicros(); - - if( LogAcceptCategory("smartrewards-block") ){ - LogPrint("smartrewards-block", "Round %d - Block: %d - Progress %d%%\n",currentRound.number, currentBlock.nHeight, int(prewards->GetProgress() * 100)); - LogPrint("smartrewards-block", " Commit block: %.2fms\n", (nTime2 - nTime1) * 0.001); + if (LogAcceptCategory("smartrewards-bench")) { + int nTime2 = GetTimeMicros(); + double dProcessingTime = (nTime2 - nTime1) * 0.001; + LogPrint("smartrewards-bench", "Round %d - Block: %d\n", round->number, cache.GetCurrentBlock()->nHeight); + LogPrint("smartrewards-bench", " Commit block: %.2fms\n", dProcessingTime); } // If we are synced notify the UI on each new block. // If not notify the UI every nRewardsUISyncUpdateRate blocks to let it update the // loading screen. - if( IsSynced() || !(currentBlock.nHeight % nRewardsUISyncUpdateRate) ) + if (IsSynced() || !(cache.GetCurrentBlock()->nHeight % nRewardsUISyncUpdateRate)) uiInterface.NotifySmartRewardUpdate(); return true; } -bool CSmartRewards::CommitUndoBlock(CBlockIndex *pIndex, const CSmartRewardsUpdateResult &result) +bool CSmartRewards::CommitUndoBlock(CBlockIndex* pIndex, const CSmartRewardsUpdateResult& result) { int nTime1 = GetTimeMicros(); - if(pIndex && pIndex->nHeight > sporkManager.GetSporkValue(SPORK_15_SMARTREWARDS_BLOCKS_ENABLED)){ + if (pIndex && pIndex->nHeight > sporkManager.GetSporkValue(SPORK_15_SMARTREWARDS_BLOCKS_ENABLED)) { return true; } - if(!pIndex || pIndex->nHeight != currentBlock.nHeight ){ + LOCK2(cs_rewardscache, cs_rewardsdb); + + if (!pIndex || pIndex->nHeight != cache.GetCurrentBlock()->nHeight) { LogPrintf("CSmartRewards::CommitUndoBlock - Invalid next block!"); return false; } - if(!SyncCached(result.block, true)){ - LogPrintf("CSmartRewards::CommitUndoBlock - Failed to sync cache - Initial!"); - return false; + const CSmartRewardRound* pRound = cache.GetCurrentRound(); + + CSmartRewardsUpdateResult undoUpdate(pIndex->pprev->nHeight, pIndex->pprev->phashBlock, pIndex->pprev->GetBlockTime()); + + undoUpdate.disqualifiedEntries = result.disqualifiedEntries; + undoUpdate.disqualifiedSmart = result.disqualifiedSmart; + undoUpdate.qualifiedEntries = result.qualifiedEntries; + undoUpdate.qualifiedSmart = result.qualifiedSmart; + + cache.ApplyRoundUpdateResult(undoUpdate); + + // If just hit the last round's threshold + if ((MainNet() && pRound->number < nRewardsFirstAutomatedRound - 1 && pIndex->GetBlockTime() < pRound->endBlockTime) || + (((TestNet() && pRound->number > 1) || pRound->number >= nRewardsFirstAutomatedRound - 1) && pIndex->nHeight == pRound->startBlockHeight)) { + // Recover the last round from the history as current round + CSmartRewardRound prevRound = cache.GetRounds()->at(pRound->number - 1); + + cache.SetCurrentRound(prevRound); + cache.RemoveFinishedRound(prevRound.number); + + CSmartRewardsRoundResult* undoResult = new CSmartRewardsRoundResult(); + + undoResult->round = prevRound; + + if (!GetRewardRoundResults(prevRound.number, undoResult->results)) { + LogPrintf("CSmartRewards::CommitUndoBlock - Failed to read last round's results!"); + return false; + } + + cache.SetUndoResult(undoResult); + + // Load all entries into the cache + CSmartRewardEntryMap tmpEntries; + pdb->ReadRewardEntries(tmpEntries); + + for (auto itDb = tmpEntries.begin(); itDb != tmpEntries.end(); ++itDb) { + auto itCache = cache.GetEntries()->find(itDb->first); + + if (itCache == cache.GetEntries()->end()) { + cache.AddEntry(itDb->second); + } else { + delete itDb->second; + } + } } - currentBlock = CSmartRewardBlock(pIndex->pprev->nHeight, pIndex->pprev->GetBlockHash(), pIndex->pprev->GetBlockTime() ); + UpdatePercentage(); - if( result.disqualifiedEntries || result.disqualifiedSmart || - result.qualifiedEntries || result.qualifiedSmart ){ + cache.UpdateHeights(GetBlockHeight(pIndex), cache.GetCurrentBlock()->nHeight); - // If there were disqualification during the last block processing - // update the current round stats. + int nTime2 = GetTimeMicros(); - currentRound.disqualifiedEntries += result.disqualifiedEntries; - currentRound.disqualifiedSmart += result.disqualifiedSmart; + if (LogAcceptCategory("smartrewards-block")) { + LogPrint("smartrewards-block", "Round %d - Block: %d\n", pRound->number, cache.GetCurrentBlock()->nHeight); + LogPrint("smartrewards-block", " Commit undo block: %.2fms\n", (nTime2 - nTime1) * 0.001); + } - currentRound.eligibleEntries += result.qualifiedEntries; - currentRound.eligibleSmart += result.qualifiedSmart; + return true; +} + +CSmartRewardsCache::~CSmartRewardsCache() +{ + LOCK(cs_rewardscache); - CalculateRewardRatio(currentRound); + for (std::pair it : entries) { + delete it.second; } - // If just hit the last round's threshold - if( ( MainNet() && currentRound.number < nRewardsFirstAutomatedRound - 1 && pIndex->GetBlockTime() < currentRound.endBlockTime ) || - ( ( TestNet() || currentRound.number >= nRewardsFirstAutomatedRound - 1 ) && pIndex->nHeight == currentRound.startBlockHeight ) ){ + if (result) { + result->Clear(); + delete result; + } - LOCK2(cs_rewardsdb, cs_rewardrounds); + block = CSmartRewardBlock(); + round = CSmartRewardRound(); + rounds.clear(); + addTransactions.clear(); + removeTransactions.clear(); + entries.clear(); +} - // Recover the last round from the history as current round - currentRound = finishedRounds.back(); - finishedRounds.pop_back(); +unsigned long CSmartRewardsCache::EstimatedSize() +{ + unsigned long nEntriesSize = entries.size() * (sizeof(CSmartAddress) + sizeof(CSmartRewardEntry)); + unsigned long nRoundsSize = (rounds.size() + 1) * sizeof(CSmartRewardRound); + unsigned long nTransactionsSize = (addTransactions.size() + removeTransactions.size()) * (sizeof(uint256) + sizeof(CSmartRewardTransaction)); + unsigned long nBlockSize = sizeof(CSmartRewardBlock); + return nEntriesSize + nRoundsSize + nTransactionsSize + nBlockSize; +} - lastRound = finishedRounds.back(); +void CSmartRewardsCache::Load(const CSmartRewardBlock& block, const CSmartRewardRound& round, const CSmartRewardRoundMap& rounds) +{ + this->block = block; + this->round = round; + this->rounds = rounds; +} - CSmartRewardEntryList entries; - CSmartRewardRoundResultList results; +bool CSmartRewardsCache::NeedsSync() +{ + LOCK(cs_rewardscache); + return (result != nullptr && !result->fSynced) || + (undoResults != nullptr && !undoResults->fSynced) || + EstimatedSize() > REWARDS_MAX_CACHE || entries.size() > nCacheRewardEntries; +} - // Get the current entries - if( !GetRewardRoundResults(currentRound.number, results) ){ - LogPrintf("CSmartRewards::CommitUndoBlock - Failed to read last round's results!"); - return false; +void CSmartRewardsCache::Clear() +{ + LOCK(cs_rewardscache); + + if (result) { + result->fSynced = true; + } + + if (undoResults) { + undoResults->fSynced = true; + } + + for (auto it = entries.cbegin(); it != entries.cend(); it++) { + delete it->second; + } + + entries.clear(); + addTransactions.clear(); + removeTransactions.clear(); +} + +void CSmartRewardsCache::ClearResult() +{ + if (result) { + auto it = result->results.begin(); + + while (it != result->results.end()) { + delete *it; + ++it; } - CalculateRewardRatio(currentRound); + result->results.clear(); + result->payouts.clear(); + delete result; + result = nullptr; + } + + if (undoResults) { + auto it = undoResults->results.begin(); - if( !UndoFinalizeRound(currentRound, results) ){ - LogPrintf("CSmartRewards::CommitUndoBlock - Failed to finalize round!"); - return false; + while (it != undoResults->results.end()) { + delete *it; + ++it; } + undoResults->results.clear(); + undoResults->payouts.clear(); + delete undoResults; + undoResults = nullptr; } +} - if(!SyncCached(true)){ - LogPrintf("CSmartRewards::CommitUndoBlock - Failed to sync cached - Processed!"); - return false; +void CSmartRewardsCache::SetCurrentBlock(const CSmartRewardBlock& currentBlock) +{ + AssertLockHeld(cs_rewardscache); + block = currentBlock; +} + +void CSmartRewardsCache::SetCurrentRound(const CSmartRewardRound& currentRound) +{ + AssertLockHeld(cs_rewardscache); + round = currentRound; +} + +void CSmartRewardsCache::SetResult(CSmartRewardsRoundResult* pResult) +{ + AssertLockHeld(cs_rewardscache); + + if (result) { + result->Clear(); + delete result; } - prewards->UpdateHeights(GetBlockHeight(pIndex), currentBlock.nHeight); + result = pResult; +} - int nTime2 = GetTimeMicros(); +void CSmartRewardsCache::SetUndoResult(CSmartRewardsRoundResult* pResult) +{ + AssertLockHeld(cs_rewardscache); - if( LogAcceptCategory("smartrewards-block") ){ - LogPrint("smartrewards-block", "Round %d - Block: %d - Progress %d%%\n",currentRound.number, currentBlock.nHeight, int(prewards->GetProgress() * 100)); - LogPrint("smartrewards-block", " Commit undo block: %.2fms\n", (nTime2 - nTime1) * 0.001); + if (undoResults) { + undoResults->Clear(); + delete undoResults; } - // If we are synced notify the UI on each new block. - // If not notify the UI every nRewardsUISyncUpdateRate blocks to let it update the - // loading screen. - if( IsSynced() || !(currentBlock.nHeight % nRewardsUISyncUpdateRate) ) - uiInterface.NotifySmartRewardUpdate(); + undoResults = pResult; +} - return true; +void CSmartRewardsCache::ApplyRoundUpdateResult(const CSmartRewardsUpdateResult& result) +{ + AssertLockHeld(cs_rewardscache); + + if (result.IsValid()) { + SetCurrentBlock(result.block); + + round.disqualifiedEntries += result.disqualifiedEntries; + round.disqualifiedSmart += result.disqualifiedSmart; + + round.eligibleEntries += result.qualifiedEntries; + round.eligibleSmart += result.qualifiedSmart; + } +} + +void CSmartRewardsCache::UpdateRoundPayoutParameter(int64_t nBlockPayees, int64_t nBlockInterval) +{ + AssertLockHeld(cs_rewardscache); + + round.nBlockPayees = nBlockPayees; + round.nBlockInterval = nBlockInterval; +} + +void CSmartRewardsCache::UpdateRoundEnd(int nBlockHeight, int64_t nBlockTime) +{ + AssertLockHeld(cs_rewardscache); + + round.endBlockHeight = nBlockHeight; + round.endBlockTime = nBlockTime; +} + +void CSmartRewardsCache::UpdateRoundPercent(double dPercent) +{ + AssertLockHeld(cs_rewardscache); + + round.percent = dPercent; +} + +void CSmartRewardsCache::UpdateHeights(const int nHeight, const int nRewardHeight) +{ + AssertLockHeld(cs_rewardscache); + + chainHeight = nHeight; + rewardHeight = nRewardHeight; +} + +void CSmartRewardsCache::AddFinishedRound(const CSmartRewardRound& round) +{ + AssertLockHeld(cs_rewardscache); + + rounds[round.number] = round; +} + +void CSmartRewardsCache::RemoveFinishedRound(const int& nNumber) +{ + AssertLockHeld(cs_rewardscache); + + auto it = rounds.find(nNumber); + + if (it != rounds.end()) { + rounds.erase(it); + } +} + +void CSmartRewardsCache::AddTransaction(const CSmartRewardTransaction& transaction) +{ + LOCK(cs_rewardscache); + auto it = removeTransactions.find(transaction.hash); + + if (it != removeTransactions.end()) { + removeTransactions.erase(it); + } else { + addTransactions.insert(std::make_pair(transaction.hash, transaction)); + } +} + +void CSmartRewardsCache::RemoveTransaction(const CSmartRewardTransaction& transaction) +{ + LOCK(cs_rewardscache); + auto it = addTransactions.find(transaction.hash); + + if (it != addTransactions.end()) { + addTransactions.erase(it); + } else { + removeTransactions.insert(std::make_pair(transaction.hash, transaction)); + } +} + +void CSmartRewardsCache::AddEntry(CSmartRewardEntry* entry) +{ + LOCK(cs_rewardscache); + entries[entry->id] = entry; +} + +void CSmartRewardsRoundResult::Clear() +{ + for (CSmartRewardResultEntry* resultEntry : results) { + delete resultEntry; + } + + results.clear(); + payouts.clear(); } diff --git a/src/smartrewards/rewards.h b/src/smartrewards/rewards.h index 218dcfd5..5928ee9c 100644 --- a/src/smartrewards/rewards.h +++ b/src/smartrewards/rewards.h @@ -7,17 +7,18 @@ #include "sync.h" -#include #include "consensus/consensus.h" +#include using namespace std; -static const CAmount SMART_REWARDS_MIN_BALANCE = 1000 * COIN; -// Cache max. n prepared entries before the sync (leveldb batch write). -const int64_t nCacheEntires = 8000; -// Minimum distance of the last processed block compared to the current chain -// height to assume the rewards are synced. -const int64_t nRewardsSyncDistance = 150; +#define REWARDS_CACHE_ENTRIES_DEFAULT 50000 + +static const CAmount SMART_REWARDS_MIN_BALANCE_1_2 = 1000 * COIN; +static const CAmount SMART_REWARDS_MIN_BALANCE_1_3 = 10000 * COIN; + +// Seconds between current time and last blocktime which must be undershot for the rewards processing to assume its synced +const int64_t nRewardsSyncDistance = 600; // Number of blocks we update the SmartRewards UI when we are in the sync process const int64_t nRewardsUISyncUpdateRate = 500; @@ -31,97 +32,166 @@ const int64_t nFirstRoundStartBlock = 1; const int64_t nFirstRoundEndBlock = 60001; // Timestamps of the first round's start and end on testnet -const int64_t nRewardsSyncDistance_Testnet = 60; -const int64_t nFirstTxTimestamp_Testnet = 1527192589; +const int64_t nFirstTxTimestamp_Testnet = 1579594059; const int64_t nFirstRoundStartTime_Testnet = nFirstTxTimestamp_Testnet; -const int64_t nFirstRoundEndTime_Testnet = nFirstRoundStartTime_Testnet + (2*60*60); -const int64_t nFirstRoundStartBlock_Testnet = TESTNET_V1_2_PAYMENTS_HEIGHT; -const int64_t nFirstRoundEndBlock_Testnet = nFirstRoundStartBlock_Testnet + 1000; +const int64_t nFirstRoundEndTime_Testnet = nFirstRoundStartTime_Testnet + (1 * 60 * 60); +const int64_t nFirstRoundStartBlock_Testnet = TESTNET_V1_2_8_PAYMENTS_HEIGHT - 1; +const int64_t nFirstRoundEndBlock_Testnet = nFirstRoundStartBlock_Testnet + 100; void ThreadSmartRewards(bool fRecreate = false); CAmount CalculateRewardsForBlockRange(int64_t start, int64_t end); +extern CCriticalSection cs_rewardscache; extern CCriticalSection cs_rewardsdb; -extern CCriticalSection cs_rewardrounds; -struct CSmartRewardsUpdateResult -{ +extern size_t nCacheRewardEntries; + +struct CSmartRewardsUpdateResult { int64_t disqualifiedEntries; int64_t disqualifiedSmart; int64_t qualifiedEntries; int64_t qualifiedSmart; CSmartRewardBlock block; - CSmartRewardsUpdateResult(const int nHeight, const uint256* pBlockHash, const int64_t nBlockTime) : disqualifiedEntries(0), disqualifiedSmart(0), qualifiedEntries(0), qualifiedSmart(0), block(nHeight, pBlockHash, nBlockTime) { } + CSmartRewardsUpdateResult() : disqualifiedEntries(0), disqualifiedSmart(0), qualifiedEntries(0), qualifiedSmart(0), block() {} + CSmartRewardsUpdateResult(const int nHeight, const uint256* pBlockHash, const int64_t nBlockTime) : disqualifiedEntries(0), disqualifiedSmart(0), qualifiedEntries(0), qualifiedSmart(0), block(nHeight, pBlockHash, nBlockTime) {} + CSmartRewardsUpdateResult(const CBlockIndex* pIndex) : disqualifiedEntries(0), disqualifiedSmart(0), qualifiedEntries(0), qualifiedSmart(0), block() + { + if (pIndex && pIndex->phashBlock) { + block = CSmartRewardBlock(pIndex->nHeight, pIndex->phashBlock, pIndex->nTime); + } + } + + bool IsValid() const { return block.IsValid(); } }; -class CSmartRewards -{ - CSmartRewardsDB * pdb; - CSmartRewardRoundList finishedRounds; - CSmartRewardRound currentRound; - CSmartRewardRound lastRound; - CSmartRewardBlock currentBlock; +struct CSmartRewardsRoundResult { + CSmartRewardRound round; + CSmartRewardResultEntryPtrList results; + CSmartRewardResultEntryPtrList payouts; + bool fSynced; + CSmartRewardsRoundResult() { fSynced = false; } + void Clear(); +}; + +class CSmartRewardsCache +{ int chainHeight; int rewardHeight; - CSmartRewardTransactionList transactionEntries; - CSmartRewardEntryMap rewardEntries; + CSmartRewardBlock block; + CSmartRewardRound round; + CSmartRewardRoundMap rounds; + CSmartRewardTransactionMap addTransactions; + CSmartRewardTransactionMap removeTransactions; + CSmartRewardEntryMap entries; + CSmartRewardsRoundResult* result; + CSmartRewardsRoundResult* undoResults; + +public: + CSmartRewardsCache() : block(), round(), rounds(), addTransactions(), removeTransactions(), entries(), result(nullptr), undoResults(nullptr) {} + ~CSmartRewardsCache(); + + unsigned long EstimatedSize(); + + void Load(const CSmartRewardBlock& block, const CSmartRewardRound& round, const CSmartRewardRoundMap& rounds); + + bool NeedsSync(); + void Clear(); + void ClearResult(); + + void SetCurrentBlock(const CSmartRewardBlock& currentBlock); + void SetCurrentRound(const CSmartRewardRound& currentRound); + void SetResult(CSmartRewardsRoundResult* pResult); + void SetUndoResult(CSmartRewardsRoundResult* pResult); + + void ApplyRoundUpdateResult(const CSmartRewardsUpdateResult& result); + void UpdateRoundPayoutParameter(int64_t nBlockPayees, int64_t nBlockInterval); + void UpdateRoundEnd(int nBlockHeight, int64_t nBlockTime); + void UpdateRoundPercent(double dPercent); + void UpdateHeights(const int nHeight, const int nRewardHeight); + + const CSmartRewardBlock* GetCurrentBlock() const { return █ } + const CSmartRewardRound* GetCurrentRound() const { return &round; } + const CSmartRewardRoundMap* GetRounds() const { return &rounds; } + const CSmartRewardTransactionMap* GetAddedTransactions() const { return &addTransactions; } + const CSmartRewardTransactionMap* GetRemovedTransactions() const { return &removeTransactions; } + const CSmartRewardEntryMap* GetEntries() const { return &entries; } + const CSmartRewardsRoundResult* GetLastRoundResult() const { return result; } + const CSmartRewardsRoundResult* GetUndoResult() const { return undoResults; } + + void AddFinishedRound(const CSmartRewardRound& round); + void RemoveFinishedRound(const int& nNumber); + void AddTransaction(const CSmartRewardTransaction& transaction); + void RemoveTransaction(const CSmartRewardTransaction& transaction); + void AddEntry(CSmartRewardEntry* entry); +}; + +class CSmartRewards +{ + CSmartRewardsDB* pdb; + CSmartRewardsCache cache; mutable CCriticalSection csRounds; - void UpdatePayoutParameter(CSmartRewardRound &round); + void UpdateRoundPayoutParameter(); + void UpdatePercentage(); - bool GetCachedRewardEntry(const CSmartAddress &id, CSmartRewardEntry *&entry); - bool ReadRewardEntry(const CSmartAddress &id, CSmartRewardEntry &entry); - bool GetRewardEntries(CSmartRewardEntryList &entries); - void AddTransaction(const CSmartRewardTransaction &transaction); -public: + bool ReadRewardEntry(const CSmartAddress& id, CSmartRewardEntry& entry); + bool GetRewardEntries(CSmartRewardEntryMap& entries); - CSmartRewards(CSmartRewardsDB *prewardsdb); +public: + CSmartRewards(CSmartRewardsDB* prewardsdb); ~CSmartRewards() { delete pdb; } void Lock(); bool IsLocked(); - bool GetLastBlock(CSmartRewardBlock &block); - bool GetTransaction(const uint256 hash, CSmartRewardTransaction &transaction); - const CSmartRewardRound& GetCurrentRound(); - const CSmartRewardRound &GetLastRound(); - const CSmartRewardRoundList& GetRewardRounds(); + bool GetLastBlock(CSmartRewardBlock& block); + bool GetTransaction(const uint256 hash, CSmartRewardTransaction& transaction); + const CSmartRewardRound* GetCurrentRound(); + const CSmartRewardRoundMap* GetRewardRounds(); void UpdateHeights(const int nHeight, const int nRewardHeight); bool Verify(); - bool SyncCached(bool fUndo = false); - bool SyncCached(const CSmartRewardBlock &block, bool fUndo = false); + bool NeedsCacheWrite(); + bool SyncCached(); bool IsSynced(); - double GetProgress(); - int GetLastHeight(); int GetBlocksPerRound(const int nRound); - bool Update(CBlockIndex *pindexNew, const CChainParams& chainparams, const int nCurrentRound, CSmartRewardsUpdateResult &result); - bool UpdateRound(const CSmartRewardRound &round); + bool Update(CBlockIndex* pindexNew, const CChainParams& chainparams, const int nCurrentRound, CSmartRewardsUpdateResult& result); + bool UpdateRound(const CSmartRewardRound& round); + + void ProcessInput(const CTransaction& tx, const CTxOut& in, uint16_t nCurrentRound, CSmartRewardsUpdateResult& result); + void ProcessOutput(const CTransaction& tx, const CTxOut& out, uint16_t nCurrentRound, int nHeight, CSmartRewardsUpdateResult& result); + + void UndoInput(const CTransaction& tx, const CTxOut& in, uint16_t nCurrentRound, CSmartRewardsUpdateResult& result); + void UndoOutput(const CTransaction& tx, const CTxOut& out, uint16_t nCurrentRound, CSmartRewardsUpdateResult& result); + + bool ProcessTransaction(CBlockIndex* pIndex, const CTransaction& tx, int nCurrentRound); + void UndoTransaction(CBlockIndex* pIndex, const CTransaction& tx, CCoinsViewCache& coins, const CChainParams& chainparams, CSmartRewardsUpdateResult& result); - void StartBlock(); - void ProcessTransaction(CBlockIndex* pLastIndex, const CTransaction& tx, CCoinsViewCache& coins, const CChainParams& chainparams, CSmartRewardsUpdateResult &result); - void UndoTransaction(CBlockIndex* pIndex, const CTransaction& tx, CCoinsViewCache& coins, const CChainParams& chainparams, CSmartRewardsUpdateResult &result); bool CommitBlock(CBlockIndex* pIndex, const CSmartRewardsUpdateResult& result); bool CommitUndoBlock(CBlockIndex* pIndex, const CSmartRewardsUpdateResult& result); - bool GetRewardEntry(const CSmartAddress &id, CSmartRewardEntry &entry); + bool GetRewardEntry(const CSmartAddress& id, CSmartRewardEntry*& entry, bool fCreate); + + void EvaluateRound(CSmartRewardRound& next); + bool StartFirstRound(const CSmartRewardRound& next, const CSmartRewardEntryList& entries); + bool FinalizeRound(const CSmartRewardRound& current, const CSmartRewardRound& next, const CSmartRewardEntryList& entries, const CSmartRewardResultEntryList& results); + bool UndoFinalizeRound(const CSmartRewardRound& current, const CSmartRewardResultEntryList& results); - void EvaluateRound(CSmartRewardRound ¤t, CSmartRewardRound &next, CSmartRewardEntryList &entries, CSmartRewardRoundResultList &results); - bool StartFirstRound(const CSmartRewardRound &next, const CSmartRewardEntryList &entries); - bool FinalizeRound(const CSmartRewardRound ¤t, const CSmartRewardRound &next, const CSmartRewardEntryList &entries, const CSmartRewardRoundResultList &results); - bool UndoFinalizeRound(const CSmartRewardRound ¤t, const CSmartRewardRoundResultList &results); + bool GetRewardRoundResults(const int16_t round, CSmartRewardResultEntryList& results); + bool GetRewardRoundResults(const int16_t round, CSmartRewardResultEntryPtrList& results); + const CSmartRewardsRoundResult* GetLastRoundResult(); + bool GetRewardPayouts(const int16_t round, CSmartRewardResultEntryList& payouts); + bool GetRewardPayouts(const int16_t round, CSmartRewardResultEntryPtrList& payouts); - bool GetRewardRoundResults(const int16_t round, CSmartRewardRoundResultList &results); - bool GetRewardPayouts(const int16_t round, CSmartRewardRoundResultList &payouts); - bool GetRewardPayouts(const int16_t round, CSmartRewardRoundResultPtrList &payouts); + bool Is_1_3(uint16_t round); }; /** Global variable that points to the active rewards object (protected by cs_main) */ -extern CSmartRewards *prewards; +extern CSmartRewards* prewards; extern bool fSmartRewardsRunning; #endif // REWARDS_H diff --git a/src/smartrewards/rewardsdb.cpp b/src/smartrewards/rewardsdb.cpp index bb015438..a6b3fc20 100644 --- a/src/smartrewards/rewardsdb.cpp +++ b/src/smartrewards/rewardsdb.cpp @@ -6,16 +6,17 @@ #include "chainparams.h" #include "hash.h" -#include "pow.h" -#include "uint256.h" -#include "ui_interface.h" #include "init.h" +#include "pow.h" +#include "rewards.h" #include "rewardsdb.h" +#include "ui_interface.h" +#include "uint256.h" #include -#include #include "leveldb/include/leveldb/db.h" +#include using namespace std; @@ -29,16 +30,12 @@ static const char DB_BLOCK_LAST = 'b'; static const char DB_TX_HASH = 't'; static const char DB_VERSION = 'V'; -static const char DB_LOCK = 'L'; - -CSmartRewardsDB::CSmartRewardsDB(size_t nCacheSize, bool fMemory, bool fWipe) : CDBWrapper(GetDataDir() / "rewards", nCacheSize, fMemory, fWipe) { - locked = false; - - if( !Exists(DB_VERSION) ){ +CSmartRewardsDB::CSmartRewardsDB(size_t nCacheSize, bool fMemory, bool fWipe) : CDBWrapper(GetDataDir() / "rewards", nCacheSize, fMemory, fWipe) +{ + if (!Exists(DB_VERSION)) { Write(DB_VERSION, REWARDS_DB_VERSION); } - } bool CSmartRewardsDB::Verify(int& lastBlockHeight) @@ -49,97 +46,47 @@ bool CSmartRewardsDB::Verify(int& lastBlockHeight) lastBlockHeight = 0; - if( !Read(DB_VERSION, dbVersion) ){ + if (!Read(DB_VERSION, dbVersion)) { LogPrintf("CSmartRewards::Verify() Could't read DB_VERSION\n"); return false; } - if( dbVersion < REWARDS_DB_VERSION ){ + if (dbVersion < REWARDS_DB_VERSION) { LogPrintf("CSmartRewards::Verify() DB_VERSION too old.\n"); return false; } - if(!ReadLastBlock(last)){ + if (!ReadLastBlock(last)) { LogPrintf("CSmartRewards::Verify() No block here yet\n"); return true; } lastBlockHeight = last.nHeight; - LogPrintf("CSmartRewards::Verify() Verify blocks 1 - %d\n", last.nHeight); - - std::vector testBlocks; - - pcursor->Seek(DB_BLOCK); - - while (pcursor->Valid()) { - boost::this_thread::interruption_point(); - std::pair key; - if (pcursor->GetKey(key) && key.first == DB_BLOCK) { - CSmartRewardBlock nValue; - if (pcursor->GetValue(nValue)) { - if( nValue.nHeight != key.second ) return error("Block value %d contains wrong height: %s",key.second, nValue.ToString()); - pcursor->Next(); - testBlocks.push_back(nValue); - } else { - return error("failed to get block entry %d", key.second); - } - } else { - if( testBlocks.size() < size_t(last.nHeight) ) return error("Odd block count %d <> %d", testBlocks.size(), last.nHeight); - break; - } - } - - std::sort(testBlocks.begin(), testBlocks.end()); - vector::iterator it; - for(it = testBlocks.begin() + 1; it != testBlocks.end(); it++ ) { - if( (it-1)->nHeight + 1 != it->nHeight) return error("Block %d missing", it->nHeight); - } - return true; } -void CSmartRewardsDB::Lock() -{ - locked = true; - Write(DB_LOCK, 1, true); - Sync(); -} - -void CSmartRewardsDB::Unlock() -{ - if(locked){ - Erase(DB_LOCK,true); - Sync(); - } -} - -bool CSmartRewardsDB::IsLocked() -{ - return Exists(DB_LOCK); -} - -bool CSmartRewardsDB::ReadBlock(const int nHeight, CSmartRewardBlock &block) +bool CSmartRewardsDB::ReadBlock(const int nHeight, CSmartRewardBlock& block) { - return Read(make_pair(DB_BLOCK,nHeight), block); + return Read(make_pair(DB_BLOCK, nHeight), block); } -bool CSmartRewardsDB::ReadLastBlock(CSmartRewardBlock &block) +bool CSmartRewardsDB::ReadLastBlock(CSmartRewardBlock& block) { return Read(DB_BLOCK_LAST, block); } -bool CSmartRewardsDB::ReadTransaction(const uint256 hash, CSmartRewardTransaction &transaction) +bool CSmartRewardsDB::ReadTransaction(const uint256 hash, CSmartRewardTransaction& transaction) { - return Read(make_pair(DB_TX_HASH,hash), transaction); + return Read(make_pair(DB_TX_HASH, hash), transaction); } -bool CSmartRewardsDB::ReadRound(const int16_t number, CSmartRewardRound &round) +bool CSmartRewardsDB::ReadRound(const int16_t number, CSmartRewardRound& round) { - return Read(make_pair(DB_ROUND,number), round); + return Read(make_pair(DB_ROUND, number), round); } -bool CSmartRewardsDB::ReadRounds(CSmartRewardRoundList &vect) +bool CSmartRewardsDB::ReadRounds(CSmartRewardRoundMap& rounds) { boost::scoped_ptr pcursor(NewIterator()); @@ -147,11 +94,11 @@ bool CSmartRewardsDB::ReadRounds(CSmartRewardRoundList &vect) while (pcursor->Valid()) { boost::this_thread::interruption_point(); - std::pair key; + std::pair key; if (pcursor->GetKey(key) && key.first == DB_ROUND) { CSmartRewardRound nValue; if (pcursor->GetValue(nValue)) { - vect.push_back(nValue); + rounds.insert(make_pair(nValue.number, nValue)); pcursor->Next(); } else { return error("failed to get reward round"); @@ -164,110 +111,149 @@ bool CSmartRewardsDB::ReadRounds(CSmartRewardRoundList &vect) return true; } -bool CSmartRewardsDB::ReadCurrentRound(CSmartRewardRound &round) +bool CSmartRewardsDB::ReadCurrentRound(CSmartRewardRound& round) { return Read(DB_ROUND_CURRENT, round); } -bool CSmartRewardsDB::ReadRewardEntry(const CSmartAddress &id, CSmartRewardEntry &entry) +bool CSmartRewardsDB::ReadRewardEntry(const CSmartAddress& id, CSmartRewardEntry& entry) { - return Read(make_pair(DB_REWARD_ENTRY,id), entry); + return Read(make_pair(DB_REWARD_ENTRY, id), entry); } -bool CSmartRewardsDB::SyncCached(const CSmartRewardRound& current, const CSmartRewardEntryMap &rewards, const CSmartRewardTransactionList &transactions, bool fUndo) -{ - return SyncCached(CSmartRewardBlock(), current, rewards, transactions, fUndo); -} -bool CSmartRewardsDB::SyncCached(const CSmartRewardBlock &block, const CSmartRewardRound& current, const CSmartRewardEntryMap &rewards, const CSmartRewardTransactionList &transactions, bool fUndo) +bool CSmartRewardsDB::SyncCached(const CSmartRewardsCache& cache) { CDBBatch batch(*this); - BOOST_FOREACH(const PAIRTYPE(CSmartAddress, CSmartRewardEntry*)& r, rewards) { - if( r.second->balance <= 0 ){ - batch.Erase(make_pair(DB_REWARD_ENTRY,r.first)); - }else{ - batch.Write(make_pair(DB_REWARD_ENTRY,r.first), *r.second); + if (cache.GetUndoResult() != nullptr && !cache.GetUndoResult()->fSynced) { + CSmartRewardResultEntryPtrList tmpResults = cache.GetUndoResult()->results; + + auto entry = cache.GetEntries()->begin(); + + while (entry != cache.GetEntries()->end()) { + CSmartAddress searchAddress = entry->second->id; + + auto it = std::find_if(tmpResults.begin(), + tmpResults.end(), + [searchAddress](const CSmartRewardResultEntry* rEntry) -> bool { + return rEntry->entry.id == searchAddress; + }); + + if (it == tmpResults.end()) { + batch.Erase(make_pair(DB_REWARD_ENTRY, entry->first)); + } else { + CSmartRewardEntry rewardEntry = (*it)->entry; + + std::cout << rewardEntry.ToString() << std::endl; + + batch.Write(make_pair(DB_REWARD_ENTRY, rewardEntry.id), rewardEntry); + batch.Erase(make_pair(DB_ROUND_SNAPSHOT, make_pair(cache.GetUndoResult()->round.number, rewardEntry.id))); + tmpResults.erase(it); + } + + ++entry; } - } - BOOST_FOREACH(const CSmartRewardTransaction &t, transactions) { + auto it = tmpResults.begin(); + + while (it != tmpResults.end()) { + batch.Write(make_pair(DB_REWARD_ENTRY, (*it)->entry.id), (*it)->entry); + batch.Erase(make_pair(DB_ROUND_SNAPSHOT, make_pair(cache.GetUndoResult()->round.number, (*it)->entry.id))); - if( fUndo ){ - batch.Erase(make_pair(DB_TX_HASH, t.hash)); - }else{ - batch.Write(make_pair(DB_TX_HASH,t.hash), t); + ++it; } - } - if( block.IsValid() ){ - batch.Write(make_pair(DB_BLOCK,block.nHeight), block); - batch.Write(DB_BLOCK_LAST, block); - } + } else { + auto entry = cache.GetEntries()->begin(); - batch.Write(DB_ROUND_CURRENT, current); + while (entry != cache.GetEntries()->end()) { + if (entry->second->balance <= 0) { + batch.Erase(make_pair(DB_REWARD_ENTRY, entry->first)); + } else { + batch.Write(make_pair(DB_REWARD_ENTRY, entry->first), *entry->second); + } - return WriteBatch(batch); -} + ++entry; + } + } -bool CSmartRewardsDB::StartFirstRound(const CSmartRewardRound &start, const CSmartRewardEntryList &entries) -{ - CDBBatch batch(*this); + auto addTx = cache.GetAddedTransactions()->begin(); - BOOST_FOREACH(const CSmartRewardEntry &e, entries) { - batch.Write(make_pair(DB_REWARD_ENTRY,e.id), e); + while (addTx != cache.GetAddedTransactions()->end()) { + batch.Write(make_pair(DB_TX_HASH, addTx->first), addTx->second); + ++addTx; } - batch.Write(DB_ROUND_CURRENT, start); + auto removeTx = cache.GetRemovedTransactions()->begin(); - return WriteBatch(batch); -} + while (removeTx != cache.GetRemovedTransactions()->end()) { + batch.Erase(make_pair(DB_TX_HASH, removeTx->first)); + ++removeTx; + } -bool CSmartRewardsDB::FinalizeRound(const CSmartRewardRound ¤t, const CSmartRewardRound &next, const CSmartRewardEntryList &entries, const CSmartRewardRoundResultList &results) -{ - CDBBatch batch(*this); + auto round = cache.GetRounds()->begin(); - BOOST_FOREACH(const CSmartRewardRoundResult &s, results) { - batch.Write(make_pair(DB_ROUND_SNAPSHOT, make_pair(current.number, s.entry.id)), s); + while (round != cache.GetRounds()->end()) { + batch.Write(make_pair(DB_ROUND, round->first), round->second); + ++round; } - BOOST_FOREACH(const CSmartRewardEntry &e, entries) { - batch.Write(make_pair(DB_REWARD_ENTRY,e.id), e); + batch.Write(DB_BLOCK_LAST, *cache.GetCurrentBlock()); + + if (cache.GetCurrentRound()->number) { + batch.Write(DB_ROUND_CURRENT, *cache.GetCurrentRound()); } - batch.Write(make_pair(DB_ROUND,current.number), current); - batch.Write(DB_ROUND_CURRENT, next); + if (cache.GetLastRoundResult() != nullptr && !cache.GetLastRoundResult()->fSynced) { + BOOST_FOREACH (const CSmartRewardResultEntry* s, cache.GetLastRoundResult()->results) { + batch.Write(make_pair(DB_ROUND_SNAPSHOT, make_pair(cache.GetLastRoundResult()->round.number, s->entry.id)), *s); + } + } - return WriteBatch(batch); + return WriteBatch(batch, true); } -bool CSmartRewardsDB::UndoFinalizeRound(const CSmartRewardRound ¤t, const CSmartRewardRoundResultList &results) +bool CSmartRewardsDB::ReadRewardEntries(CSmartRewardEntryMap& entries) { - CDBBatch batch(*this); + boost::scoped_ptr pcursor(NewIterator()); - BOOST_FOREACH(const CSmartRewardRoundResult &s, results) { - batch.Erase(make_pair(DB_ROUND_SNAPSHOT, make_pair(current.number, s.entry.id))); - batch.Write(make_pair(DB_REWARD_ENTRY,s.entry.id), s.entry); - } + pcursor->Seek(DB_REWARD_ENTRY); - batch.Erase(make_pair(DB_ROUND, current.number)); - batch.Write(DB_ROUND_CURRENT, current); + while (pcursor->Valid()) { + boost::this_thread::interruption_point(); + std::pair key; + if (pcursor->GetKey(key) && key.first == DB_REWARD_ENTRY) { + CSmartRewardEntry entry; + if (pcursor->GetValue(entry)) { + entries.insert(std::make_pair(entry.id, new CSmartRewardEntry(entry))); + pcursor->Next(); + } else { + return error("failed to get reward entry"); + } + } else { + break; + } + } - return WriteBatch(batch); + return true; } -bool CSmartRewardsDB::ReadRewardEntries(CSmartRewardEntryList &entries) { - +bool CSmartRewardsDB::ReadRewardRoundResults(const int16_t round, CSmartRewardResultEntryList& results) +{ boost::scoped_ptr pcursor(NewIterator()); - pcursor->Seek(DB_REWARD_ENTRY); + pcursor->Seek(make_pair(DB_ROUND_SNAPSHOT, round)); while (pcursor->Valid()) { boost::this_thread::interruption_point(); - std::pair key; - if (pcursor->GetKey(key) && key.first == DB_REWARD_ENTRY) { - CSmartRewardEntry nValue; + std::pair > key; + if (pcursor->GetKey(key) && key.first == DB_ROUND_SNAPSHOT) { + if (key.second.first != round) + break; + + CSmartRewardResultEntry nValue; if (pcursor->GetValue(nValue)) { - entries.push_back(nValue); + results.push_back(nValue); pcursor->Next(); } else { return error("failed to get reward entry"); @@ -280,22 +266,22 @@ bool CSmartRewardsDB::ReadRewardEntries(CSmartRewardEntryList &entries) { return true; } -bool CSmartRewardsDB::ReadRewardRoundResults(const int16_t round, CSmartRewardRoundResultList &results) { - +bool CSmartRewardsDB::ReadRewardRoundResults(const int16_t round, CSmartRewardResultEntryPtrList& results) +{ boost::scoped_ptr pcursor(NewIterator()); - pcursor->Seek(make_pair(DB_ROUND_SNAPSHOT,round)); + pcursor->Seek(make_pair(DB_ROUND_SNAPSHOT, round)); while (pcursor->Valid()) { boost::this_thread::interruption_point(); - std::pair> key; + std::pair > key; if (pcursor->GetKey(key) && key.first == DB_ROUND_SNAPSHOT) { + if (key.second.first != round) + break; - if( key.second.first != round ) break; - - CSmartRewardRoundResult nValue; + CSmartRewardResultEntry nValue; if (pcursor->GetValue(nValue)) { - results.push_back(nValue); + results.push_back(new CSmartRewardResultEntry(nValue)); pcursor->Next(); } else { return error("failed to get reward entry"); @@ -308,22 +294,23 @@ bool CSmartRewardsDB::ReadRewardRoundResults(const int16_t round, CSmartRewardRo return true; } -bool CSmartRewardsDB::ReadRewardPayouts(const int16_t round, CSmartRewardRoundResultList &payouts) { - +bool CSmartRewardsDB::ReadRewardPayouts(const int16_t round, CSmartRewardResultEntryList& payouts) +{ boost::scoped_ptr pcursor(NewIterator()); - pcursor->Seek(make_pair(DB_ROUND_SNAPSHOT,round)); + pcursor->Seek(make_pair(DB_ROUND_SNAPSHOT, round)); while (pcursor->Valid()) { boost::this_thread::interruption_point(); - std::pair> key; + std::pair > key; if (pcursor->GetKey(key) && key.first == DB_ROUND_SNAPSHOT) { + if (key.second.first != round) + break; - if( key.second.first != round ) break; - - CSmartRewardRoundResult nValue; + CSmartRewardResultEntry nValue; if (pcursor->GetValue(nValue)) { - if( nValue.reward ) payouts.push_back(nValue); + if (nValue.reward) + payouts.push_back(nValue); pcursor->Next(); } else { return error("failed to get reward entry"); @@ -336,26 +323,28 @@ bool CSmartRewardsDB::ReadRewardPayouts(const int16_t round, CSmartRewardRoundRe return true; } -bool CSmartRewardsDB::ReadRewardPayouts(const int16_t round, CSmartRewardRoundResultPtrList &payouts) { - +bool CSmartRewardsDB::ReadRewardPayouts(const int16_t round, CSmartRewardResultEntryPtrList& payouts) +{ boost::scoped_ptr pcursor(NewIterator()); - pcursor->Seek(make_pair(DB_ROUND_SNAPSHOT,round)); + pcursor->Seek(make_pair(DB_ROUND_SNAPSHOT, round)); while (pcursor->Valid()) { boost::this_thread::interruption_point(); - std::pair> key; + std::pair > key; if (pcursor->GetKey(key) && key.first == DB_ROUND_SNAPSHOT) { + if (key.second.first != round) + break; - if( key.second.first != round ) break; - - CSmartRewardRoundResult nValue; + CSmartRewardResultEntry nValue; if (pcursor->GetValue(nValue)) { - if( nValue.reward ) payouts.push_back(new CSmartRewardRoundResult(nValue)); + if (nValue.reward) + payouts.push_back(new CSmartRewardResultEntry(nValue)); pcursor->Next(); } else { // Delete everything if something fails - for( auto it : payouts ) delete it; + for (auto it : payouts) + delete it; payouts.clear(); return error("failed to get reward entry"); } @@ -372,30 +361,37 @@ string CSmartRewardEntry::GetAddress() const return id.ToString(); } -void CSmartRewardEntry::setNull() +void CSmartRewardEntry::SetNull() { id = CSmartAddress(); balance = 0; + balanceAtStart = 0; balanceEligible = 0; + disqualifyingTx.SetNull(); + fDisqualifyingTx = false; smartnodePaymentTx.SetNull(); - voteProof.SetNull(); + fSmartnodePaymentTx = false; + activationTx.SetNull(); + fActivated = false; + bonusLevel = NoBonus; } string CSmartRewardEntry::ToString() const { std::stringstream s; - s << strprintf("CSmartRewardEntry(id=%s, balance=%d, balanceEligible=%d, isSmartNode=%b, voteProven=%b)\n", + s << strprintf("CSmartRewardEntry(id=%s, balance=%d, balanceEligible=%d, isSmartNode=%b, activated=%b, bonus=%d)\n", GetAddress(), balance, balanceEligible, - !smartnodePaymentTx.IsNull(), - !voteProof.IsNull()); + fSmartnodePaymentTx, + fActivated, + bonusLevel); return s.str(); } bool CSmartRewardEntry::IsEligible() { - return !voteProof.IsNull() && smartnodePaymentTx.IsNull() && balanceEligible > 0 && disqualifyingTx.IsNull(); + return fActivated && !fSmartnodePaymentTx && balanceEligible > 0 && !fDisqualifyingTx; } string CSmartRewardBlock::ToString() const @@ -403,11 +399,26 @@ string CSmartRewardBlock::ToString() const std::stringstream s; s << strprintf("CSmartRewardBlock(height=%d, hash=%s, time=%d)\n", nHeight, - blockHash.ToString(), - blockTime); + nHash.ToString(), + nTime); return s.str(); } +void CSmartRewardRound::UpdatePayoutParameter() +{ + nPayeeCount = eligibleEntries - disqualifiedEntries; + + if (nPayeeCount > 0 && nBlockPayees > 0) { + int64_t nPayoutDelay = Params().GetConsensus().nRewardsPayoutStartDelay; + + nRewardBlocks = nPayeeCount / nBlockPayees; + if (nPayeeCount % nBlockPayees) + nRewardBlocks += 1; + + nLastRoundBlock = endBlockHeight + nPayoutDelay + ((nRewardBlocks - 1) * nBlockInterval); + } +} + string CSmartRewardRound::ToString() const { std::stringstream s; @@ -424,24 +435,24 @@ string CSmartRewardRound::ToString() const return s.str(); } -string CSmartRewardRoundResult::GetAddress() const +string CSmartRewardResultEntry::GetAddress() const { return entry.id.ToString(); } -string CSmartRewardRoundResult::ToString() const +string CSmartRewardResultEntry::ToString() const { std::stringstream s; - s << strprintf("CSmartRewardRoundResult(id=%d, balance=%d, reward=%d\n", + s << strprintf("CSmartRewardResultEntry(id=%d, balance=%d, reward=%d\n", GetAddress(), entry.balance, reward); return s.str(); } -arith_uint256 CSmartRewardRoundResult::CalculateScore(const uint256& blockHash) +arith_uint256 CSmartRewardResultEntry::CalculateScore(const uint256& blockHash) { - // Deterministically calculate a "score" for a CSmartRewardRoundResult based on any given (block)hash + // Deterministically calculate a "score" for a CSmartRewardResultEntry based on any given (block)hash // Used to sort the payout list for 1.3 smartreward payouts CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); ss << reward << entry.id << blockHash; diff --git a/src/smartrewards/rewardsdb.h b/src/smartrewards/rewardsdb.h index 7d2dd2ab..9e54bfe1 100644 --- a/src/smartrewards/rewardsdb.h +++ b/src/smartrewards/rewardsdb.h @@ -12,7 +12,7 @@ #include "base58.h" #include "smarthive/hive.h" -static constexpr uint8_t REWARDS_DB_VERSION = 0x09; +static constexpr uint8_t REWARDS_DB_VERSION = 0x0A; //! Compensate for extra memory peak (x1.5-x1.9) at flush time. static constexpr int REWARDS_DB_PEAK_USAGE_FACTOR = 2; @@ -24,16 +24,17 @@ static const int64_t nRewardsMaxDbCache = sizeof(void*) > 4 ? 16384 : 1024; class CSmartRewardBlock; class CSmartRewardEntry; class CSmartRewardRound; -class CSmartRewardRoundResult; +class CSmartRewardResultEntry; class CSmartRewardTransaction; +class CSmartRewardsCache; typedef std::vector CSmartRewardBlockList; typedef std::vector CSmartRewardEntryList; -typedef std::vector CSmartRewardRoundList; -typedef std::vector CSmartRewardRoundResultList; -typedef std::vector CSmartRewardRoundResultPtrList; -typedef std::vector CSmartRewardTransactionList; +typedef std::map CSmartRewardRoundMap; +typedef std::vector CSmartRewardResultEntryList; +typedef std::vector CSmartRewardResultEntryPtrList; +typedef std::map CSmartRewardTransactionMap; typedef std::map CSmartRewardEntryMap; class CSmartRewardTransaction @@ -70,19 +71,19 @@ class CSmartRewardBlock public: int nHeight; - uint256 blockHash; - int64_t blockTime; - CSmartRewardBlock(){nHeight = 0; blockHash = uint256(); blockTime = 0;} - CSmartRewardBlock(int height, const uint256* pHash, int64_t time) : nHeight(height), blockHash(*pHash), blockTime(time) {} - CSmartRewardBlock(int height, const uint256 nHash, int64_t time) : nHeight(height), blockHash(nHash), blockTime(time) {} + uint256 nHash; + int64_t nTime; + CSmartRewardBlock() : nHeight(0), nHash(uint256()), nTime(0) {} + CSmartRewardBlock(int nHeight, const uint256* pHash, int64_t nTime) : nHeight(nHeight), nHash(*pHash), nTime(nTime) {} + CSmartRewardBlock(int nHeight, const uint256 nHash, int64_t nTime) : nHeight(nHeight), nHash(nHash), nTime(nTime) {} ADD_SERIALIZE_METHODS template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(nHeight); - READWRITE(blockHash); - READWRITE(blockTime); + READWRITE(nHash); + READWRITE(nTime); } friend bool operator==(const CSmartRewardBlock& a, const CSmartRewardBlock& b) @@ -102,11 +103,17 @@ class CSmartRewardBlock std::string ToString() const; - bool IsValid() const { return !blockHash.IsNull(); } + bool IsValid() const { return nHeight > 0; } }; class CSmartRewardRound { + + /* Memory only */ + int nPayeeCount; + int nRewardBlocks; + int nLastRoundBlock; + public: uint16_t number; int64_t startBlockHeight; @@ -139,6 +146,9 @@ class CSmartRewardRound nBlockInterval = 0; rewards = 0; percent = 0; + nPayeeCount = 0; + nRewardBlocks = 0; + nLastRoundBlock = 0; } ADD_SERIALIZE_METHODS @@ -158,6 +168,9 @@ class CSmartRewardRound READWRITE(nBlockInterval); READWRITE(rewards); READWRITE(percent); + + if( ser_action.ForRead()) + UpdatePayoutParameter(); } friend bool operator<(const CSmartRewardRound& a, const CSmartRewardRound& b) @@ -165,6 +178,13 @@ class CSmartRewardRound return a.number < b.number; } + void UpdatePayoutParameter(); + + int GetPayeeCount() const { return nPayeeCount; } + int GetRewardBlocks() const { return nRewardBlocks; } + int GetLastRoundBlock() const { return nLastRoundBlock; } + bool Is_1_3() const { return number >= Params().GetConsensus().nRewardsFirst_1_3_Round; } + std::string ToString() const; }; @@ -182,27 +202,45 @@ class CSmartRewardEntry READWRITE(balanceAtStart); READWRITE(balanceEligible); READWRITE(disqualifyingTx); - READWRITE(voteProof); + READWRITE(fDisqualifyingTx); + READWRITE(activationTx); + READWRITE(fActivated); READWRITE(smartnodePaymentTx); + READWRITE(fSmartnodePaymentTx); + READWRITE(bonusLevel); } + enum BonusLevel { + NoBonus = 0, + TwoMonthsBonus, + FourMonthsBonus, + SixMonthsBonus + }; + CSmartAddress id; CAmount balance; CAmount balanceAtStart; CAmount balanceEligible; - CAmount reward; uint256 disqualifyingTx; - uint256 voteProof; + bool fDisqualifyingTx; + uint256 activationTx; + bool fActivated; uint256 smartnodePaymentTx; + bool fSmartnodePaymentTx; + uint8_t bonusLevel; CSmartRewardEntry() : id(CSmartAddress()), - balance(0), balanceAtStart(0), balanceEligible(0), reward(0), - disqualifyingTx(uint256()), voteProof(uint256()), - smartnodePaymentTx(uint256()) {} + balance(0), balanceAtStart(0), balanceEligible(0), + disqualifyingTx(uint256()), fDisqualifyingTx(false), + activationTx(uint256()), fActivated(false), + smartnodePaymentTx(uint256()), fSmartnodePaymentTx(false), + bonusLevel(NoBonus) {} CSmartRewardEntry(const CSmartAddress &address) : id(address), - balance(0), balanceAtStart(0), balanceEligible(0), reward(0), - disqualifyingTx(uint256()), voteProof(uint256()), - smartnodePaymentTx(uint256()) {} + balance(0), balanceAtStart(0), balanceEligible(0), + disqualifyingTx(uint256()), fDisqualifyingTx(false), + activationTx(uint256()), fActivated(false), + smartnodePaymentTx(uint256()), fSmartnodePaymentTx(false), + bonusLevel(NoBonus) {} friend bool operator==(const CSmartRewardEntry& a, const CSmartRewardEntry& b) { @@ -215,12 +253,12 @@ class CSmartRewardEntry } std::string GetAddress() const; - void setNull(); + void SetNull(); std::string ToString() const; bool IsEligible(); }; -class CSmartRewardRoundResult +class CSmartRewardResultEntry { public: @@ -236,22 +274,22 @@ class CSmartRewardRoundResult READWRITE(reward); } - CSmartRewardRoundResult(){} + CSmartRewardResultEntry(){} - CSmartRewardRoundResult(CSmartRewardEntry &entry, CAmount nReward) : - entry(entry), reward(nReward){} + CSmartRewardResultEntry(CSmartRewardEntry *entry, CAmount nReward) : + entry(*entry), reward(nReward){} - friend bool operator==(const CSmartRewardRoundResult& a, const CSmartRewardRoundResult& b) + friend bool operator==(const CSmartRewardResultEntry& a, const CSmartRewardResultEntry& b) { return (a.entry.id == b.entry.id); } - friend bool operator!=(const CSmartRewardRoundResult& a, const CSmartRewardRoundResult& b) + friend bool operator!=(const CSmartRewardResultEntry& a, const CSmartRewardResultEntry& b) { return !(a == b); } - friend bool operator<(const CSmartRewardRoundResult& a, const CSmartRewardRoundResult& b) + friend bool operator<(const CSmartRewardResultEntry& a, const CSmartRewardResultEntry& b) { // TBD, verify this sort is fast/unique int cmp = a.entry.id.Compare(b.entry.id); @@ -270,42 +308,36 @@ class CSmartRewardsDB : public CDBWrapper { public: CSmartRewardsDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); - ~CSmartRewardsDB() { Unlock(); } + ~CSmartRewardsDB() {Sync();} private: CSmartRewardsDB(const CSmartRewardsDB&); void operator=(const CSmartRewardsDB&); - bool locked; - void Unlock(); public: bool Verify(int& lastBlockHeight); - void Lock(); - bool IsLocked(); - bool ReadBlock(const int nHeight, CSmartRewardBlock &block); bool ReadLastBlock(CSmartRewardBlock &block); bool ReadTransaction(const uint256 hash, CSmartRewardTransaction &transaction); bool ReadRound(const int16_t number, CSmartRewardRound &round); - bool ReadRounds(CSmartRewardRoundList &vect); + bool ReadRounds(CSmartRewardRoundMap &rounds); bool ReadCurrentRound(CSmartRewardRound &round); bool ReadRewardEntry(const CSmartAddress &id, CSmartRewardEntry &entry); - bool ReadRewardEntries(CSmartRewardEntryList &vect); + bool ReadRewardEntries(CSmartRewardEntryMap &entries); - bool ReadRewardRoundResults(const int16_t round, CSmartRewardRoundResultList &results); - bool ReadRewardPayouts(const int16_t round, CSmartRewardRoundResultList &payouts); - bool ReadRewardPayouts(const int16_t round, CSmartRewardRoundResultPtrList &payouts); + bool ReadRewardRoundResults(const int16_t round, CSmartRewardResultEntryList &results); + bool ReadRewardRoundResults(const int16_t round, CSmartRewardResultEntryPtrList &results); + bool ReadRewardPayouts(const int16_t round, CSmartRewardResultEntryList &payouts); + bool ReadRewardPayouts(const int16_t round, CSmartRewardResultEntryPtrList &payouts); - bool SyncCached(const CSmartRewardRound& current, const CSmartRewardEntryMap &rewards, const CSmartRewardTransactionList &transactions, bool fUndo = false); - bool SyncCached(const CSmartRewardBlock &block, const CSmartRewardRound& current, const CSmartRewardEntryMap &rewards, const CSmartRewardTransactionList &transactions, bool fUndo = false); - bool StartFirstRound(const CSmartRewardRound &start, const CSmartRewardEntryList &entries); - bool FinalizeRound(const CSmartRewardRound ¤t, const CSmartRewardRound &next, const CSmartRewardEntryList &entries, const CSmartRewardRoundResultList &results); - bool UndoFinalizeRound(const CSmartRewardRound ¤t, const CSmartRewardRoundResultList &results); + bool SyncCached(const CSmartRewardsCache &cache); + bool FinalizeRound(const CSmartRewardRound ¤t, const CSmartRewardRound &next, const CSmartRewardEntryList &entries, const CSmartRewardResultEntryList &results); + bool UndoFinalizeRound(const CSmartRewardRound ¤t, const CSmartRewardResultEntryList &results); }; diff --git a/src/smartrewards/rewardspayments.cpp b/src/smartrewards/rewardspayments.cpp index cd6e9052..84ac4322 100644 --- a/src/smartrewards/rewardspayments.cpp +++ b/src/smartrewards/rewardspayments.cpp @@ -16,114 +16,28 @@ #include -static std::pair *paymentData = nullptr; - -static void ResetPaymentData() -{ - if( paymentData ){ - - for( CSmartRewardRoundResult* s : paymentData->second ){ - delete s; - } - - delete paymentData; - } - - paymentData = new std::pair(); - paymentData->second.clear(); -} - -struct CompareRewardScore +CSmartRewardResultEntryPtrList SmartRewardPayments::GetPayments(const CSmartRewardsRoundResult *pResult, const int64_t nPayoutDelay, const int nHeight, int64_t blockTime, SmartRewardPayments::Result &result) { - bool operator()(const std::pair& s1, - const std::pair& s2) const - { - return (s1.first != s2.first) ? (s1.first < s2.first) : (s1.second->entry.balance < s2.second->entry.balance); - } -}; - -struct ComparePaymentPrtList -{ - bool operator()(const CSmartRewardRoundResult* p1, - const CSmartRewardRoundResult* p2) const - { - return *p1 < *p2; - } -}; - -CSmartRewardRoundResultPtrList SmartRewardPayments::GetPayments(const CSmartRewardRound &round, const int64_t nPayoutDelay, const int nHeight, int64_t blockTime, SmartRewardPayments::Result &result) -{ - int64_t nPayeeCount = round.eligibleEntries - round.disqualifiedEntries; + int64_t nPayeeCount = pResult->round.eligibleEntries - pResult->round.disqualifiedEntries; // If we have no eligible addresses. Just to make sure...wont happen. if( nPayeeCount <= 0 ){ result = SmartRewardPayments::NoRewardBlock; - return CSmartRewardRoundResultPtrList(); + return CSmartRewardResultEntryPtrList(); } - int nFirst_1_3_Round = Params().GetConsensus().nRewardsFirst_1_3_Round; - - int64_t nBlockPayees = round.nBlockPayees; - int64_t nPayoutInterval = round.nBlockInterval; + int64_t nBlockPayees = pResult->round.nBlockPayees; + int64_t nPayoutInterval = pResult->round.nBlockInterval; int64_t nRewardBlocks = nPayeeCount / nBlockPayees; // If we dont match nRewardsPayoutsPerBlock add one more block for the remaining payments. if( nPayeeCount % nBlockPayees ) nRewardBlocks += 1; - int64_t nLastRoundBlock = round.endBlockHeight + nPayoutDelay + ( (nRewardBlocks - 1) * nPayoutInterval ); + int64_t nLastRoundBlock = pResult->round.endBlockHeight + nPayoutDelay + ( (nRewardBlocks - 1) * nPayoutInterval ); if( nHeight <= nLastRoundBlock && !(( nLastRoundBlock - nHeight ) % nPayoutInterval) ){ // We have a reward block! Now try to create the payments vector. - // Do this stuff only one time and leave it in memory until all blocks are paid - if( !paymentData || paymentData->first != round.number ){ - - ResetPaymentData(); - - if( round.number < nFirst_1_3_Round ){ - - if( !prewards->GetRewardPayouts( round.number, paymentData->second ) || - paymentData->second.size() != static_cast(nPayeeCount) ){ - result = SmartRewardPayments::DatabaseError; - return CSmartRewardRoundResultPtrList(); - } - - // Sort it to make sure the slices are the same network wide. - std::sort(paymentData->second.begin(), paymentData->second.end(), ComparePaymentPrtList() ); - - }else{ - - CSmartRewardRoundResultPtrList roundPayments; - if( !prewards->GetRewardPayouts( round.number, roundPayments ) || - roundPayments.size() != static_cast(nPayeeCount) ){ - result = SmartRewardPayments::DatabaseError; - return CSmartRewardRoundResultPtrList(); - } - - uint256 blockHash; - if(!GetBlockHash(blockHash, round.startBlockHeight)) { - LogPrintf("SmartRewardPayments::GetPayments_1_3 -- ERROR: GetBlockHash() failed at nBlockHeight %d\n", round.startBlockHeight); - result = SmartRewardPayments::CoreError; - return CSmartRewardRoundResultPtrList(); - } - - std::vector> vecScores; - // Since we use payouts stretched out over a week better to have some "random" sort here - // based on a score calculated with the round start's blockhash. - for (auto s : roundPayments) { - arith_uint256 nScore = s->CalculateScore(blockHash); - vecScores.push_back(std::make_pair(nScore,s)); - } - - std::sort(vecScores.begin(), vecScores.end(), CompareRewardScore()); - - for(auto s : vecScores) - paymentData->second.push_back(s.second); - } - // And set the round number the payment data belongs to - paymentData->first = round.number; - } - // Index of the current payout block for this round. int64_t nRewardBlock = nRewardBlocks - ( (nLastRoundBlock - nHeight) / nPayoutInterval ); int64_t nFinalBlockPayees = nBlockPayees; @@ -140,71 +54,67 @@ CSmartRewardRoundResultPtrList SmartRewardPayments::GetPayments(const CSmartRewa // As ennd index we use the startIndex + number of payees for this round. size_t nEndIndex = nStartIndex + nFinalBlockPayees; // If for any reason the calculations end up in an overflow of the vector return an error. - if( nEndIndex > paymentData->second.size() ){ + if( nEndIndex > pResult->payouts.size() ){ // Should not happen! result = SmartRewardPayments::DatabaseError; - return CSmartRewardRoundResultPtrList(); + return CSmartRewardResultEntryPtrList(); } // Finally return the subvector with the payees of this blockHeight! - return CSmartRewardRoundResultPtrList(paymentData->second.begin() + nStartIndex, paymentData->second.begin() + nEndIndex); + return CSmartRewardResultEntryPtrList(pResult->payouts.begin() + nStartIndex, pResult->payouts.begin() + nEndIndex); } // If we arent in any rounds payout range! result = SmartRewardPayments::NoRewardBlock; - return CSmartRewardRoundResultPtrList(); + return CSmartRewardResultEntryPtrList(); } -CSmartRewardRoundResultPtrList SmartRewardPayments::GetPaymentsForBlock(const int nHeight, int64_t blockTime, SmartRewardPayments::Result &result) +CSmartRewardResultEntryPtrList SmartRewardPayments::GetPaymentsForBlock(const int nHeight, int64_t blockTime, SmartRewardPayments::Result &result) { result = SmartRewardPayments::Valid; if(nHeight > sporkManager.GetSporkValue(SPORK_15_SMARTREWARDS_BLOCKS_ENABLED)) { LogPrint("smartrewards", "SmartRewardPayments::GetPaymentsForBlock -- Disabled"); result = SmartRewardPayments::NoRewardBlock; - return CSmartRewardRoundResultPtrList(); + return CSmartRewardResultEntryPtrList(); } // If we are not yet at the 1.2 payout block time. if( ( MainNet() && nHeight < HF_V1_2_SMARTREWARD_HEIGHT + Params().GetConsensus().nRewardsBlocksPerRound_1_2 ) || ( TestNet() && nHeight < nFirstRoundEndBlock_Testnet ) ){ result = SmartRewardPayments::NoRewardBlock; - return CSmartRewardRoundResultPtrList(); + return CSmartRewardResultEntryPtrList(); } - CSmartRewardRound round; - - { - LOCK(cs_rewardrounds); - round = prewards->GetLastRound(); - } + const CSmartRewardsRoundResult *pResult = prewards->GetLastRoundResult(); // If there are no rounds yet or the database has an issue. - if( !round.number ){ + if( !pResult ){ result = SmartRewardPayments::NoRewardBlock; - return CSmartRewardRoundResultPtrList(); + return CSmartRewardResultEntryPtrList(); } // If the requested height is lower then the rounds end step forward to the // next round. int64_t nPayoutDelay = Params().GetConsensus().nRewardsPayoutStartDelay; - if( nHeight >= ( round.endBlockHeight + nPayoutDelay ) ){ - return SmartRewardPayments::GetPayments( round, nPayoutDelay, nHeight, blockTime, result ); + if( nHeight >= ( pResult->round.endBlockHeight + nPayoutDelay ) ){ + return SmartRewardPayments::GetPayments( pResult, nPayoutDelay, nHeight, blockTime, result ); } // If we arent in any rounds payout range! result = SmartRewardPayments::NoRewardBlock; - return CSmartRewardRoundResultPtrList(); + return CSmartRewardResultEntryPtrList(); } void SmartRewardPayments::FillPayments(CMutableTransaction &coinbaseTx, int nHeight, int64_t prevBlockTime, std::vector& voutSmartRewards) { + LOCK(cs_rewardscache); SmartRewardPayments::Result result; - CSmartRewardRoundResultPtrList rewards = SmartRewardPayments::GetPaymentsForBlock(nHeight, prevBlockTime, result); + CSmartRewardResultEntryPtrList rewards = SmartRewardPayments::GetPaymentsForBlock(nHeight, prevBlockTime, result); // only create rewardblocks if a rewardblock is actually required at the current height. if( result == SmartRewardPayments::Valid && rewards.size() ) { @@ -224,13 +134,15 @@ void SmartRewardPayments::FillPayments(CMutableTransaction &coinbaseTx, int nHei SmartRewardPayments::Result SmartRewardPayments::Validate(const CBlock& block, int nHeight, CAmount &smartReward) { + LOCK(cs_rewardscache); + SmartRewardPayments::Result result; smartReward = 0; const CTransaction &txCoinbase = block.vtx[0]; - CSmartRewardRoundResultPtrList rewards = SmartRewardPayments::GetPaymentsForBlock(nHeight, block.GetBlockTime(), result); + CSmartRewardResultEntryPtrList rewards = SmartRewardPayments::GetPaymentsForBlock(nHeight, block.GetBlockTime(), result); if( result == SmartRewardPayments::Valid && rewards.size() ) { diff --git a/src/smartrewards/rewardspayments.h b/src/smartrewards/rewardspayments.h index bbbbaeb9..70a6c42b 100644 --- a/src/smartrewards/rewardspayments.h +++ b/src/smartrewards/rewardspayments.h @@ -12,6 +12,8 @@ #include "coins.h" #include "base58.h" +struct CSmartRewardsRoundResult; + namespace SmartRewardPayments{ typedef enum{ @@ -23,8 +25,8 @@ typedef enum{ CoreError } Result; -CSmartRewardRoundResultPtrList GetPayments(const CSmartRewardRound &round, const int64_t nPayoutDelay, const int nHeight, int64_t blockTime, SmartRewardPayments::Result &result); -CSmartRewardRoundResultPtrList GetPaymentsForBlock(const int nHeight, int64_t blockTime, SmartRewardPayments::Result &result); +CSmartRewardResultEntryPtrList GetPayments(const CSmartRewardsRoundResult *pResult, const int64_t nPayoutDelay, const int nHeight, int64_t blockTime, SmartRewardPayments::Result &result); +CSmartRewardResultEntryPtrList GetPaymentsForBlock(const int nHeight, int64_t blockTime, SmartRewardPayments::Result &result); SmartRewardPayments::Result Validate(const CBlock& block, const int nHeight, CAmount& smartReward); void FillPayments(CMutableTransaction& txNew, int nHeight, int64_t prevBlockTime, std::vector& voutSmartRewards); diff --git a/src/smartvoting/proposal.cpp b/src/smartvoting/proposal.cpp index 546d411b..2cf0762a 100644 --- a/src/smartvoting/proposal.cpp +++ b/src/smartvoting/proposal.cpp @@ -525,8 +525,6 @@ bool CProposal::IsCollateralValid(std::string& strError, int& fMissingConfirmati CScript findDataScript; findDataScript << OP_RETURN << ToByteVector(nExpectedHash); - CScript findHiveScript = SmartHive::Script(SmartHive::ProjectTreasury); - bool fFoundOpReturn = false; bool fFoundFee = false; for (const auto& output : txCollateral.vout) { @@ -539,10 +537,6 @@ bool CProposal::IsCollateralValid(std::string& strError, int& fMissingConfirmati LogPrintf ("CProposal::IsCollateralValid -- %s\n", strError); return false; } - if(output.scriptPubKey == findHiveScript && output.nValue >= nMinFee) { - DBG( std::cout << "IsCollateralValid fFoundFee = true" << std::endl; ); - fFoundFee = true; - } if(output.scriptPubKey == findDataScript && output.nValue == 0) { DBG( std::cout << "IsCollateralValid fFoundOpReturn = true" << std::endl; ); fFoundOpReturn = true; diff --git a/src/txmempool.cpp b/src/txmempool.cpp index be98da40..383aa4e4 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -450,6 +450,26 @@ void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewC CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n); mapAddress.insert(make_pair(key, delta)); inserted.push_back(key); + } else if (prevout.scriptPubKey.IsPayToPublicKeyHashLocked() ) { + + int nOffset = prevout.scriptPubKey[0] + 6; + + vector hashBytes(prevout.scriptPubKey.begin() + nOffset, prevout.scriptPubKey.begin() + nOffset + 20); + + CMempoolAddressDeltaKey key(1, uint160(hashBytes), txhash, j, 1); + CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n); + mapAddress.insert(make_pair(key, delta)); + inserted.push_back(key); + } else if (prevout.scriptPubKey.IsPayToScriptHashLocked() ) { + + int nOffset = prevout.scriptPubKey[0] + 5; + + vector hashBytes(prevout.scriptPubKey.begin() + nOffset, prevout.scriptPubKey.begin() + nOffset + 20); + + CMempoolAddressDeltaKey key(2, uint160(hashBytes), txhash, j, 1); + CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n); + mapAddress.insert(make_pair(key, delta)); + inserted.push_back(key); } } @@ -477,6 +497,25 @@ void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewC CMempoolAddressDeltaKey key(1, nPubKeyHash, txhash, k, 0); mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue))); inserted.push_back(key); + } else if (out.scriptPubKey.IsPayToPublicKeyHashLocked() ) { + + int nOffset = out.scriptPubKey[0] + 6; + + vector hashBytes(out.scriptPubKey.begin() + nOffset, out.scriptPubKey.begin() + nOffset + 20); + + std::pair ret; + CMempoolAddressDeltaKey key(1, uint160(hashBytes), txhash, k, 0); + mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue))); + inserted.push_back(key); + } else if (out.scriptPubKey.IsPayToScriptHashLocked() ) { + + int nOffset = out.scriptPubKey[0] + 5; + + vector hashBytes(out.scriptPubKey.begin() + nOffset, out.scriptPubKey.begin() + nOffset + 20); + + CMempoolAddressDeltaKey key(2, uint160(hashBytes), txhash, k, 0); + mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue))); + inserted.push_back(key); } } @@ -542,6 +581,18 @@ void CTxMemPool::addSpentIndex(const CTxMemPoolEntry &entry, const CCoinsViewCac } addressType = 1; + } else if (prevout.scriptPubKey.IsPayToPublicKeyHashLocked()) { + + int nOffset = prevout.scriptPubKey[0] + 6; + + addressHash = uint160(vector (prevout.scriptPubKey.begin() + nOffset, prevout.scriptPubKey.begin() + nOffset + 20)); + addressType = 1; + } else if (prevout.scriptPubKey.IsPayToScriptHashLocked()) { + + int nOffset = prevout.scriptPubKey[0] + 5; + + addressHash = uint160(vector (prevout.scriptPubKey.begin() + nOffset, prevout.scriptPubKey.begin() + nOffset + 20)); + addressType = 2; } else { addressHash.SetNull(); addressType = 0; diff --git a/src/util.cpp b/src/util.cpp index 05d5eb95..84f5c7db 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -109,7 +109,7 @@ int nWalletBackups = 10; const char * const BITCOIN_CONF_FILENAME = "smartcash.conf"; const char * const BITCOIN_PID_FILENAME = "smartcashd.pid"; -const std::string args[136] = {"version", "alertnotify", "blocknotify", "blocksonly", "checkblocks", "checklevel", "conf", "daemon", "datadir", "dbcache", "feefilter", "loadblock", "maxorphantx", "maxmempool", "mempoolexpiry", "par", "pid", "prune", "reindex-chainstate", "reindex", "sysperms", "depositindex", "addnode", "banscore", "bantime", "bind", "connect", "discover", "dns", "dnsseed", "externalip", "forcednsseed", "listen", "listenonion", "maxconnections", "maxreceivebuffer", "maxsendbuffer", "maxtimeadjustment", "onion", "onlynet", "permitbaremultisig", "peerbloomfilters", "port", "proxy", "proxyrandomize", "rpcserialversion", "seednode", "timeout", "torcontrol", "torpassword", "upnp", "whitebind", "whitelist", "whitelistrelay", "whitelistforcerelay", "maxuploadtarget", "zmqpubhashblock", "zmqpubhashtx", "zmqpubrawblock", "zmqpubrawtx", "uacomment", "checkblockindex", "checkmempool", "checkpoints", "disablesafemode", "testsafemode", "dropmessagestest", "fuzzmessagestest", "stopafterblockimport", "limitancestorcount", "limitancestorsize", "limitdescendantcount", "limitdescendantsize", "bip9params", "debug", "nodebug", "help-debug", "logips", "logtimestamps", "logtimemicros", "mocktime", "limitfreerelay", "relaypriority", "maxsigcachesize", "maxtipage", "minrelaytxfee", "maxtxfee", "printtoconsole", "printpriority", "shrinkdebugfile", "acceptnonstdtxn", "bytespersigop", "datacarrier", "datacarriersize", "mempoolreplacement", "blockmaxweight", "blockmaxsize", "txmaxcount", "blockprioritysize", "blockversion", "server", "rest", "rpcbind", "rpccookiefile", "rpcuser", "rpcpassword", "rpcauth", "rpcport", "rpcallowip", "rpcthreads", "rpcworkqueue", "rpcservertimeout", "help", "?", "disablewallet", "keypool", "fallbackfee", "mintxfee", "paytxfee", "rescan", "salvagewallet", "sendfreetransactions", "spendzeroconfchange", "txconfirmtarget", "usehd", "upgradewallet", "wallet", "walletbroadcast", "walletnotify", "zapwallettxes", "dblogsize", "flushwallet", "privdb", "walletrejectlongchains", "testnet"}; +const std::vector args = {"version", "alertnotify", "blocknotify", "blocksonly", "checkblocks", "checklevel", "conf", "daemon", "datadir", "dbcache", "feefilter", "loadblock", "maxorphantx", "maxmempool", "mempoolexpiry", "par", "pid", "prune", "reindex-chainstate", "reindex", "sysperms", "depositindex", "addnode", "banscore", "bantime", "bind", "connect", "discover", "dns", "dnsseed", "externalip", "forcednsseed", "listen", "listenonion", "maxconnections", "maxreceivebuffer", "maxsendbuffer", "maxtimeadjustment", "onion", "onlynet", "permitbaremultisig", "peerbloomfilters", "port", "proxy", "proxyrandomize", "rpcserialversion", "seednode", "timeout", "torcontrol", "torpassword", "upnp", "whitebind", "whitelist", "whitelistrelay", "whitelistforcerelay", "maxuploadtarget", "zmqpubhashblock", "zmqpubhashtx", "zmqpubrawblock", "zmqpubrawtx", "uacomment", "checkblockindex", "checkmempool", "checkpoints", "disablesafemode", "testsafemode", "dropmessagestest", "fuzzmessagestest", "stopafterblockimport", "limitancestorcount", "limitancestorsize", "limitdescendantcount", "limitdescendantsize", "bip9params", "debug", "nodebug", "help-debug", "logips", "logtimestamps", "logtimemicros", "mocktime", "limitfreerelay", "relaypriority", "maxsigcachesize", "maxtipage", "minrelaytxfee", "maxtxfee", "printtoconsole", "printpriority", "shrinkdebugfile", "acceptnonstdtxn", "bytespersigop", "datacarrier", "datacarriersize", "mempoolreplacement", "blockmaxweight", "blockmaxsize", "txmaxcount", "blockprioritysize", "blockversion", "server", "rest", "rpcbind", "rpccookiefile", "rpcuser", "rpcpassword", "rpcauth", "rpcport", "rpcallowip", "rpcthreads", "rpcworkqueue", "rpcservertimeout", "help", "?", "disablewallet", "keypool", "fallbackfee", "mintxfee", "paytxfee", "rescan", "salvagewallet", "sendfreetransactions", "spendzeroconfchange", "txconfirmtarget", "usehd", "upgradewallet", "wallet", "walletbroadcast", "walletnotify", "zapwallettxes", "dblogsize", "flushwallet", "privdb", "walletrejectlongchains", "testnet", "usenewaddressformat", "sapi", "sapiport", "sapithreads", "sapiworkqueue", "sapiservertimeout", "sapiwhitelist"}; map mapArgs; map > mapMultiArgs; @@ -364,30 +364,19 @@ static void InterpretNegativeSetting(std::string& strKey, std::string& strValue) bool CheckDaemonParameters() { - bool ok = true; - for(map::const_iterator it = mapArgs.begin(); it != mapArgs.end(); ++it) { - bool found = false; - unsigned int j = 0; - while(!found && j < sizeof(args)/sizeof(args[0])) - { - if(it->first.compare("-" + args[j]) == 0) - { - found = true; - } - j++; - } + auto argFind = std::find_if(args.begin(), args.end(), [it](const std::string &arg) -> bool { + return (it->first.substr(1) == arg); + }); - if(!found) - { - ok = false; + if( argFind == args.end() ){ fprintf(stdout, "Invalid parameter %s check the help with -help command\n", it->first.c_str()); - break; + return false; } } - return ok; + return true; } void ParseParameters(int argc, const char* const argv[]) diff --git a/src/util.h b/src/util.h index af90d602..87c006d5 100644 --- a/src/util.h +++ b/src/util.h @@ -56,7 +56,7 @@ extern bool fSmartNode; extern bool fLiteMode; extern int nWalletBackups; -extern const std::string args[136]; +extern const std::vector args; extern std::map mapArgs; extern std::map > mapMultiArgs; extern bool fDebug; diff --git a/src/validation.cpp b/src/validation.cpp index 5bf475f9..0cbbaec1 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -121,11 +121,6 @@ struct COrphanTx { }; void EraseOrphansFor(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main); -/** - * Returns true if there are nRequired or more blocks of minVersion or above - * in the last Consensus::Params::nMajorityWindow blocks, starting at pstart and going backwards. - */ -static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned nRequired, const Consensus::Params& consensusParams); static void CheckBlockIndex(const Consensus::Params& consensusParams); /** Constant stuff for coinbase transactions we create: */ @@ -1829,7 +1824,7 @@ int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out) /** Undo the effects of this block (with given index) on the UTXO set represented by coins. * When UNCLEAN or FAILED is returned, view is left in an indeterminate state. */ -static DisconnectResult DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockIndex* pindex, CCoinsViewCache& view) +static DisconnectResult DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockIndex* pindex, CCoinsViewCache& view, bool fIsVerifyDB = false) { assert(pindex->GetBlockHash() == view.GetBestBlock()); @@ -1862,9 +1857,7 @@ static DisconnectResult DisconnectBlock(const CBlock& block, CValidationState& s */ // Result of the smartrewards block processing. - CSmartRewardsUpdateResult smartRewardsResult(pindex->nHeight, pindex->phashBlock, pindex->nTime); - - prewards->StartBlock(); + CSmartRewardsUpdateResult smartRewardsResult(pindex); // undo transactions in reverse order for (int i = block.vtx.size() - 1; i >= 0; i--) { @@ -1896,6 +1889,18 @@ static DisconnectResult DisconnectBlock(const CBlock& block, CValidationState& s CPubKey pubKey(pubKeyBytes); hashBytes = pubKey.GetID(); addressType = 1; + } else if (out.scriptPubKey.IsPayToScriptHashLocked()) { + + int nOffset = out.scriptPubKey[0] + 5; + + hashBytes = uint160(vector(out.scriptPubKey.begin() + nOffset, out.scriptPubKey.begin() + nOffset + 20)); + addressType = 2; + } else if (out.scriptPubKey.IsPayToPublicKeyHashLocked()) { + + int nOffset = out.scriptPubKey[0] + 6; + + hashBytes = uint160(vector(out.scriptPubKey.begin() + nOffset, out.scriptPubKey.begin() + nOffset + 20)); + addressType = 1; } else { continue; } @@ -1985,6 +1990,20 @@ static DisconnectResult DisconnectBlock(const CBlock& block, CValidationState& s CPubKey pubKey(pubKeyBytes); hashBytes = pubKey.GetID(); addressType = 1; + } else if (prevout.scriptPubKey.IsPayToScriptHashLocked()) { + + int nOffset = prevout.scriptPubKey[0] + 5; + + hashBytes = uint160(vector(prevout.scriptPubKey.begin() + nOffset, prevout.scriptPubKey.begin() + nOffset + 20)); + addressType = 2; + + } else if (prevout.scriptPubKey.IsPayToPublicKeyHashLocked()) { + + int nOffset = prevout.scriptPubKey[0] + 6; + + hashBytes = uint160(vector(prevout.scriptPubKey.begin() + nOffset, prevout.scriptPubKey.begin() + nOffset + 20)); + addressType = 1; + } else { continue; } @@ -2068,14 +2087,16 @@ static DisconnectResult DisconnectBlock(const CBlock& block, CValidationState& s // At this point, all of txundo.vprevout should have been moved out. } - prewards->UndoTransaction((CBlockIndex*) pindex, tx, view, params, smartRewardsResult); + if( !fIsVerifyDB ){ + prewards->UndoTransaction((CBlockIndex*) pindex, tx, view, params, smartRewardsResult); + } } // move best block pointer to prevout block view.SetBestBlock(pindex->pprev->GetBlockHash()); - if (fDepositIndex) { + if (!fIsVerifyDB && fDepositIndex) { if( !pblocktree->EraseDepositIndex(depositIndex) ){ AbortNode(state, "Failed to write deposit index"); @@ -2083,7 +2104,7 @@ static DisconnectResult DisconnectBlock(const CBlock& block, CValidationState& s } } - if (fAddressIndex) { + if (!fIsVerifyDB && fAddressIndex) { if (!pblocktree->EraseAddressIndex(addressIndex)) { AbortNode(state, "Failed to delete address index"); return DISCONNECT_FAILED; @@ -2094,7 +2115,7 @@ static DisconnectResult DisconnectBlock(const CBlock& block, CValidationState& s } } - if( !prewards->CommitUndoBlock( (CBlockIndex*) pindex, smartRewardsResult) ){ + if( !fIsVerifyDB && !prewards->CommitUndoBlock( (CBlockIndex*) pindex, smartRewardsResult) ){ AbortNode(state, "Failed to commit smartrewards block undo"); return DISCONNECT_FAILED; } @@ -2111,7 +2132,6 @@ static DisconnectResult DisconnectBlock(const CBlock& block, CValidationState& s } */ - return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN; } @@ -2222,7 +2242,7 @@ static int64_t nTimeTotal = 0; /** Apply the effects of this block (with given index) on the UTXO set represented by coins. * Validity checks that depend on the UTXO set are also done; ConnectBlock() * can fail if those validity checks fail (among other reasons). */ -static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck = false) +static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck = false, bool fIsVerifyDB = false) { const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); @@ -2344,6 +2364,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); std::vector prevheights; + std::vector prevouts; CAmount nFees = 0; int nInputs = 0; unsigned int nSigOps = 0; @@ -2361,12 +2382,10 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd */ // Result of the smartrewards block processing. - CSmartRewardsUpdateResult smartRewardsResult(pindex->nHeight, pindex->phashBlock, pindex->nTime); + CSmartRewardsUpdateResult smartRewardsResult(pindex); //bool fDIP0001Active_context = (VersionBitsState(pindex->pprev, chainparams.GetConsensus(), Consensus::DEPLOYMENT_DIP0001, versionbitscache) == THRESHOLD_ACTIVE); - prewards->StartBlock(); - for (unsigned int i = 0; i < block.vtx.size(); i++) { const CTransaction &tx = block.vtx[i]; @@ -2374,7 +2393,8 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd std::map, CAmount> vecInputs; std::map, CAmount> vecOutputs; - if( pindex->nHeight > 0 ) prewards->ProcessTransaction(pindex, tx, view, chainparams, smartRewardsResult); + int nCurrentRewardsRound = prewards->GetCurrentRound()->number; + bool fProcessRewards = !fIsVerifyDB && prewards->ProcessTransaction(pindex, tx, nCurrentRewardsRound); nInputs += tx.vin.size(); nSigOps += GetLegacySigOpCount(tx); @@ -2392,8 +2412,11 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd // BIP68 lock checks (as opposed to nLockTime checks) must // be in ConnectBlock because they require the UTXO set prevheights.resize(tx.vin.size()); + prevouts.resize(tx.vin.size()); + for (size_t j = 0; j < tx.vin.size(); j++) { - prevheights[j] = view.AccessCoin(tx.vin[j].prevout).nHeight; + prevouts[j] = view.AccessCoin(tx.vin[j].prevout); + prevheights[j] = prevouts[j].nHeight; } if (!SequenceLocks(tx, nLockTimeFlags, &prevheights, *pindex)) { @@ -2401,12 +2424,18 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd REJECT_INVALID, "bad-txns-nonfinal"); } - if (fAddressIndex || fSpentIndex || fDepositIndex) - { - for (size_t j = 0; j < tx.vin.size(); j++) { - const CTxIn input = tx.vin[j]; - const Coin& coin = view.AccessCoin(tx.vin[j].prevout); - const CTxOut &prevout = coin.out; + for (size_t j = 0; j < tx.vin.size(); j++) { + + const CTxIn input = tx.vin[j]; + const Coin& coin = prevouts[j]; + const CTxOut &prevout = coin.out; + + if( fProcessRewards && !input.scriptSig.IsZerocoinSpend() ){ + prewards->ProcessInput(tx, prevout, nCurrentRewardsRound, smartRewardsResult); + } + + if (fAddressIndex || fSpentIndex || fDepositIndex) + { uint160 hashBytes; int addressType; @@ -2421,6 +2450,16 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd CPubKey pubKey(pubKeyBytes); hashBytes = pubKey.GetID(); addressType = 1; + } else if (prevout.scriptPubKey.IsPayToScriptHashLocked()) { + int nOffset = prevout.scriptPubKey[0] + 5; + + hashBytes = uint160(vector(prevout.scriptPubKey.begin() + nOffset, prevout.scriptPubKey.begin() + nOffset + 20)); + addressType = 2; + } else if (prevout.scriptPubKey.IsPayToPublicKeyHashLocked()) { + int nOffset = prevout.scriptPubKey[0] + 6; + + hashBytes = uint160(vector(prevout.scriptPubKey.begin() + nOffset, prevout.scriptPubKey.begin() + nOffset + 20)); + addressType = 1; } else { hashBytes.SetNull(); addressType = 0; @@ -2478,9 +2517,15 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd control.Add(vChecks); } - if (fAddressIndex || fDepositIndex) { - for (unsigned int k = 0; k < tx.vout.size(); k++) { - const CTxOut &out = tx.vout[k]; + for (unsigned int k = 0; k < tx.vout.size(); k++) { + + const CTxOut &out = tx.vout[k]; + + if( fProcessRewards && !out.scriptPubKey.IsZerocoinMint() ){ + prewards->ProcessOutput(tx, out, nCurrentRewardsRound, pindex->nHeight, smartRewardsResult); + } + + if (fAddressIndex || fDepositIndex) { uint160 hashBytes; int addressType; @@ -2496,6 +2541,16 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd CPubKey pubKey(pubKeyBytes); hashBytes = pubKey.GetID(); addressType = 1; + } else if (out.scriptPubKey.IsPayToScriptHashLocked()) { + int nOffset = out.scriptPubKey[0] + 5; + + hashBytes = uint160(vector(out.scriptPubKey.begin() + nOffset, out.scriptPubKey.begin() + nOffset + 20)); + addressType = 2; + } else if (out.scriptPubKey.IsPayToPublicKeyHashLocked()) { + int nOffset = out.scriptPubKey[0] + 6; + + hashBytes = uint160(vector(out.scriptPubKey.begin() + nOffset, out.scriptPubKey.begin() + nOffset + 20)); + addressType = 1; } else { continue; } @@ -2526,7 +2581,6 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd if( fDepositIndex ){ - for( auto const &output : vecOutputs ){ auto input = vecInputs.find(std::make_pair( output.first.first, output.first.second )); @@ -2624,8 +2678,6 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd return false; } - prewards->CommitBlock(pindex, smartRewardsResult); - // END SMARTCASH if (!control.Wait()) @@ -2636,6 +2688,10 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd if (fJustCheck) return true; + if( !fIsVerifyDB && smartRewardsResult.IsValid() ){ + prewards->CommitBlock(pindex, smartRewardsResult); + } + // Write undo information to disk if (pindex->GetUndoPos().IsNull() || !pindex->IsValid(BLOCK_VALID_SCRIPTS)) { @@ -2655,11 +2711,11 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd setDirtyBlockIndex.insert(pindex); } - if (fTxIndex) + if (!fIsVerifyDB && fTxIndex) if (!pblocktree->WriteTxIndex(vPos)) return AbortNode(state, "Failed to write transaction index"); - if (fAddressIndex) { + if (!fIsVerifyDB && fAddressIndex) { if (!pblocktree->WriteAddressIndex(addressIndex)) { return AbortNode(state, "Failed to write address index"); } @@ -2669,15 +2725,15 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd } } - if (fSpentIndex) + if (!fIsVerifyDB && fSpentIndex) if (!pblocktree->UpdateSpentIndex(spentIndex)) - return AbortNode(state, "Failed to write transaction index"); + return AbortNode(state, "Failed to write spent index"); - if (fTimestampIndex) + if (!fIsVerifyDB && fTimestampIndex) if (!pblocktree->WriteTimestampIndex(CTimestampIndexKey(pindex->nTime, pindex->GetBlockHash()))) return AbortNode(state, "Failed to write timestamp index"); - if (fDepositIndex) { + if (!fIsVerifyDB && fDepositIndex) { if( !pblocktree->WriteDepositIndex(depositIndex) ){ return AbortNode(state, "Failed to write deposit index"); @@ -2755,8 +2811,10 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { bool fPeriodicWrite = mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000; // It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage. bool fPeriodicFlush = mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000; + // The cache is over the limit, we have to write now. + bool fRewardsNeedsSync = prewards->NeedsCacheWrite(); // Combine all conditions that result in a full cache flush. - bool fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune; + bool fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune || fRewardsNeedsSync; // Write blocks and block index to disk. if (fDoFullFlush || fPeriodicWrite) { // Depend on nMinDiskSpace to ensure we can write block index @@ -2781,6 +2839,9 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) { return AbortNode(state, "Files to write to block index database"); } + if (!prewards->SyncCached()) { + return AbortNode(state, "Files to write to block index database"); + } } // Finally remove any pruned files if (fFlushForPrune) @@ -2966,7 +3027,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001); { CCoinsViewCache view(pcoinsTip); - bool rv = ConnectBlock(*pblock, state, pindexNew, view); + bool rv = ConnectBlock(*pblock, state, pindexNew, view, false, false); GetMainSignals().BlockChecked(*pblock, state); if (!rv) { if (state.IsInvalid()) @@ -3946,9 +4007,6 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha } int nHeight = pindex->nHeight; - if(!newHash && ((nHeight > HF_V1_3_SMARTREWARD_WITHOUT_NODE_HEIGHT && Params().NetworkIDString() == CBaseChainParams::MAIN) || (nHeight > HF_V1_3_SMARTREWARD_WITHOUT_NODE_HEIGHT_TESTNET && Params().NetworkIDString() == CBaseChainParams::TESTNET))){ - newHash = true; - } // Write block to history file try { @@ -3973,7 +4031,7 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha return true; } -static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned nRequired, const Consensus::Params& consensusParams) +bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned nRequired, const Consensus::Params& consensusParams) { unsigned int nFound = 0; for (int i = 0; i < consensusParams.nMajorityWindow && nFound < nRequired && pstart != NULL; i++) @@ -4031,7 +4089,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, return false; if (!ContextualCheckBlock(block, state, pindexPrev)) return false; - if (!ConnectBlock(block, state, &indexDummy, viewNew, true)) + if (!ConnectBlock(block, state, &indexDummy, viewNew, true, true)) return false; assert(state.IsValid()); @@ -4362,11 +4420,6 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, boost::this_thread::interruption_point(); uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))))); - int nHeight = pindex->nHeight; - if(!newHash && ((nHeight > HF_V1_3_SMARTREWARD_WITHOUT_NODE_HEIGHT && Params().NetworkIDString() == CBaseChainParams::MAIN) || (nHeight > HF_V1_3_SMARTREWARD_WITHOUT_NODE_HEIGHT_TESTNET && Params().NetworkIDString() == CBaseChainParams::TESTNET))){ - newHash = true; - } - if (pindex->nHeight < chainActive.Height()-nCheckDepth) break; CBlock block; @@ -4387,10 +4440,11 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, } // check level 3: check for inconsistencies during memory-only disconnect of tip blocks if (nCheckLevel >= 3 && pindex == pindexState && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) { - DisconnectResult res = DisconnectBlock(block, state, pindex, coins); + DisconnectResult res = DisconnectBlock(block, state, pindex, coins, true); if (res == DISCONNECT_FAILED) { return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); } + pindexState = pindex->pprev; if (res == DISCONNECT_UNCLEAN) { nGoodTransactions = 0; @@ -4415,7 +4469,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, CBlock block; if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus())) return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); - if (!ConnectBlock(block, state, pindex, coins)) + if (!ConnectBlock(block, state, pindex, coins, false, true)) return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); } } diff --git a/src/validation.h b/src/validation.h index a5f1f063..bb4dd7c9 100644 --- a/src/validation.h +++ b/src/validation.h @@ -89,16 +89,16 @@ static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB /** Dust Hard Limit, ignored as wallet inputs (mininput default) */ static const int64_t DUST_HARD_LIMIT = 1000; // 0.00001 SMART mininput /** Maximum number of script-checking threads allowed */ -static const int MAX_SCRIPTCHECK_THREADS = 16; +static const int MAX_SCRIPTCHECK_THREADS = 15; // was 16 /** -par default (number of script-checking threads, 0 = auto) */ static const int DEFAULT_SCRIPTCHECK_THREADS = 0; /** Number of blocks that can be requested at any given time from a single peer. */ -static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 16; +static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 64; //was 16 /** Timeout in seconds during which a peer must stall block download progress before being disconnected. */ -static const unsigned int BLOCK_STALLING_TIMEOUT = 2; +static const unsigned int BLOCK_STALLING_TIMEOUT = 1; //was 2 /** Number of headers sent in one getheaders result. We rely on the assumption that if a peer sends * less than this number, we reached its tip. Changing this value is a protocol upgrade. */ -static const unsigned int MAX_HEADERS_RESULTS = 2000; +static const unsigned int MAX_HEADERS_RESULTS = 2000; //was 2000 /** Maximum depth of blocks we're willing to serve as compact blocks to peers * when requested. For older blocks, a regular BLOCK response will be sent. */ static const int MAX_CMPCTBLOCK_DEPTH = 5; @@ -239,6 +239,7 @@ static const int SYNC_TRANSACTION_NOT_IN_BLOCK = -1; // full block file chunks, we need the high water mark which triggers the prune to be // one 128MB block file + added 15% undo data = 147MB greater for a total of 545MB // Setting the target to > than 1414MB will make it likely we can respect the target. +// x2 for 8MB blocks = 2818 - Assume blocks will be half full over 288 blocks. static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 1414 * 1024 * 1024; /** @@ -260,6 +261,13 @@ static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 1414 * 1024 * 1024; * @return True if state.IsValid() */ bool ProcessNewBlock(const CChainParams& chainparams, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp, bool* fNewBlock); + +/** + * Returns true if there are nRequired or more blocks of minVersion or above + * in the last Consensus::Params::nMajorityWindow blocks, starting at pstart and going backwards. + */ +bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned nRequired, const Consensus::Params& consensusParams); + /** * Process incoming block headers. * diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index f2679bc5..3b9fd9fa 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -457,40 +457,46 @@ UniValue importwallet(const UniValue& params, bool fHelp) 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; + if (vchSecret.SetString(vstr[0])) { + 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); + } else if (IsHex(vstr[0])) { + std::vector vData(ParseHex(vstr[0])); + CScript script = CScript(vData.begin(), vData.end()); + if (!pwalletMain->HaveCScript(CScriptID(script))) { + pwalletMain->AddCScript(script); } } - 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 @@ -770,6 +776,7 @@ UniValue dumpwallet(const UniValue& params, bool fHelp) std::set setKeyPool; pwalletMain->GetKeyBirthTimes(mapKeyBirth); pwalletMain->GetAllReserveKeys(setKeyPool); + std::set setScripts = pwalletMain->GetCScripts(); // sort time/key pairs std::vector > vKeyBirth; @@ -848,6 +855,16 @@ UniValue dumpwallet(const UniValue& params, bool fHelp) } } file << "\n"; + for (const CScriptID &scriptid : setScripts) { + CScript script; + std::string create_time = "0"; + std::string address = CBitcoinAddress(scriptid).ToString(); + if(pwalletMain->GetCScript(scriptid, script)) { + file << strprintf("%s script=1", HexStr(script.begin(), script.end())); + file << strprintf(" # addr=%s\n", address); + } + } + file << "\n"; file << "# End of dump\n"; file.close(); return NullUniValue; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 9e4bb5e2..fc7b0b6e 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -110,20 +110,45 @@ UniValue getnewaddress(const UniValue& params, bool fHelp) if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - if (fHelp || params.size() > 1) - throw runtime_error( - "getnewaddress ( \"account\" )\n" + bool fBIP65Enabled = IsSuperMajority(4, chainActive.Tip(), Params().GetConsensus().nMajorityEnforceBlockUpgrade, + Params().GetConsensus()); + + if (fHelp || params.size() > 2) + { + std::string errorMsg = + "getnewaddress ( \"account\" locktime)\n" "\nReturns a new SmartCash address for receiving payments.\n" "If 'account' is specified (DEPRECATED), 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) DEPRECATED. 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" - "\"smartcashaddress\" (string) The new SmartCash address\n" + "1. \"account\" (string, optional) DEPRECATED. 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"; + + if (fBIP65Enabled) + { + errorMsg += + "2. locktime (numeric, optional, default=0) Locktime. Non-0 value locks the address to be spendable until after a locking period. If locktime is less than 500000000, then it is processed as the block height at which the address becomes spendable. If locktime is greater than 500000000 then it is processed as a UNIX timestamp after which the coins attached to the address can be spent." + "\nResult:\n" + "{\n" + " \"address\":\"address\", (string) The new SmartCash address\n" + " \"redeemScript\":\"hex\", (string) The hex encoded redeem script if locktime was set\n" + "}\n"; + } + else + { + errorMsg += + "\nResult:\n" + "{\n" + " \"address\":\"address\", (string) The new SmartCash address\n" + "}\n"; + } + + errorMsg += "\nExamples:\n" + HelpExampleCli("getnewaddress", "") - + HelpExampleRpc("getnewaddress", "") - ); + + HelpExampleRpc("getnewaddress", ""); + + throw runtime_error(errorMsg); + } LOCK2(cs_main, pwalletMain->cs_wallet); @@ -132,6 +157,10 @@ UniValue getnewaddress(const UniValue& params, bool fHelp) if (params.size() > 0) strAccount = AccountFromValue(params[0]); + int nLockTime = 0; + if (fBIP65Enabled && (params.size() > 1)) + nLockTime = params[1].get_int(); + if (!pwalletMain->IsLocked(true)) pwalletMain->TopUpKeyPool(); @@ -140,10 +169,21 @@ UniValue getnewaddress(const UniValue& params, bool fHelp) if (!pwalletMain->GetKeyFromPool(newKey, false)) throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); CKeyID keyID = newKey.GetID(); + UniValue obj(UniValue::VOBJ); - pwalletMain->SetAddressBook(keyID, strAccount, "receive"); + if (nLockTime > 0) { + CScript redeemScript = GetLockedScriptForDestination(keyID, nLockTime); + CScriptID scriptHash(redeemScript); + pwalletMain->AddCScript(redeemScript); + pwalletMain->SetAddressBook(scriptHash, strAccount, "receive"); + obj.push_back(Pair("address", CBitcoinAddress(scriptHash).ToString())); + obj.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); + } else { + pwalletMain->SetAddressBook(keyID, strAccount, "receive"); + obj.push_back(Pair("address", CBitcoinAddress(keyID).ToString())); + } - return CBitcoinAddress(keyID).ToString(); + return obj; } UniValue getaddress(const UniValue& params, bool fHelp) @@ -561,6 +601,96 @@ UniValue instantsendtoaddress(const UniValue& params, bool fHelp) return wtx.GetHash().GetHex(); } + +UniValue sendtoaddresslocked(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() < 3 || params.size() > 5) + throw runtime_error( + "sendtoaddresslocked \"smartcashaddress\" amount blockheight( \"comment\" \"comment-to\" subtractfeefromamount )\n" + "\nSend an amount to a given address and lock the output until a future blockheight or Unix time.\n" + "This is not an InstantPay lock. Use sendmany or instantsendtoaddress instead.\n" + + HelpRequiringPassphrase() + + "\nArguments:\n" + "1. \"smartcashaddress\" (string, required) The SmartCash address to send to.\n" + "2. \"amount\" (numeric or string, required) The amount in " + CURRENCY_UNIT + " to send. eg 0.1\n" + "3. \"blockheight\" (numeric, required) The blockheight at which the output becomes spendable/unlocked. \n" + " Unix time can be used for the time available to spend.\n" + " This is impossible to reverse after sent. Please be careful.\n" + "4. \"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" + "5. \"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("sendtoaddresslocked", "\"SXun9XDHLdBhG4Yd1ueZfLfRpC9kZgwT1b\" 0.1 1400000") + + HelpExampleCli("sendtoaddresslocked", "\"SXun9XDHLdBhG4Yd1ueZfLfRpC9kZgwT1b\" 0.1 1577132833 \"donation\" \"seans outpost\"") + + HelpExampleCli("sendtoaddresslocked", "\"SXun9XDHLdBhG4Yd1ueZfLfRpC9kZgwT1b\" 0.1 1400000 \"\" \"\" true") + + HelpExampleRpc("sendtoaddresslocked", "\"SXun9XDHLdBhG4Yd1ueZfLfRpC9kZgwT1b\", 0.1, 1400000, \"donation\", \"seans outpost\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + + CBitcoinAddress address(params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid SmartCash address"); + + // Amount + CAmount nAmount = AmountFromValue(params[1]); + if (nAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); + + // Locktime + unsigned int nLockTime = params[2].get_int(); + + if( nLockTime >= LOCKTIME_THRESHOLD ){ + throw JSONRPCError(RPC_TYPE_ERROR, strprintf("blockheight needs to be < %d", LOCKTIME_THRESHOLD)); + } + + // Wallet comments + CWalletTx wtx; + if (params.size() > 3 && !params[3].isNull() && !params[3].get_str().empty()) + wtx.mapValue["comment"] = params[3].get_str(); + if (params.size() > 4 && !params[4].isNull() && !params[4].get_str().empty()) + wtx.mapValue["to"] = params[4].get_str(); + + EnsureWalletIsUnlocked(); + + CAmount curBalance = pwalletMain->GetBalance(); + + if (nAmount > curBalance) + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + // Parse SmartCash address + CScript scriptPubKey = GetLockedScriptForDestination(address.Get(), nLockTime); + + // Create and send the transaction + CReserveKey reservekey(pwalletMain); + CAmount nFeeRequired; + std::string strError; + vector vecSend; + int nChangePosRet = -1; + CRecipient recipient = {scriptPubKey, nAmount, false}; + vecSend.push_back(recipient); + if (!pwalletMain->CreateTransaction(vecSend, wtx, reservekey, nFeeRequired, nChangePosRet, + strError, NULL, true, ALL_COINS, false)) { + if (nAmount + 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)); + throw JSONRPCError(RPC_WALLET_ERROR, strError); + } + if (!pwalletMain->CommitTransaction(wtx, reservekey, g_connman.get(), NetMsgType::TX)) + 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."); + + return wtx.GetHash().GetHex(); +} + UniValue listaddressgroupings(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 551023c1..5ed5a66c 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -461,28 +461,6 @@ bool CWallet::UpdateVotingKeyRegistration(const CKeyID &keyId) { return CWalletDB(strWalletFile).UpdateVotingKeyRegistration(keyId, mapVotingKeyRegistrations[keyId]); } -bool CWallet::LoadVotedMap(const CKeyID &keyId, const std::map &mapVoted) { - AssertLockHeld(cs_wallet); - this->mapVoted[keyId] = mapVoted; - return true; -} - -bool CWallet::UpdateVotedMap(const CKeyID &keyId) { - AssertLockHeld(cs_wallet); - return CWalletDB(strWalletFile).UpdateVotedMap(keyId, mapVoted[keyId]); -} - -bool CWallet::LoadVoteProofs(const CKeyID &keyId, const std::map &mapVoteProofs) { - AssertLockHeld(cs_wallet); - this->mapVoteProofs[keyId] = mapVoteProofs; - return true; -} - -bool CWallet::UpdateVoteProofs(const CKeyID &keyId) { - AssertLockHeld(cs_wallet); - return CWalletDB(strWalletFile).UpdateVoteProofs(keyId, mapVoteProofs[keyId]); -} - bool CWallet::LoadCryptedVotingKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret) { return CCryptoKeyStore::AddCryptedVotingKey(vchPubKey, vchCryptedSecret); } @@ -2405,11 +2383,13 @@ void CWallet::AvailableCoins(vector &vCoins, bool fOnlyConfirmed, cons if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && (!IsLockedCoin((*it).first, i) || nCoinType == ONLY_10000) && (pcoin->vout[i].nValue > 0 || fIncludeZeroValue) && - (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i)))) + (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i)))){ + vCoins.push_back(COutput(pcoin, i, nDepth, ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO), - (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO)); + (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO, pcoin->vout[i].GetLockTime())); + } } } @@ -2452,11 +2432,13 @@ void CWallet::AvailableCoins(vector &vCoins, const CSmartAddress& addr isminetype mine = IsMine(pcoin->vout[i]); if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && !IsLockedCoin((*it).first, i) && - pcoin->vout[i].nValue > 0) + pcoin->vout[i].nValue > 0){ vCoins.push_back(COutput(pcoin, i, nDepth, ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || false, - (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO)); + (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO, + pcoin->vout[i].GetLockTime())); + } } } @@ -2937,7 +2919,14 @@ int CWallet::CountInputsWithAmount(CAmount nInputAmount) int nDepth = pcoin->GetDepthInMainChain(false); for (unsigned int i = 0; i < pcoin->vout.size(); i++) { - COutput out = COutput(pcoin, i, nDepth, true, true); + + uint32_t nLockTime = pcoin->vout[i].GetLockTime(); + + if( nLockTime ){ + LogPrintf("LOGTIME FOUND %d\n", nLockTime); + } + + COutput out = COutput(pcoin, i, nDepth, true, true, nLockTime); //COutPoint outpoint = COutPoint(out.tx->GetHash(), out.i); if(out.tx->vout[out.i].nValue != nInputAmount) continue; @@ -2980,10 +2969,6 @@ bool CWallet::GetProposalFeeTX(CWalletTx& tx, const CSmartAddress& fromAddress, vector< CRecipient > vecSend; vecSend.push_back(dataRecipient); - CScript hiveScript = SmartHive::Script(SmartHive::ProjectTreasury); - CRecipient hiveRecipient = {hiveScript, nAmount, false}; - vecSend.push_back(hiveRecipient); - CCoinControl coinControl; coinControl.destChange = fromAddress.Get(); @@ -3254,7 +3239,7 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt // Note how the sequence number is set to max()-1 so that the // nLockTime set above actually works. BOOST_FOREACH(const PAIRTYPE(const CWalletTx *, unsigned int) &coin, setCoins) - txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), std::numeric_limits < unsigned int > ::max())); + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), std::numeric_limits < unsigned int > ::max() - 1)); // Sign int nIn = 0; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 4bc0988e..c9746227 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -467,10 +467,11 @@ class COutput int nDepth; bool fSpendable; bool fSolvable; + unsigned int nLockTime; - COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn) + COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn, unsigned int nLockTimeIn) { - tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; + tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; nLockTime = nLockTimeIn; } std::string ToString() const; @@ -674,10 +675,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface std::map mapVotingKeyMetadata; std::map mapVotingKeyRegistrations; - /* SmartRewards Vote proof */ - std::map> mapVoted; - std::map> mapVoteProofs; - typedef std::map MasterKeyMap; MasterKeyMap mapMasterKeys; unsigned int nMasterKeyMaxID; @@ -839,14 +836,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool LoadVotingKeyRegistration(const CKeyID &keyId, const uint256 &txhash); //! Update votekeys registrations bool UpdateVotingKeyRegistration(const CKeyID &keyId); - //! Load voted rounds per address (used by LoadWallet) - bool LoadVotedMap(const CKeyID &keyId, const std::map &mapVoted); - //! Update voted round per address - bool UpdateVotedMap(const CKeyID &keyId); - //! Load vote proofs per address (used by LoadWallet) - bool LoadVoteProofs(const CKeyID &keyId, const std::map &mapVoteProofs); - //! Update vote proofs per address - bool UpdateVoteProofs(const CKeyID &keyId); //! Adds an encrypted key to the store, and saves it to disk. bool AddCryptedVotingKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret); //! Adds an encrypted key to the store, without saving it to disk (used by LoadWallet) diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 10b63cdd..fa2d659a 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -135,20 +135,6 @@ bool CWalletDB::UpdateVotingKeyRegistration(const CKeyID& keyId, const uint256& txHash, true); } -bool CWalletDB::UpdateVotedMap(const CKeyID& keyId, const std::map& mapVoted) -{ - nWalletDBUpdated++; - return Write(std::make_pair(std::string("vmap"), keyId), - mapVoted, true); -} - -bool CWalletDB::UpdateVoteProofs(const CKeyID& keyId, const std::map& mapVoteProofs) -{ - nWalletDBUpdated++; - return Write(std::make_pair(std::string("vproofs"), keyId), - mapVoteProofs, true); -} - bool CWalletDB::WriteVotingKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CVotingKeyMetadata& keyMeta) { nWalletDBUpdated++; @@ -748,26 +734,6 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, pwallet->LoadVotingKeyRegistration(keyId, txHash); } - else if (strType == "vmap") - { - CKeyID keyId; - ssKey >> keyId; - std::map mapVoted; - ssValue >> mapVoted; - wss.nMapVoted++; - - pwallet->LoadVotedMap(keyId, mapVoted); - } - else if (strType == "vproofs") - { - CKeyID keyId; - ssKey >> keyId; - std::map mapVoteProofs; - ssValue >> mapVoteProofs; - wss.nMapVoteProofs++; - - pwallet->LoadVoteProofs(keyId, mapVoteProofs); - } else if (strType == "defaultkey") { ssValue >> pwallet->vchDefaultKey; diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 2e5348c6..91f2aa38 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -166,8 +166,6 @@ class CWalletDB : public CDB bool WriteVotingMasterKey(unsigned int nID, const CMasterKey& kMasterKey); bool UpdateVotingKeyMeta(const CKeyID& keyId, const CVotingKeyMetadata& keyMeta); bool UpdateVotingKeyRegistration(const CKeyID& keyId, const uint256& txHash); - bool UpdateVotedMap(const CKeyID& keyId, const std::map& mapVoted); - bool UpdateVoteProofs(const CKeyID& keyId, const std::map& mapVoteProofs); bool WriteCScript(const uint160& hash, const CScript& redeemScript);