From 78c5ca53f6ff9fcf9735df3ec4d16ab81307d437 Mon Sep 17 00:00:00 2001 From: Evan Duffield Date: Mon, 8 Dec 2014 18:17:57 -0700 Subject: [PATCH] Merged in Darksend/InstantX/Masternodes This doesn't include any of the changes in the darkcoin/v0.10.18.x branch. Those will be brought over into this commit history. --- .gitignore | 23 +- Makefile.am | 6 +- TODO.md | 22 +- configure.ac | 12 +- src/Makefile.am | 71 +- src/Makefile.include | 10 +- src/activemasternode.cpp | 320 +++ src/activemasternode.h | 54 + src/base58.h | 2 +- src/bignum.h | 4 +- src/chainparams.cpp | 2 +- src/chainparams.h | 2 +- src/coins.h | 2 +- src/core.h | 26 + src/{bitcoin-cli.cpp => darkcoin-cli.cpp} | 0 src/{bitcoind.cpp => darkcoind.cpp} | 0 src/darksend.cpp | 2183 +++++++++++++++++ src/darksend.h | 433 ++++ src/init.cpp | 86 +- src/instantx.cpp | 459 ++++ src/instantx.h | 89 + src/main.cpp | 435 +++- src/main.h | 14 +- src/masternode.cpp | 828 +++++++ src/masternode.h | 272 ++ src/miner.cpp | 54 +- src/net.cpp | 90 +- src/net.h | 47 +- src/protocol.h | 2 + src/qt/Makefile.am | 36 +- src/qt/addresstablemodel.cpp | 2 +- src/qt/askpassphrasedialog.cpp | 14 +- src/qt/askpassphrasedialog.h | 1 + src/qt/bitcoingui.cpp | 23 + src/qt/bitcoingui.h | 2 + src/qt/{bitcoin.cpp => darkcoin.cpp} | 2 +- src/qt/darksendconfig.cpp | 74 + src/qt/darksendconfig.h | 37 + src/qt/forms/askpassphrasedialog.ui | 16 + src/qt/forms/optionsdialog.ui | 28 + src/qt/forms/sendcoinsdialog.ui | 39 +- src/qt/optionsmodel.cpp | 19 + src/qt/optionsmodel.h | 4 + src/qt/res/bitcoin-qt-res.rc | 10 +- src/qt/sendcoinsdialog.cpp | 55 +- src/qt/sendcoinsdialog.h | 2 +- src/qt/signverifymessagedialog.cpp | 2 +- src/qt/test/Makefile.am | 20 +- src/qt/walletframe.cpp | 7 + src/qt/walletframe.h | 2 + src/qt/walletmodel.cpp | 76 +- src/qt/walletmodel.h | 20 +- src/qt/walletview.cpp | 13 +- src/qt/walletview.h | 2 + src/rpcdarksend.cpp | 323 +++ src/rpcmining.cpp | 4 +- src/rpcserver.cpp | 13 +- src/rpcserver.h | 7 +- src/rpcwallet.cpp | 137 +- src/script.cpp | 25 + src/script.h | 3 +- src/test/Makefile.am | 22 +- .../{test_bitcoin.cpp => test_darkcoin.cpp} | 1 + src/uint256.h | 5 + src/util.cpp | 16 + src/util.h | 13 + src/version.h | 4 +- src/wallet.cpp | 597 ++++- src/wallet.h | 81 +- 69 files changed, 7008 insertions(+), 297 deletions(-) create mode 100644 src/activemasternode.cpp create mode 100644 src/activemasternode.h rename src/{bitcoin-cli.cpp => darkcoin-cli.cpp} (100%) rename src/{bitcoind.cpp => darkcoind.cpp} (100%) create mode 100644 src/darksend.cpp create mode 100644 src/darksend.h create mode 100644 src/instantx.cpp create mode 100644 src/instantx.h create mode 100644 src/masternode.cpp create mode 100644 src/masternode.h rename src/qt/{bitcoin.cpp => darkcoin.cpp} (99%) create mode 100644 src/qt/darksendconfig.cpp create mode 100644 src/qt/darksendconfig.h create mode 100644 src/rpcdarksend.cpp rename src/test/{test_bitcoin.cpp => test_darkcoin.cpp} (98%) diff --git a/.gitignore b/.gitignore index 85ddf3871f..aac8bde773 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,11 @@ *.tar.gz *.exe -src/bitcoin -src/bitcoind -src/bitcoin-cli -src/test/test_bitcoin -src/qt/test/test_bitcoin-qt +src/darkcoin +src/darkcoind +src/darkcoin-cli +src/test/test_darkcoin +src/qt/test/test_darkcoin-qt Makefile.in aclocal.m4 @@ -36,7 +36,7 @@ src/qt/test/moc*.cpp *.o *.o-* *.patch -.bitcoin +.darkcoin *.a *.pb.cc *.pb.h @@ -51,12 +51,12 @@ src/qt/test/moc*.cpp # Compilation and Qt preprocessor part *.qm Makefile -bitcoin-qt -Bitcoin-Qt.app +darkcoin-qt +Darkcoin-Qt.app # Unit-tests Makefile.test -bitcoin-qt_test +darkcoin-qt_test # Resources cpp qrc_*.cpp @@ -71,7 +71,7 @@ build #lcov *.gcno /*.info -test_bitcoin.coverage/ +test_darkcoin.coverage/ total.coverage/ coverage_percent.txt @@ -83,3 +83,6 @@ qa/pull-tester/run-bitcoind-for-test.sh qa/pull-tester/build-tests.sh !src/leveldb*/Makefile + +.cproject +.project \ No newline at end of file diff --git a/Makefile.am b/Makefile.am index 04f8368dd7..c72db2c986 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,9 +4,9 @@ SUBDIRS = src GZIP_ENV="-9n" -BITCOIND_BIN=$(top_builddir)/src/bitcoind$(EXEEXT) -BITCOIN_QT_BIN=$(top_builddir)/src/qt/bitcoin-qt$(EXEEXT) -BITCOIN_CLI_BIN=$(top_builddir)/src/bitcoin-cli$(EXEEXT) +BITCOIND_BIN=$(top_builddir)/src/darkcoind$(EXEEXT) +BITCOIN_QT_BIN=$(top_builddir)/src/qt/darkcoin-qt$(EXEEXT) +BITCOIN_CLI_BIN=$(top_builddir)/src/darkcoin-cli$(EXEEXT) BITCOIN_WIN_INSTALLER=$(PACKAGE)-$(PACKAGE_VERSION)-win$(WINDOWS_BITS)-setup$(EXEEXT) OSX_APP=Bitcoin-Qt.app diff --git a/TODO.md b/TODO.md index afdb074934..6bf995485c 100644 --- a/TODO.md +++ b/TODO.md @@ -28,16 +28,22 @@ DONE: MANDATORY: ---------- -- Add masternode payment checks a.k.a. enforcement (based on blockheight) -- Fix mining protocol to include correct pow and masternodes +- Check rpcminer (should be working though) -OPTIONAL: ---------- +BUGS: +----- -- Include Evan's public key for msg signing -- Darksend, Instant Transactions, Atomic Transfers, etc. pp. -- Include centralized checkpoint syncing (peercoin style) -- Remove Bitcoin dead weight (SHA256, hardcoded keys, nodes) +- Daemon and CLI tool can't connect to testnet/regtest instances (wrong port?) +- Daemon and CLI tool can't authenticate via RPC (uh-oh?) +- Qt wallet can't find the config file in testnet mode (wrong path?) + + +ADDITIONAL: +----------- + +- Include trusted public key for message signing +- Masternodes, Enforcement, Darksend, InstantX, Atomic Transfers, ... +- Remove Bitcoin dead weight (SHA256, hardcoded keys, seednodes, ...) - Update strings - Write tests diff --git a/configure.ac b/configure.ac index e5290cb78f..ce9ddbe871 100644 --- a/configure.ac +++ b/configure.ac @@ -1,12 +1,12 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 0) -define(_CLIENT_VERSION_MINOR, 9) -define(_CLIENT_VERSION_REVISION, 3) +define(_CLIENT_VERSION_MINOR, 11) +define(_CLIENT_VERSION_REVISION, 0) define(_CLIENT_VERSION_BUILD, 0) define(_CLIENT_VERSION_IS_RELEASE, true) -define(_COPYRIGHT_YEAR, 2014) -AC_INIT([Bitcoin Core],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_REVISION],[info@bitcoin.org],[bitcoin]) +define(_COPYRIGHT_YEAR, 2015) +AC_INIT([Darkcoin Core],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_REVISION],[info@darkcoin.io],[darkcoin]) AC_CONFIG_AUX_DIR([src/build-aux]) AC_CONFIG_MACRO_DIR([src/m4]) AC_CANONICAL_HOST @@ -660,7 +660,7 @@ if test x$bitcoin_enable_qt != xno; then AC_MSG_WARN("xgettext is required to update qt translations") fi - AC_MSG_CHECKING([whether to build test_bitcoin-qt]) + AC_MSG_CHECKING([whether to build test_darkcoin-qt]) if test x$use_tests$bitcoin_enable_qt_test = xyesyes; then AC_MSG_RESULT([yes]) BUILD_TEST_QT="test" @@ -669,7 +669,7 @@ if test x$bitcoin_enable_qt != xno; then fi fi -AC_MSG_CHECKING([whether to build test_bitcoin]) +AC_MSG_CHECKING([whether to build test_darkcoin]) if test x$use_tests = xyes; then AC_MSG_RESULT([yes]) BUILD_TEST="test" diff --git a/src/Makefile.am b/src/Makefile.am index 6301028e31..e3c779377e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,28 +3,29 @@ include Makefile.include AM_CPPFLAGS += -I$(builddir) noinst_LIBRARIES = \ - libbitcoin_server.a \ - libbitcoin_common.a \ - libbitcoin_cli.a + libdarkcoin_server.a \ + libdarkcoin_common.a \ + libdarkcoin_cli.a if ENABLE_WALLET -noinst_LIBRARIES += libbitcoin_wallet.a +noinst_LIBRARIES += libdarkcoin_wallet.a endif bin_PROGRAMS = if BUILD_BITCOIND - bin_PROGRAMS += bitcoind + bin_PROGRAMS += darkcoind endif if BUILD_BITCOIN_CLI - bin_PROGRAMS += bitcoin-cli + bin_PROGRAMS += darkcoin-cli endif SUBDIRS = . $(BUILD_QT) $(BUILD_TEST) DIST_SUBDIRS = . qt test .PHONY: FORCE -# bitcoin core # +# darkcoin core # BITCOIN_CORE_H = \ + activemasternode.h \ addrman.h \ alert.h \ allocators.h \ @@ -39,14 +40,17 @@ BITCOIN_CORE_H = \ compat.h \ core.h \ crypter.h \ + darksend.h \ db.h \ hash.h \ init.h \ + instantx.h \ key.h \ keystore.h \ leveldbwrapper.h \ limitedmap.h \ main.h \ + masternode.h \ miner.h \ mruset.h \ netbase.h \ @@ -99,7 +103,8 @@ obj/build.h: FORCE $(abs_top_srcdir) version.o: obj/build.h -libbitcoin_server_a_SOURCES = \ +libdarkcoin_server_a_SOURCES = \ + activemasternode.cpp \ addrman.cpp \ alert.cpp \ bloom.cpp \ @@ -113,6 +118,7 @@ libbitcoin_server_a_SOURCES = \ net.cpp \ noui.cpp \ rpcblockchain.cpp \ + rpcdarksend.cpp \ rpcmining.cpp \ rpcmisc.cpp \ rpcnet.cpp \ @@ -123,7 +129,8 @@ libbitcoin_server_a_SOURCES = \ $(JSON_H) \ $(BITCOIN_CORE_H) -libbitcoin_wallet_a_SOURCES = \ +libdarkcoin_wallet_a_SOURCES = \ + activemasternode.cpp \ db.cpp \ crypter.cpp \ rpcdump.cpp \ @@ -132,11 +139,15 @@ libbitcoin_wallet_a_SOURCES = \ walletdb.cpp \ $(BITCOIN_CORE_H) -libbitcoin_common_a_SOURCES = \ +libdarkcoin_common_a_SOURCES = \ + activemasternode.cpp \ base58.cpp \ allocators.cpp \ chainparams.cpp \ core.cpp \ + darksend.cpp \ + masternode.cpp \ + instantx.cpp \ hash.cpp \ key.cpp \ netbase.cpp \ @@ -161,47 +172,47 @@ libbitcoin_common_a_SOURCES = \ $(BITCOIN_CORE_H) if GLIBC_BACK_COMPAT -libbitcoin_common_a_SOURCES += compat/glibc_compat.cpp -libbitcoin_common_a_SOURCES += compat/glibcxx_compat.cpp +libdarkcoin_common_a_SOURCES += compat/glibc_compat.cpp +libdarkcoin_common_a_SOURCES += compat/glibcxx_compat.cpp endif -libbitcoin_cli_a_SOURCES = \ +libdarkcoin_cli_a_SOURCES = \ rpcclient.cpp \ $(BITCOIN_CORE_H) -nodist_libbitcoin_common_a_SOURCES = $(top_srcdir)/src/obj/build.h +nodist_libdarkcoin_common_a_SOURCES = $(top_srcdir)/src/obj/build.h # -# bitcoind binary # -bitcoind_LDADD = \ - libbitcoin_server.a \ - libbitcoin_cli.a \ - libbitcoin_common.a \ +# darkcoind binary # +darkcoind_LDADD = \ + libdarkcoin_server.a \ + libdarkcoin_cli.a \ + libdarkcoin_common.a \ $(LIBLEVELDB) \ $(LIBMEMENV) if ENABLE_WALLET -bitcoind_LDADD += libbitcoin_wallet.a +darkcoind_LDADD += libdarkcoin_wallet.a endif -bitcoind_SOURCES = bitcoind.cpp +darkcoind_SOURCES = darkcoind.cpp # if TARGET_WINDOWS -bitcoind_SOURCES += bitcoind-res.rc +darkcoind_SOURCES += bitcoind-res.rc endif AM_CPPFLAGS += $(BDB_CPPFLAGS) -bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) +darkcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) -# bitcoin-cli binary # -bitcoin_cli_LDADD = \ - libbitcoin_cli.a \ - libbitcoin_common.a \ +# darkcoin-cli binary # +darkcoin_cli_LDADD = \ + libdarkcoin_cli.a \ + libdarkcoin_common.a \ $(BOOST_LIBS) -bitcoin_cli_SOURCES = bitcoin-cli.cpp +darkcoin_cli_SOURCES = darkcoin-cli.cpp # if TARGET_WINDOWS -bitcoin_cli_SOURCES += bitcoin-cli-res.rc +darkcoin_cli_SOURCES += bitcoin-cli-res.rc endif # NOTE: This dependency is not strictly necessary, but without it make may try to build both in parallel, which breaks the LevelDB build system in a race @@ -212,7 +223,7 @@ leveldb/%.a: CC="$(CC)" PLATFORM=$(TARGET_OS) AR="$(AR)" $(LEVELDB_TARGET_FLAGS) \ OPT="$(CXXFLAGS) $(CPPFLAGS)" -qt/bitcoinstrings.cpp: $(libbitcoin_server_a_SOURCES) $(libbitcoin_common_a_SOURCES) $(libbitcoin_cli_a_SOURCES) +qt/bitcoinstrings.cpp: $(libdarkcoin_server_a_SOURCES) $(libdarkcoin_common_a_SOURCES) $(libdarkcoin_cli_a_SOURCES) @test -n $(XGETTEXT) || echo "xgettext is required for updating translations" @cd $(top_srcdir); XGETTEXT=$(XGETTEXT) share/qt/extract_strings_qt.py diff --git a/src/Makefile.include b/src/Makefile.include index 2fc6cd7775..499c69dfbe 100644 --- a/src/Makefile.include +++ b/src/Makefile.include @@ -12,11 +12,11 @@ AM_CPPFLAGS = $(INCLUDES) \ AM_CPPFLAGS += $(LEVELDB_CPPFLAGS) AM_LDFLAGS = $(PTHREAD_CFLAGS) -LIBBITCOIN_SERVER=$(top_builddir)/src/libbitcoin_server.a -LIBBITCOIN_WALLET=$(top_builddir)/src/libbitcoin_wallet.a -LIBBITCOIN_COMMON=$(top_builddir)/src/libbitcoin_common.a -LIBBITCOIN_CLI=$(top_builddir)/src/libbitcoin_cli.a -LIBBITCOINQT=$(top_builddir)/src/qt/libbitcoinqt.a +LIBBITCOIN_SERVER=$(top_builddir)/src/libdarkcoin_server.a +LIBBITCOIN_WALLET=$(top_builddir)/src/libdarkcoin_wallet.a +LIBBITCOIN_COMMON=$(top_builddir)/src/libdarkcoin_common.a +LIBBITCOIN_CLI=$(top_builddir)/src/libdarkcoin_cli.a +LIBBITCOINQT=$(top_builddir)/src/qt/libdarkcoinqt.a $(LIBBITCOIN): $(MAKE) -C $(top_builddir)/src $(@F) diff --git a/src/activemasternode.cpp b/src/activemasternode.cpp new file mode 100644 index 0000000000..7d5ee8a7f5 --- /dev/null +++ b/src/activemasternode.cpp @@ -0,0 +1,320 @@ + +#include "core.h" +#include "protocol.h" +#include "activemasternode.h" +#include + +using namespace std; +using namespace boost; + +// +// Bootup the masternode, look for a 1000DRK input and register on the network +// +void CActiveMasternode::RegisterAsMasterNode(bool stop) +{ + if(!fMasterNode) return; + + //need correct adjusted time to send ping + bool fIsInitialDownload = IsInitialBlockDownload(); + if(fIsInitialDownload) { + isCapableMasterNode = MASTERNODE_SYNC_IN_PROCESS; + LogPrintf("CActiveMasternode::RegisterAsMasterNode() - Sync in progress. Must wait until sync is complete to start masternode.\n"); + return; + } + + std::string errorMessage; + + CKey key2; + CPubKey pubkey2; + + if(!darkSendSigner.SetKey(strMasterNodePrivKey, errorMessage, key2, pubkey2)) + { + LogPrintf("CActiveMasternode::RegisterAsMasterNode() - Invalid masternodeprivkey: '%s'\n", errorMessage.c_str()); + exit(0); + } + + if(isCapableMasterNode == MASTERNODE_INPUT_TOO_NEW || isCapableMasterNode == MASTERNODE_NOT_CAPABLE || isCapableMasterNode == MASTERNODE_SYNC_IN_PROCESS){ + isCapableMasterNode = MASTERNODE_NOT_PROCESSED; + } + + if(isCapableMasterNode == MASTERNODE_NOT_PROCESSED) { + if(strMasterNodeAddr.empty()) { + if(!GetLocal(masterNodeSignAddr)) { + LogPrintf("CActiveMasternode::RegisterAsMasterNode() - Can't detect external address. Please use the masternodeaddr configuration option.\n"); + isCapableMasterNode = MASTERNODE_NOT_CAPABLE; + return; + } + } else { + masterNodeSignAddr = CService(strMasterNodeAddr); + } + + if((Params().NetworkID() == CChainParams::TESTNET && masterNodeSignAddr.GetPort() != 19999) || (!(Params().NetworkID() == CChainParams::TESTNET) && masterNodeSignAddr.GetPort() != 9999)) { + LogPrintf("CActiveMasternode::RegisterAsMasterNode() - Invalid port\n"); + isCapableMasterNode = MASTERNODE_NOT_CAPABLE; + return; + } + + LogPrintf("CActiveMasternode::RegisterAsMasterNode() - Checking inbound connection to '%s'\n", masterNodeSignAddr.ToString().c_str()); + + if(ConnectNode((CAddress)masterNodeSignAddr, masterNodeSignAddr.ToString().c_str())){ + masternodePortOpen = MASTERNODE_PORT_OPEN; + } else { + masternodePortOpen = MASTERNODE_PORT_NOT_OPEN; + isCapableMasterNode = MASTERNODE_NOT_CAPABLE; + LogPrintf("CActiveMasternode::RegisterAsMasterNode() - Port not open.\n"); + return; + } + + if(pwalletMain->IsLocked()){ + isCapableMasterNode = MASTERNODE_NOT_CAPABLE; + LogPrintf("CActiveMasternode::RegisterAsMasterNode() - Not capable.\n"); + return; + } + + isCapableMasterNode = MASTERNODE_NOT_CAPABLE; + + CKey SecretKey; + // Choose coins to use + if(GetMasterNodeVin(vinMasternode, pubkeyMasterNode, SecretKey)) { + + if(GetInputAge(vinMasternode) < MASTERNODE_MIN_CONFIRMATIONS){ + LogPrintf("CActiveMasternode::RegisterAsMasterNode() - Input must have least %d confirmations - %d confirmations\n", MASTERNODE_MIN_CONFIRMATIONS, GetInputAge(vinMasternode)); + isCapableMasterNode = MASTERNODE_INPUT_TOO_NEW; + return; + } + + int protocolVersion = PROTOCOL_VERSION; + + masterNodeSignatureTime = GetAdjustedTime(); + + std::string vchPubKey(pubkeyMasterNode.begin(), pubkeyMasterNode.end()); + std::string vchPubKey2(pubkey2.begin(), pubkey2.end()); + std::string strMessage = masterNodeSignAddr.ToString() + boost::lexical_cast(masterNodeSignatureTime) + vchPubKey + vchPubKey2 + boost::lexical_cast(protocolVersion); + + if(!darkSendSigner.SignMessage(strMessage, errorMessage, vchMasterNodeSignature, SecretKey)) { + LogPrintf("CActiveMasternode::RegisterAsMasterNode() - Sign message failed\n"); + return; + } + + if(!darkSendSigner.VerifyMessage(pubkeyMasterNode, vchMasterNodeSignature, strMessage, errorMessage)) { + LogPrintf("CActiveMasternode::RegisterAsMasterNode() - Verify message failed\n"); + return; + } + + LogPrintf("CActiveMasternode::RegisterAsMasterNode() - Is capable master node!\n"); + + isCapableMasterNode = MASTERNODE_IS_CAPABLE; + + pwalletMain->LockCoin(vinMasternode.prevout); + + bool found = false; + BOOST_FOREACH(CMasterNode& mn, darkSendMasterNodes) + if(mn.vin == vinMasternode) + found = true; + + if(!found) { + LogPrintf("CActiveMasternode::RegisterAsMasterNode() - Adding myself to masternode list %s - %s\n", masterNodeSignAddr.ToString().c_str(), vinMasternode.ToString().c_str()); + CMasterNode mn(masterNodeSignAddr, vinMasternode, pubkeyMasterNode, vchMasterNodeSignature, masterNodeSignatureTime, pubkey2, PROTOCOL_VERSION); + mn.UpdateLastSeen(masterNodeSignatureTime); + darkSendMasterNodes.push_back(mn); + LogPrintf("CActiveMasternode::RegisterAsMasterNode() - Masternode input = %s\n", vinMasternode.ToString().c_str()); + } + + //relay to all + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + pnode->PushMessage("dsee", vinMasternode, masterNodeSignAddr, vchMasterNodeSignature, masterNodeSignatureTime, pubkeyMasterNode, pubkey2, -1, -1, masterNodeSignatureTime, protocolVersion); + } + + return; + } + } + + if(isCapableMasterNode != MASTERNODE_IS_CAPABLE && isCapableMasterNode != MASTERNODE_REMOTELY_ENABLED) return; + + masterNodeSignatureTime = GetAdjustedTime(); + + std::string strMessage = masterNodeSignAddr.ToString() + boost::lexical_cast(masterNodeSignatureTime) + boost::lexical_cast(stop); + + if(!darkSendSigner.SignMessage(strMessage, errorMessage, vchMasterNodeSignature, key2)) { + LogPrintf("CActiveMasternode::RegisterAsMasterNode() - Sign message failed\n"); + return; + } + + if(!darkSendSigner.VerifyMessage(pubkey2, vchMasterNodeSignature, strMessage, errorMessage)) { + LogPrintf("CActiveMasternode::RegisterAsMasterNode() - Verify message failed\n"); + return; + } + + bool found = false; + BOOST_FOREACH(CMasterNode& mn, darkSendMasterNodes) { + //LogPrintf(" -- %s\n", mn.vin.ToString().c_str()); + + if(mn.vin == vinMasternode) { + found = true; + mn.UpdateLastSeen(); + } + } + if(!found){ + LogPrintf("CActiveMasternode::RegisterAsMasterNode() - Darksend Masternode List doesn't include our masternode, Shutting down masternode pinging service! %s\n", vinMasternode.ToString().c_str()); + isCapableMasterNode = MASTERNODE_STOPPED; + return; + } + + LogPrintf("CActiveMasternode::RegisterAsMasterNode() - Masternode input = %s\n", vinMasternode.ToString().c_str()); + + if (stop) isCapableMasterNode = MASTERNODE_STOPPED; + + //relay to all peers + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + pnode->PushMessage("dseep", vinMasternode, vchMasterNodeSignature, masterNodeSignatureTime, stop); + } +} + +// +// Bootup the masternode, look for a 1000DRK input and register on the network +// Takes 2 parameters to start a remote masternode +// +bool CActiveMasternode::RegisterAsMasterNodeRemoteOnly(std::string strMasterNodeAddr, std::string strMasterNodePrivKey) +{ + if(!fMasterNode) return false; + + LogPrintf("CActiveMasternode::RegisterAsMasterNodeRemoteOnly() - Address %s MasterNodePrivKey %s\n", strMasterNodeAddr.c_str(), strMasterNodePrivKey.c_str()); + + std::string errorMessage; + + CKey key2; + CPubKey pubkey2; + + + if(!darkSendSigner.SetKey(strMasterNodePrivKey, errorMessage, key2, pubkey2)) + { + LogPrintf("CActiveMasternode::RegisterAsMasterNodeRemoteOnly() - Invalid masternodeprivkey: '%s'\n", errorMessage.c_str()); + return false; + } + + CService masterNodeSignAddr = CService(strMasterNodeAddr); + BOOST_FOREACH(CMasterNode& mn, darkSendMasterNodes){ + if(mn.addr == masterNodeSignAddr){ + LogPrintf("CActiveMasternode::RegisterAsMasterNodeRemoteOnly() - Address in use\n"); + return false; + } + } + + if((Params().NetworkID() == CChainParams::TESTNET && masterNodeSignAddr.GetPort() != 19999) || (!(Params().NetworkID() == CChainParams::TESTNET) && masterNodeSignAddr.GetPort() != 9999)) { + LogPrintf("CActiveMasternode::RegisterAsMasterNodeRemoteOnly() - Invalid port\n"); + return false; + } + + LogPrintf("CActiveMasternode::RegisterAsMasterNodeRemoteOnly() - Checking inbound connection to '%s'\n", masterNodeSignAddr.ToString().c_str()); + + if(!ConnectNode((CAddress)masterNodeSignAddr, masterNodeSignAddr.ToString().c_str())){ + LogPrintf("CActiveMasternode::RegisterAsMasterNodeRemoteOnly() - Error connecting to port\n"); + return false; + } + + if(pwalletMain->IsLocked()){ + LogPrintf("CActiveMasternode::RegisterAsMasterNodeRemoteOnly() - Wallet is locked\n"); + return false; + } + + CKey SecretKey; + CTxIn vinMasternode; + CPubKey pubkeyMasterNode; + int masterNodeSignatureTime = 0; + + // Choose coins to use + while (GetMasterNodeVin(vinMasternode, pubkeyMasterNode, SecretKey)) { + // don't use a vin that's registered + BOOST_FOREACH(CMasterNode& mn, darkSendMasterNodes) + if(mn.vin == vinMasternode) + continue; + + if(GetInputAge(vinMasternode) < MASTERNODE_MIN_CONFIRMATIONS) + continue; + + masterNodeSignatureTime = GetAdjustedTime(); + + std::string vchPubKey(pubkeyMasterNode.begin(), pubkeyMasterNode.end()); + std::string vchPubKey2(pubkey2.begin(), pubkey2.end()); + std::string strMessage = masterNodeSignAddr.ToString() + boost::lexical_cast(masterNodeSignatureTime) + vchPubKey + vchPubKey2 + boost::lexical_cast(PROTOCOL_VERSION); + + if(!darkSendSigner.SignMessage(strMessage, errorMessage, vchMasterNodeSignature, SecretKey)) { + LogPrintf("CActiveMasternode::RegisterAsMasterNodeRemoteOnly() - Sign message failed\n"); + return false; + } + + if(!darkSendSigner.VerifyMessage(pubkeyMasterNode, vchMasterNodeSignature, strMessage, errorMessage)) { + LogPrintf("CActiveMasternode::RegisterAsMasterNodeRemoteOnly() - Verify message failed\n"); + return false; + } + + LogPrintf("CActiveMasternode::RegisterAsMasterNodeRemoteOnly() - Is capable master node!\n"); + + pwalletMain->LockCoin(vinMasternode.prevout); + + int protocolVersion = PROTOCOL_VERSION; + //relay to all + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + pnode->PushMessage("dsee", vinMasternode, masterNodeSignAddr, vchMasterNodeSignature, masterNodeSignatureTime, pubkeyMasterNode, pubkey2, -1, -1, masterNodeSignatureTime, protocolVersion); + } + + return true; + } + + LogPrintf("CActiveMasternode::RegisterAsMasterNodeRemoteOnly() - No sutable vin found\n"); + return false; +} + + +bool CActiveMasternode::GetMasterNodeVin(CTxIn& vin, CPubKey& pubkey, CKey& secretKey) +{ + int64_t nValueIn = 0; + CScript pubScript; + + // try once before we try to denominate + if (!pwalletMain->SelectCoinsMasternode(vin, nValueIn, pubScript)) + { + if(fDebug) LogPrintf("CActiveMasternode::GetMasterNodeVin - I'm not a capable masternode\n"); + return false; + } + + CTxDestination address1; + ExtractDestination(pubScript, address1); + CBitcoinAddress address2(address1); + + CKeyID keyID; + if (!address2.GetKeyID(keyID)) { + LogPrintf("CActiveMasternode::GetMasterNodeVin - Address does not refer to a key\n"); + return false; + } + + if (!pwalletMain->GetKey(keyID, secretKey)) { + LogPrintf ("CActiveMasternode::GetMasterNodeVin - Private key for address is not known\n"); + return false; + } + + pubkey = secretKey.GetPubKey(); + return true; +} + +// when starting a masternode, this can enable to run as a hot wallet with no funds +bool CActiveMasternode::EnableHotColdMasterNode(CTxIn& vin, int64_t sigTime, CService& addr) +{ + if(!fMasterNode) return false; + + isCapableMasterNode = MASTERNODE_REMOTELY_ENABLED; + + vinMasternode = vin; + masterNodeSignatureTime = sigTime; + masterNodeSignAddr = addr; + + LogPrintf("CActiveMasternode::EnableHotColdMasterNode() - Enabled! You may shut down the cold daemon.\n"); + + return true; +} diff --git a/src/activemasternode.h b/src/activemasternode.h new file mode 100644 index 0000000000..1b4cc3b40f --- /dev/null +++ b/src/activemasternode.h @@ -0,0 +1,54 @@ + +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef ACTIVEMASTERNODE_H +#define ACTIVEMASTERNODE_H + +#include "bignum.h" +#include "sync.h" +#include "net.h" +#include "key.h" +#include "core.h" +#include "init.h" +#include "wallet.h" +#include "darksend.h" + +// Responsible for activating the masternode and pinging the network +class CActiveMasternode +{ +public: + CTxIn vinMasternode; + CPubKey pubkeyMasterNode; + CPubKey pubkeyMasterNode2; + + std::string strMasterNodeSignMessage; + std::vector vchMasterNodeSignature; + + std::string masterNodeAddr; + CService masterNodeSignAddr; + + int isCapableMasterNode; + int64_t masterNodeSignatureTime; + int masternodePortOpen; + + CActiveMasternode() + { + isCapableMasterNode = MASTERNODE_NOT_PROCESSED; + masternodePortOpen = 0; + } + + // get 1000DRK input that can be used for the masternode + bool GetMasterNodeVin(CTxIn& vin, CPubKey& pubkey, CKey& secretKey); + + // start the masternode and register with the network + void RegisterAsMasterNode(bool stop); + // start a remote masternode + bool RegisterAsMasterNodeRemoteOnly(std::string strMasterNodeAddr, std::string strMasterNodePrivKey); + + // enable hot wallet mode (run a masternode with no funds) + bool EnableHotColdMasterNode(CTxIn& vin, int64_t sigTime, CService& addr); +}; + +#endif \ No newline at end of file diff --git a/src/base58.h b/src/base58.h index 70681f589a..ae430e5199 100644 --- a/src/base58.h +++ b/src/base58.h @@ -92,7 +92,7 @@ public: bool operator> (const CBase58Data& b58) const { return CompareTo(b58) > 0; } }; -/** base58-encoded Bitcoin addresses. +/** base58-encoded Darkcoin addresses. * Public-key-hash-addresses have version 0 (or 111 testnet). * The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. * Script-hash-addresses have version 5 (or 196 testnet). diff --git a/src/bignum.h b/src/bignum.h index 0259338b31..9b553491c9 100644 --- a/src/bignum.h +++ b/src/bignum.h @@ -136,7 +136,7 @@ public: if (sn < (int64_t)0) { - // Since the minimum signed integer cannot be represented as positive so long as its type is signed, + // Since the minimum signed integer cannot be represented as positive so long as its type is signed, // and it's not well-defined what happens if you make it unsigned before negating it, // we instead increment the negative integer by 1, convert it, then increment the (now positive) unsigned integer by 1 to compensate n = -(sn + 1); @@ -284,7 +284,7 @@ public: // and 0xc0de000000 is compact (0x0600c0de) // (0x05c0de00) would be -0x40de000000 // - // Bitcoin only uses this "compact" format for encoding difficulty + // Darkcoin only uses this "compact" format for encoding difficulty // targets, which are unsigned 256bit quantities. Thus, all the // complexities of the sign bit and using base 256 are probably an // implementation accident. diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 16df50d339..8a36769369 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -67,7 +67,7 @@ public: base58Prefixes[PUBKEY_ADDRESS] = list_of(76); // Darkcoin addresses start with X base58Prefixes[SCRIPT_ADDRESS] = list_of(5); - base58Prefixes[SECRET_KEY] = list_of(128); + base58Prefixes[SECRET_KEY] = list_of(204); base58Prefixes[EXT_PUBLIC_KEY] = list_of(0x04)(0x88)(0xB2)(0x1E); base58Prefixes[EXT_SECRET_KEY] = list_of(0x04)(0x88)(0xAD)(0xE4); diff --git a/src/chainparams.h b/src/chainparams.h index 542afeaf92..0f9e723ac9 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -26,7 +26,7 @@ struct CDNSSeedData { /** * CChainParams defines various tweakable parameters of a given instance of the - * Bitcoin system. There are three: the main network on which people trade goods + * Darkcoin system. There are three: the main network on which people trade goods * and services, the public test network which gets reset from time to time and * a regression test mode which is intended for private networks only. It has * minimal difficulty to ensure that blocks can be found instantly. diff --git a/src/coins.h b/src/coins.h index 0ad28524a1..99d4b37a6b 100644 --- a/src/coins.h +++ b/src/coins.h @@ -334,7 +334,7 @@ public: // Calculate the size of the cache (in number of transactions) unsigned int GetCacheSize(); - /** Amount of bitcoins coming in to a transaction + /** Amount of darkcoins coming in to a transaction Note that lightweight clients may not know anything besides the hash of previous transactions, so may not be able to calculate this. diff --git a/src/core.h b/src/core.h index 5eb953610d..8124b39a5f 100644 --- a/src/core.h +++ b/src/core.h @@ -12,6 +12,30 @@ #include +#define START_MASTERNODE_PAYMENTS_TESTNET 1403568776 //Tue, 24 Jun 2014 00:12:56 GMT +#define START_MASTERNODE_PAYMENTS 1403728576 //Wed, 25 Jun 2014 20:36:16 GMT + +static const int64_t DARKSEND_COLLATERAL = (0.1*COIN); +static const int64_t DARKSEND_FEE = (0.0125*COIN); +static const int64_t DARKSEND_POOL_MAX = (999.99*COIN); + +#define MASTERNODE_NOT_PROCESSED 0 // initial state +#define MASTERNODE_IS_CAPABLE 1 +#define MASTERNODE_NOT_CAPABLE 2 +#define MASTERNODE_STOPPED 3 +#define MASTERNODE_INPUT_TOO_NEW 4 +#define MASTERNODE_PORT_NOT_OPEN 6 +#define MASTERNODE_PORT_OPEN 7 +#define MASTERNODE_SYNC_IN_PROCESS 8 +#define MASTERNODE_REMOTELY_ENABLED 9 + +#define MASTERNODE_MIN_CONFIRMATIONS 15 +#define MASTERNODE_MIN_DSEEP_SECONDS (30*60) +#define MASTERNODE_MIN_DSEE_SECONDS (5*60) +#define MASTERNODE_PING_SECONDS (1*60) +#define MASTERNODE_EXPIRATION_SECONDS (65*60) +#define MASTERNODE_REMOVAL_SECONDS (70*60) + class CTransaction; /** No amount larger than this (in satoshi) is valid */ @@ -72,6 +96,7 @@ class CTxIn public: COutPoint prevout; CScript scriptSig; + CScript prevPubKey; unsigned int nSequence; CTxIn() @@ -400,6 +425,7 @@ public: std::vector vtx; // memory only + mutable CScript payee; mutable std::vector vMerkleTree; CBlock() diff --git a/src/bitcoin-cli.cpp b/src/darkcoin-cli.cpp similarity index 100% rename from src/bitcoin-cli.cpp rename to src/darkcoin-cli.cpp diff --git a/src/bitcoind.cpp b/src/darkcoind.cpp similarity index 100% rename from src/bitcoind.cpp rename to src/darkcoind.cpp diff --git a/src/darksend.cpp b/src/darksend.cpp new file mode 100644 index 0000000000..8993987f52 --- /dev/null +++ b/src/darksend.cpp @@ -0,0 +1,2183 @@ + + +#include "darksend.h" +#include "main.h" +#include "init.h" +#include "util.h" +#include "masternode.h" +#include "instantx.h" + +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace boost; + + +/** The main object for accessing darksend */ +CDarkSendPool darkSendPool; +/** A helper object for signing messages from masternodes */ +CDarkSendSigner darkSendSigner; +/** The current darksends in progress on the network */ +std::vector vecDarksendQueue; +/** Keep track of the used masternodes */ +std::vector vecMasternodesUsed; +// keep track of the scanning errors I've seen +map mapDarksendBroadcastTxes; +// +CActiveMasternode activeMasternode; + +// count peers we've requested the list from +int RequestedMasterNodeList = 0; + +/* *** BEGIN DARKSEND MAGIC - DARKCOIN ********** + Copyright 2014, Darkcoin Developers + eduffield - evan@darkcoin.io +*/ + +void ProcessMessageDarksend(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) +{ + if (strCommand == "dsf") { //DarkSend Final tx + if (pfrom->nVersion < darkSendPool.MIN_PEER_PROTO_VERSION) { + return; + } + + if(darkSendPool.submittedToMasternode != pfrom->addr){ + //LogPrintf("dsc - message doesn't match current masternode - %s != %s\n", darkSendPool.submittedToMasternode.ToString().c_str(), pfrom->addr.ToString().c_str()); + return; + } + + int sessionID; + CTransaction txNew; + vRecv >> sessionID >> txNew; + + if(darkSendPool.sessionID != sessionID){ + if (fDebug) LogPrintf("dsf - message doesn't match current darksend session %d %d\n", darkSendPool.sessionID, sessionID); + return; + } + + //check to see if input is spent already? (and probably not confirmed) + darkSendPool.SignFinalTransaction(txNew, pfrom); + } + + else if (strCommand == "dsc") { //DarkSend Complete + if (pfrom->nVersion < darkSendPool.MIN_PEER_PROTO_VERSION) { + return; + } + + if(darkSendPool.submittedToMasternode != pfrom->addr){ + //LogPrintf("dsc - message doesn't match current masternode - %s != %s\n", darkSendPool.submittedToMasternode.ToString().c_str(), pfrom->addr.ToString().c_str()); + return; + } + + int sessionID; + bool error; + std::string lastMessage; + vRecv >> sessionID >> error >> lastMessage; + + if(darkSendPool.sessionID != sessionID){ + if (fDebug) LogPrintf("dsc - message doesn't match current darksend session %d %d\n", darkSendPool.sessionID, sessionID); + return; + } + + darkSendPool.CompletedTransaction(error, lastMessage); + } + + else if (strCommand == "dsa") { //DarkSend Acceptable + if (pfrom->nVersion < darkSendPool.MIN_PEER_PROTO_VERSION) { + std::string strError = "incompatible version"; + LogPrintf("dsa -- incompatible version! \n"); + pfrom->PushMessage("dssu", darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_REJECTED, strError); + + return; + } + + if(!fMasterNode){ + std::string strError = "not a masternode"; + LogPrintf("dsa -- not a masternode! \n"); + pfrom->PushMessage("dssu", darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_REJECTED, strError); + + return; + } + + int nDenom; + CTransaction txCollateral; + vRecv >> nDenom >> txCollateral; + + std::string error = ""; + int mn = GetMasternodeByVin(activeMasternode.vinMasternode); + if(mn == -1){ + std::string strError = "Not in the masternode list"; + pfrom->PushMessage("dssu", darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_REJECTED, strError); + return; + } + + if(darkSendPool.sessionUsers == 0) { + if(darkSendMasterNodes[mn].nLastDsq != 0 && + darkSendMasterNodes[mn].nLastDsq + CountMasternodesAboveProtocol(darkSendPool.MIN_PEER_PROTO_VERSION)/5 > darkSendPool.nDsqCount){ + //LogPrintf("dsa -- last dsq too recent, must wait. %s \n", darkSendMasterNodes[mn].addr.ToString().c_str()); + std::string strError = "Last darksend was too recent"; + pfrom->PushMessage("dssu", darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_REJECTED, strError); + return; + } + } + + if(!darkSendPool.IsCompatibleWithSession(nDenom, txCollateral, error)) + { + LogPrintf("dsa -- not compatible with existing transactions! \n"); + pfrom->PushMessage("dssu", darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_REJECTED, error); + return; + } else { + LogPrintf("dsa -- is compatible, please submit! \n"); + pfrom->PushMessage("dssu", darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_ACCEPTED, error); + return; + } + } else if (strCommand == "dsq") { //DarkSend Queue + if (pfrom->nVersion < darkSendPool.MIN_PEER_PROTO_VERSION) { + return; + } + + CDarksendQueue dsq; + vRecv >> dsq; + + CService addr; + if(!dsq.GetAddress(addr)) return; + if(!dsq.CheckSignature()) return; + + if(dsq.IsExpired()) return; + + int mn = GetMasternodeByVin(dsq.vin); + if(mn == -1) return; + + + // if the queue is ready, submit if we can + if(dsq.ready) { + if(darkSendPool.submittedToMasternode != addr){ + LogPrintf("dsq - message doesn't match current masternode - %s != %s\n", darkSendPool.submittedToMasternode.ToString().c_str(), pfrom->addr.ToString().c_str()); + return; + } + + if (fDebug) LogPrintf("darksend queue is ready - %s\n", addr.ToString().c_str()); + + darkSendPool.DoAutomaticDenominating(false, true); + } else { + BOOST_FOREACH(CDarksendQueue q, vecDarksendQueue){ + if(q.vin == dsq.vin) return; + } + + if(fDebug) LogPrintf("dsq last %d last2 %d count %d\n", darkSendMasterNodes[mn].nLastDsq, darkSendMasterNodes[mn].nLastDsq + (int)darkSendMasterNodes.size()/5, darkSendPool.nDsqCount); + //don't allow a few nodes to dominate the queuing process + if(darkSendMasterNodes[mn].nLastDsq != 0 && + darkSendMasterNodes[mn].nLastDsq + CountMasternodesAboveProtocol(darkSendPool.MIN_PEER_PROTO_VERSION)/5 > darkSendPool.nDsqCount){ + if(fDebug) LogPrintf("dsq -- masternode sending too many dsq messages. %s \n", darkSendMasterNodes[mn].addr.ToString().c_str()); + return; + } + darkSendPool.nDsqCount++; + darkSendMasterNodes[mn].nLastDsq = darkSendPool.nDsqCount; + darkSendMasterNodes[mn].allowFreeTx = true; + + if(fDebug) LogPrintf("dsq - new darksend queue object - %s\n", addr.ToString().c_str()); + vecDarksendQueue.push_back(dsq); + dsq.Relay(); + dsq.time = GetTime(); + } + + } else if (strCommand == "dsi") { //DarkSend vIn + std::string error = ""; + if (pfrom->nVersion < darkSendPool.MIN_PEER_PROTO_VERSION) { + LogPrintf("dsi -- incompatible version! \n"); + error = "incompatible version"; + pfrom->PushMessage("dssu", darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_REJECTED, error); + + return; + } + + if(!fMasterNode){ + LogPrintf("dsi -- not a masternode! \n"); + error = "not a masternode"; + pfrom->PushMessage("dssu", darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_REJECTED, error); + + return; + } + + std::vector in; + int64_t nAmount; + CTransaction txCollateral; + std::vector out; + vRecv >> in >> nAmount >> txCollateral >> out; + + //do we have enough users in the current session? + if(!darkSendPool.IsSessionReady()){ + LogPrintf("dsi -- session not complete! \n"); + error = "session not complete!"; + pfrom->PushMessage("dssu", darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_REJECTED, error); + return; + } + + //do we have the same denominations as the current session? + if(!darkSendPool.IsCompatibleWithEntries(out)) + { + LogPrintf("dsi -- not compatible with existing transactions! \n"); + error = "not compatible with existing transactions"; + pfrom->PushMessage("dssu", darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_REJECTED, error); + return; + } + + //check it like a transaction + { + int64_t nValueIn = 0; + int64_t nValueOut = 0; + bool missingTx = false; + + CValidationState state; + CTransaction tx; + + BOOST_FOREACH(const CTxOut o, out){ + nValueOut += o.nValue; + tx.vout.push_back(o); + + if(o.scriptPubKey.size() != 25){ + LogPrintf("dsi - non-standard pubkey detected! %s\n", o.scriptPubKey.ToString().c_str()); + error = "non-standard pubkey detected"; + pfrom->PushMessage("dssu", darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_REJECTED, error); + return; + } + if(!o.scriptPubKey.IsNormalPaymentScript()){ + LogPrintf("dsi - invalid script! %s\n", o.scriptPubKey.ToString().c_str()); + error = "invalid script detected"; + pfrom->PushMessage("dssu", darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_REJECTED, error); + return; + } + } + + BOOST_FOREACH(const CTxIn i, in){ + tx.vin.push_back(i); + + if(fDebug) LogPrintf("dsi -- tx in %s\n", i.ToString().c_str()); + + CTransaction tx2; + uint256 hash; + if(GetTransaction(i.prevout.hash, tx2, hash, true)){ + if(tx2.vout.size() > i.prevout.n) { + nValueIn += tx2.vout[i.prevout.n].nValue; + } + } else{ + missingTx = true; + } + } + + if (nValueIn > DARKSEND_POOL_MAX) { + LogPrintf("dsi -- more than darksend pool max! %s\n", tx.ToString().c_str()); + error = "more than darksed pool max"; + pfrom->PushMessage("dssu", darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_REJECTED, error); + return; + } + + if(!missingTx){ + if (nValueIn-nValueOut > nValueIn*.01) { + LogPrintf("dsi -- fees are too high! %s\n", tx.ToString().c_str()); + error = "transaction fees are too high"; + pfrom->PushMessage("dssu", darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_REJECTED, error); + return; + } + } else { + LogPrintf("dsi -- missing input tx! %s\n", tx.ToString().c_str()); + error = "missing input tx information"; + pfrom->PushMessage("dssu", darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_REJECTED, error); + return; + } + + if(AcceptableInputs(mempool, state, tx)){ + LogPrintf("dsi -- transaction not valid! \n"); + error = "transaction not valid"; + pfrom->PushMessage("dssu", darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_REJECTED, error); + return; + } + } + + if(darkSendPool.AddEntry(in, nAmount, txCollateral, out, error)){ + pfrom->PushMessage("dssu", darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_ACCEPTED, error); + darkSendPool.Check(); + + RelayDarkSendStatus(darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_RESET); + } else { + pfrom->PushMessage("dssu", darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_REJECTED, error); + } + } + + else if (strCommand == "dssub") { //DarkSend Subscribe To + if (pfrom->nVersion < darkSendPool.MIN_PEER_PROTO_VERSION) { + return; + } + + if(!fMasterNode) return; + + std::string error = ""; + pfrom->PushMessage("dssu", darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_RESET, error); + //pfrom->fDisconnect = true; + return; + } + + else if (strCommand == "dssu") { //DarkSend status update + if (pfrom->nVersion < darkSendPool.MIN_PEER_PROTO_VERSION) { + return; + } + + if(darkSendPool.submittedToMasternode != pfrom->addr){ + //LogPrintf("dssu - message doesn't match current masternode - %s != %s\n", darkSendPool.submittedToMasternode.ToString().c_str(), pfrom->addr.ToString().c_str()); + return; + } + + int sessionID; + int state; + int entriesCount; + int accepted; + std::string error; + vRecv >> sessionID >> state >> entriesCount >> accepted >> error; + + if(fDebug) LogPrintf("dssu - state: %i entriesCount: %i accepted: %i error: %s \n", state, entriesCount, accepted, error.c_str()); + + if((accepted != 1 && accepted != 0) && darkSendPool.sessionID != sessionID){ + LogPrintf("dssu - message doesn't match current darksend session %d %d\n", darkSendPool.sessionID, sessionID); + return; + } + + darkSendPool.StatusUpdate(state, entriesCount, accepted, error, sessionID); + + } + + else if (strCommand == "dss") { //DarkSend Sign Final Tx + if (pfrom->nVersion < darkSendPool.MIN_PEER_PROTO_VERSION) { + return; + } + + vector sigs; + vRecv >> sigs; + + bool success = false; + int count = 0; + + LogPrintf(" -- sigs count %d %d\n", (int)sigs.size(), count); + + BOOST_FOREACH(const CTxIn item, sigs) + { + if(darkSendPool.AddScriptSig(item)) success = true; + if(fDebug) LogPrintf(" -- sigs count %d %d\n", (int)sigs.size(), count); + count++; + } + + if(success){ + darkSendPool.Check(); + RelayDarkSendStatus(darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_RESET); + } + } + +} + +int randomizeList (int i) { return std::rand()%i;} + +// Recursively determine the rounds of a given input (How deep is the darksend chain for a given input) +int GetInputDarksendRounds(CTxIn in, int rounds) +{ + if(rounds >= 9) return rounds; + + std::string padding = ""; + padding.insert(0, ((rounds+1)*5)+3, ' '); + + CWalletTx tx; + if(pwalletMain->GetTransaction(in.prevout.hash,tx)){ + // bounds check + if(in.prevout.n >= tx.vout.size()) return -4; + + if(tx.vout[in.prevout.n].nValue == DARKSEND_FEE) return -3; + + if(rounds == 0){ //make sure the final output is non-denominate + bool found = false; + BOOST_FOREACH(int64_t d, darkSendDenominations) + if(tx.vout[in.prevout.n].nValue == d) found = true; + + if(!found) { + //LogPrintf(" - NOT DENOM\n"); + return -2; + } + } + bool found = false; + + BOOST_FOREACH(CTxOut out, tx.vout){ + BOOST_FOREACH(int64_t d, darkSendDenominations) + if(out.nValue == d) + found = true; + } + + if(!found) { + //LogPrintf(" - NOT FOUND\n"); + return rounds; + } + + // find my vin and look that up + BOOST_FOREACH(CTxIn in2, tx.vin) { + if(pwalletMain->IsMine(in2)){ + //LogPrintf("rounds :: %s %s %d NEXT\n", padding.c_str(), in.ToString().c_str(), rounds); + int n = GetInputDarksendRounds(in2, rounds+1); + if(n != -3) return n; + } + } + } else { + //LogPrintf("rounds :: %s %s %d NOTFOUND\n", padding.c_str(), in.ToString().c_str(), rounds); + } + + return rounds-1; +} + +void CDarkSendPool::SetNull(bool clearEverything){ + finalTransaction.vin.clear(); + finalTransaction.vout.clear(); + + entries.clear(); + + state = POOL_STATUS_ACCEPTING_ENTRIES; + + lastTimeChanged = GetTimeMillis(); + + entriesCount = 0; + lastEntryAccepted = 0; + countEntriesAccepted = 0; + + sessionUsers = 0; + sessionDenom = 0; + sessionFoundMasternode = false; + sessionTries = 0; + vecSessionCollateral.clear(); + txCollateral = CTransaction(); + + if(clearEverything){ + myEntries.clear(); + + if(fMasterNode){ + sessionID = 1 + (rand() % 999999); + } else { + sessionID = 0; + } + } + + // -- seed random number generator (used for ordering output lists) + unsigned int seed = 0; + RAND_bytes((unsigned char*)&seed, sizeof(seed)); + std::srand(seed); +} + +bool CDarkSendPool::SetCollateralAddress(std::string strAddress){ + CBitcoinAddress address; + if (!address.SetString(strAddress)) + { + LogPrintf("CDarkSendPool::SetCollateralAddress - Invalid DarkSend collateral address\n"); + return false; + } + collateralPubKey.SetDestination(address.Get()); + return true; +} + +// +// Unlock coins after Darksend fails or succeeds +// +void CDarkSendPool::UnlockCoins(){ + BOOST_FOREACH(CTxIn v, lockedCoins) + pwalletMain->UnlockCoin(v.prevout); + + lockedCoins.clear(); +} + +// +// Check the Darksend progress and send client updates if a masternode +// +void CDarkSendPool::Check() +{ + if(fDebug) LogPrintf("CDarkSendPool::Check()\n"); + if(fDebug) LogPrintf("CDarkSendPool::Check() - entries count %lu\n", entries.size()); + + // If entries is full, then move on to the next phase + if(state == POOL_STATUS_ACCEPTING_ENTRIES && (int)entries.size() >= GetMaxPoolTransactions()) + { + if(fDebug) LogPrintf("CDarkSendPool::Check() -- ACCEPTING OUTPUTS\n"); + UpdateState(POOL_STATUS_FINALIZE_TRANSACTION); + } + + // create the finalized transaction for distribution to the clients + if(state == POOL_STATUS_FINALIZE_TRANSACTION && finalTransaction.vin.empty() && finalTransaction.vout.empty()) { + if(fDebug) LogPrintf("CDarkSendPool::Check() -- FINALIZE TRANSACTIONS\n"); + UpdateState(POOL_STATUS_SIGNING); + + if (fMasterNode) { + // make our new transaction + CTransaction txNew; + for(unsigned int i = 0; i < entries.size(); i++){ + BOOST_FOREACH(const CTxOut v, entries[i].vout) + txNew.vout.push_back(v); + + BOOST_FOREACH(const CDarkSendEntryVin s, entries[i].sev) + txNew.vin.push_back(s.vin); + } + // shuffle the outputs for improved anonymity + std::random_shuffle ( txNew.vout.begin(), txNew.vout.end(), randomizeList); + + if(fDebug) LogPrintf("Transaction 1: %s\n", txNew.ToString().c_str()); + + SignFinalTransaction(txNew, NULL); + + // request signatures from clients + RelayDarkSendFinalTransaction(sessionID, txNew); + } + } + + // collect signatures from clients + + // If we have all of the signatures, try to compile the transaction + if(state == POOL_STATUS_SIGNING && SignaturesComplete()) { + if(fDebug) LogPrintf("CDarkSendPool::Check() -- SIGNING\n"); + UpdateState(POOL_STATUS_TRANSMISSION); + + CWalletTx txNew = CWalletTx(pwalletMain, finalTransaction); + + LOCK2(cs_main, pwalletMain->cs_wallet); + { + if (fMasterNode) { //only the main node is master atm + if(fDebug) LogPrintf("Transaction 2: %s\n", txNew.ToString().c_str()); + + // See if the transaction is valid + if (!txNew.AcceptToMemoryPool(true)) + { + LogPrintf("CDarkSendPool::Check() - CommitTransaction : Error: Transaction not valid\n"); + SetNull(); + pwalletMain->Lock(); + + // not much we can do in this case + UpdateState(POOL_STATUS_ACCEPTING_ENTRIES); + RelayDarkSendCompletedTransaction(sessionID, true, "Transaction not valid, please try again"); + return; + } + + LogPrintf("CDarkSendPool::Check() -- IS MASTER -- TRANSMITTING DARKSEND\n"); + + // sign a message + + int64_t sigTime = GetAdjustedTime(); + std::string strMessage = txNew.GetHash().ToString() + boost::lexical_cast(sigTime); + std::string strError = ""; + std::vector vchSig; + CKey key2; + CPubKey pubkey2; + + if(!darkSendSigner.SetKey(strMasterNodePrivKey, strError, key2, pubkey2)) + { + LogPrintf("Invalid masternodeprivkey: '%s'\n", strError.c_str()); + exit(0); + } + + if(!darkSendSigner.SignMessage(strMessage, strError, vchSig, key2)) { + LogPrintf("CDarkSendPool::Check() - Sign message failed\n"); + return; + } + + if(!darkSendSigner.VerifyMessage(pubkey2, vchSig, strMessage, strError)) { + LogPrintf("CDarkSendPool::Check() - Verify message failed\n"); + return; + } + + if(!mapDarksendBroadcastTxes.count(txNew.GetHash())){ + CDarksendBroadcastTx dstx; + dstx.tx = txNew; + dstx.vin = activeMasternode.vinMasternode; + dstx.vchSig = vchSig; + dstx.sigTime = sigTime; + + mapDarksendBroadcastTxes.insert(make_pair(txNew.GetHash(), dstx)); + } + + // Broadcast the transaction to the network + txNew.fTimeReceivedIsTxTime = true; + txNew.RelayWalletTransaction(); + + // Tell the clients it was successful + RelayDarkSendCompletedTransaction(sessionID, false, "Transaction Created Successfully"); + + // Randomly charge clients + ChargeRandomFees(); + } + } + } + + // move on to next phase, allow 3 seconds incase the masternode wants to send us anything else + if((state == POOL_STATUS_TRANSMISSION && fMasterNode) || (state == POOL_STATUS_SIGNING && completedTransaction) ) { + if(fDebug) LogPrintf("CDarkSendPool::Check() -- COMPLETED -- RESETTING \n"); + SetNull(true); + UnlockCoins(); + if(fMasterNode) RelayDarkSendStatus(darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_RESET); + pwalletMain->Lock(); + } + + // reset if we're here for 10 seconds + if((state == POOL_STATUS_ERROR || state == POOL_STATUS_SUCCESS) && GetTimeMillis()-lastTimeChanged >= 10000) { + if(fDebug) LogPrintf("CDarkSendPool::Check() -- RESETTING MESSAGE \n"); + SetNull(true); + if(fMasterNode) RelayDarkSendStatus(darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_RESET); + UnlockCoins(); + } +} + +// +// Charge clients a fee if they're abusive +// +// Why bother? Darksend uses collateral to ensure abuse to the process is kept to a minimum. +// The submission and signing stages in darksend are completely separate. In the cases where +// a client submits a transaction then refused to sign, there must be a cost. Otherwise they +// would be able to do this over and over again and bring the mixing to a hault. +// +// How does this work? Messages to masternodes come in via "dsi", these require a valid collateral +// transaction for the client to be able to enter the pool. This transaction is kept by the masternode +// until the transaction is either complete or fails. +// +void CDarkSendPool::ChargeFees(){ + if(fMasterNode) { + //we don't need to charge collateral for every offence. + int offences = 0; + int r = rand()%100; + if(r > 33) return; + + if(state == POOL_STATUS_ACCEPTING_ENTRIES){ + BOOST_FOREACH(const CTransaction& txCollateral, vecSessionCollateral) { + bool found = false; + BOOST_FOREACH(const CDarkSendEntry& v, entries) { + if(v.collateral == txCollateral) { + found = true; + } + } + + // This queue entry didn't send us the promised transaction + if(!found){ + LogPrintf("CDarkSendPool::ChargeFees -- found uncooperative node (didn't send transaction). Found offence.\n"); + offences++; + } + } + } + + if(state == POOL_STATUS_SIGNING) { + // who didn't sign? + BOOST_FOREACH(const CDarkSendEntry v, entries) { + BOOST_FOREACH(const CDarkSendEntryVin s, v.sev) { + if(!s.isSigSet){ + LogPrintf("CDarkSendPool::ChargeFees -- found uncooperative node (didn't sign). Found offence\n"); + offences++; + } + } + } + } + + r = rand()%100; + int target = 0; + + //mostly offending? + if(offences >= POOL_MAX_TRANSACTIONS-1 && r > 33) return; + + //everyone is an offender? That's not right + if(offences >= POOL_MAX_TRANSACTIONS) return; + + //charge one of the offenders randomly + if(offences > 1) target = 50; + + //pick random client to charge + r = rand()%100; + + if(state == POOL_STATUS_ACCEPTING_ENTRIES){ + BOOST_FOREACH(const CTransaction& txCollateral, vecSessionCollateral) { + bool found = false; + BOOST_FOREACH(const CDarkSendEntry& v, entries) { + if(v.collateral == txCollateral) { + found = true; + } + } + + // This queue entry didn't send us the promised transaction + if(!found && r > target){ + LogPrintf("CDarkSendPool::ChargeFees -- found uncooperative node (didn't send transaction). charging fees.\n"); + + CWalletTx wtxCollateral = CWalletTx(pwalletMain, txCollateral); + + // Broadcast + if (!wtxCollateral.AcceptToMemoryPool(true)) + { + // This must not fail. The transaction has already been signed and recorded. + LogPrintf("CDarkSendPool::ChargeFees() : Error: Transaction not valid"); + } + wtxCollateral.RelayWalletTransaction(); + return; + } + } + } + + if(state == POOL_STATUS_SIGNING) { + // who didn't sign? + BOOST_FOREACH(const CDarkSendEntry v, entries) { + BOOST_FOREACH(const CDarkSendEntryVin s, v.sev) { + if(!s.isSigSet && r > target){ + LogPrintf("CDarkSendPool::ChargeFees -- found uncooperative node (didn't sign). charging fees.\n"); + + CWalletTx wtxCollateral = CWalletTx(pwalletMain, v.collateral); + + // Broadcast + if (!wtxCollateral.AcceptToMemoryPool(true)) + { + // This must not fail. The transaction has already been signed and recorded. + LogPrintf("CDarkSendPool::ChargeFees() : Error: Transaction not valid"); + } + wtxCollateral.RelayWalletTransaction(); + return; + } + } + } + } + } +} + +// charge the collateral randomly +// - Darksend is completely free, to pay miners we randomly pay the collateral of users. +void CDarkSendPool::ChargeRandomFees(){ + if(fMasterNode) { + int i = 0; + + BOOST_FOREACH(const CTransaction& txCollateral, vecSessionCollateral) { + int r = rand()%1000; + + if(r <= 20) + { + LogPrintf("CDarkSendPool::ChargeRandomFees -- charging random fees. %u\n", i); + + CWalletTx wtxCollateral = CWalletTx(pwalletMain, txCollateral); + + // Broadcast + if (!wtxCollateral.AcceptToMemoryPool(true)) + { + // This must not fail. The transaction has already been signed and recorded. + LogPrintf("CDarkSendPool::ChargeRandomFees() : Error: Transaction not valid"); + } + wtxCollateral.RelayWalletTransaction(); + } + } + } +} + +// +// Check for various timeouts (queue objects, darksend, etc) +// +void CDarkSendPool::CheckTimeout(){ + // catching hanging sessions + if(!fMasterNode) { + if(state == POOL_STATUS_TRANSMISSION) { + if(fDebug) LogPrintf("CDarkSendPool::CheckTimeout() -- Session complete -- Running Check()\n"); + Check(); + } + } + + // check darksend queue objects for timeouts + int c = 0; + vector::iterator it; + for(it=vecDarksendQueue.begin();it *vec = &myEntries; + if(fMasterNode) vec = &entries; + + // check for a timeout and reset if needed + vector::iterator it2; + for(it2=vec->begin();it2end();it2++){ + if((*it2).IsExpired()){ + if(fDebug) LogPrintf("CDarkSendPool::CheckTimeout() : Removing expired entry - %d\n", c); + vec->erase(it2); + if(entries.size() == 0 && myEntries.size() == 0){ + SetNull(true); + UnlockCoins(); + } + if(fMasterNode){ + RelayDarkSendStatus(darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_RESET); + } + break; + } + c++; + } + + if(GetTimeMillis()-lastTimeChanged >= (DARKSEND_QUEUE_TIMEOUT*1000)+addLagTime){ + lastTimeChanged = GetTimeMillis(); + + ChargeFees(); + // reset session information for the queue query stage (before entering a masternode, clients will send a queue request to make sure they're compatible denomination wise) + sessionUsers = 0; + sessionDenom = 0; + sessionFoundMasternode = false; + sessionTries = 0; + vecSessionCollateral.clear(); + + UpdateState(POOL_STATUS_ACCEPTING_ENTRIES); + } + } else if(GetTimeMillis()-lastTimeChanged >= (DARKSEND_QUEUE_TIMEOUT*1000)+addLagTime){ + if(fDebug) LogPrintf("CDarkSendPool::CheckTimeout() -- Session timed out (30s) -- resetting\n"); + SetNull(); + UnlockCoins(); + + UpdateState(POOL_STATUS_ERROR); + lastMessage = "Session timed out (30), please resubmit"; + } + + if(state == POOL_STATUS_SIGNING && GetTimeMillis()-lastTimeChanged >= (DARKSEND_SIGNING_TIMEOUT*1000)+addLagTime ) { + if(fDebug) LogPrintf("CDarkSendPool::CheckTimeout() -- Session timed out -- restting\n"); + ChargeFees(); + SetNull(); + UnlockCoins(); + //add my transactions to the new session + + UpdateState(POOL_STATUS_ERROR); + lastMessage = "Signing timed out, please resubmit"; + } +} + +// check to see if the signature is valid +bool CDarkSendPool::SignatureValid(const CScript& newSig, const CTxIn& newVin){ + CTransaction txNew; + txNew.vin.clear(); + txNew.vout.clear(); + + int found = -1; + CScript sigPubKey = CScript(); + unsigned int i = 0; + + BOOST_FOREACH(CDarkSendEntry e, entries) { + BOOST_FOREACH(const CTxOut out, e.vout) + txNew.vout.push_back(out); + + BOOST_FOREACH(const CDarkSendEntryVin s, e.sev){ + txNew.vin.push_back(s.vin); + + if(s.vin == newVin){ + found = i; + sigPubKey = s.vin.prevPubKey; + } + i++; + } + } + + if(found >= 0){ //might have to do this one input at a time? + int n = found; + txNew.vin[n].scriptSig = newSig; + if(fDebug) LogPrintf("CDarkSendPool::SignatureValid() - Sign with sig %s\n", newSig.ToString().substr(0,24).c_str()); + if (!VerifyScript(txNew.vin[n].scriptSig, sigPubKey, txNew, n, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, 0)){ + if(fDebug) LogPrintf("CDarkSendPool::SignatureValid() - Signing - Error signing input %u\n", n); + return false; + } + } + + if(fDebug) LogPrintf("CDarkSendPool::SignatureValid() - Signing - Succesfully signed input\n"); + return true; +} + +// check to make sure the collateral provided by the client is valid +bool CDarkSendPool::IsCollateralValid(const CTransaction& txCollateral){ + if(txCollateral.vout.size() < 1) return false; + if(txCollateral.nLockTime != 0) return false; + + int64_t nValueIn = 0; + int64_t nValueOut = 0; + bool missingTx = false; + + CTransaction tx; + BOOST_FOREACH(const CTxOut o, txCollateral.vout){ + nValueOut += o.nValue; + + if(!o.scriptPubKey.IsNormalPaymentScript()){ + LogPrintf ("CDarkSendPool::IsCollateralValid - Invalid Script %s\n", txCollateral.ToString().c_str()); + return false; + } + } + + BOOST_FOREACH(const CTxIn i, txCollateral.vin){ + CTransaction tx2; + uint256 hash; + if(GetTransaction(i.prevout.hash, tx2, hash, true)){ + if(tx2.vout.size() > i.prevout.n) { + nValueIn += tx2.vout[i.prevout.n].nValue; + } + } else{ + missingTx = true; + } + } + + if(missingTx){ + if(fDebug) LogPrintf ("CDarkSendPool::IsCollateralValid - Unknown inputs in collateral transaction - %s\n", txCollateral.ToString().c_str()); + return false; + } + + //collateral transactions are required to pay out DARKSEND_COLLATERAL as a fee to the miners + if(nValueIn-nValueOut < DARKSEND_COLLATERAL) { + if(fDebug) LogPrintf ("CDarkSendPool::IsCollateralValid - did not include enough fees in transaction %d\n%s\n", nValueOut-nValueIn, txCollateral.ToString().c_str()); + return false; + } + + if(fDebug) LogPrintf("CDarkSendPool::IsCollateralValid %s\n", txCollateral.ToString().c_str()); + + CWalletTx wtxCollateral = CWalletTx(pwalletMain, txCollateral); + CValidationState state; + if(AcceptableInputs(mempool, state, tx)){ + if(fDebug) LogPrintf ("CDarkSendPool::IsCollateralValid - didn't pass IsAcceptable\n"); + return false; + } + + return true; +} + + +// +// Add a clients transaction to the pool +// +bool CDarkSendPool::AddEntry(const std::vector& newInput, const int64_t& nAmount, const CTransaction& txCollateral, const std::vector& newOutput, std::string& error){ + if (!fMasterNode) return false; + + BOOST_FOREACH(CTxIn in, newInput) { + if (in.prevout.IsNull() || nAmount < 0) { + if(fDebug) LogPrintf ("CDarkSendPool::AddEntry - input not valid!\n"); + error = "input not valid"; + sessionUsers--; + return false; + } + } + + if (!IsCollateralValid(txCollateral)){ + if(fDebug) LogPrintf ("CDarkSendPool::AddEntry - collateral not valid!\n"); + error = "collateral not valid"; + sessionUsers--; + return false; + } + + if((int)entries.size() >= GetMaxPoolTransactions()){ + if(fDebug) LogPrintf ("CDarkSendPool::AddEntry - entries is full!\n"); + error = "entries is full"; + sessionUsers--; + return false; + } + + BOOST_FOREACH(CTxIn in, newInput) { + if(fDebug) LogPrintf("looking for vin -- %s\n", in.ToString().c_str()); + BOOST_FOREACH(const CDarkSendEntry v, entries) { + BOOST_FOREACH(const CDarkSendEntryVin s, v.sev){ + if(s.vin == in) { + if(fDebug) LogPrintf ("CDarkSendPool::AddEntry - found in vin\n"); + error = "already have that vin"; + sessionUsers--; + return false; + } + } + } + } + + if(state == POOL_STATUS_ACCEPTING_ENTRIES) { + CDarkSendEntry v; + v.Add(newInput, nAmount, txCollateral, newOutput); + entries.push_back(v); + + if(fDebug) LogPrintf("CDarkSendPool::AddEntry -- adding %s\n", newInput[0].ToString().c_str()); + error = ""; + + return true; + } + + if(fDebug) LogPrintf ("CDarkSendPool::AddEntry - can't accept new entry, wrong state!\n"); + error = "wrong state"; + sessionUsers--; + return false; +} + +bool CDarkSendPool::AddScriptSig(const CTxIn& newVin){ + if(fDebug) LogPrintf("CDarkSendPool::AddScriptSig -- new sig %s\n", newVin.scriptSig.ToString().substr(0,24).c_str()); + + BOOST_FOREACH(const CDarkSendEntry v, entries) { + BOOST_FOREACH(const CDarkSendEntryVin s, v.sev){ + if(s.vin.scriptSig == newVin.scriptSig) { + LogPrintf("CDarkSendPool::AddScriptSig - already exists \n"); + return false; + } + } + } + + if(!SignatureValid(newVin.scriptSig, newVin)){ + if(fDebug) LogPrintf("CDarkSendPool::AddScriptSig - Invalid Sig\n"); + return false; + } + + if(fDebug) LogPrintf("CDarkSendPool::AddScriptSig -- sig %s\n", newVin.ToString().c_str()); + + if(state == POOL_STATUS_SIGNING) { + BOOST_FOREACH(CTxIn& vin, finalTransaction.vin){ + if(newVin.prevout == vin.prevout && vin.nSequence == newVin.nSequence){ + vin.scriptSig = newVin.scriptSig; + vin.prevPubKey = newVin.prevPubKey; + if(fDebug) LogPrintf("CDarkSendPool::AddScriptSig -- adding to finalTransaction %s\n", newVin.scriptSig.ToString().substr(0,24).c_str()); + } + } + for(unsigned int i = 0; i < entries.size(); i++){ + if(entries[i].AddSig(newVin)){ + if(fDebug) LogPrintf("CDarkSendPool::AddScriptSig -- adding %s\n", newVin.scriptSig.ToString().substr(0,24).c_str()); + return true; + } + } + } + + LogPrintf("CDarkSendPool::AddScriptSig -- Couldn't set sig!\n" ); + return false; +} + +// check to make sure everything is signed +bool CDarkSendPool::SignaturesComplete(){ + BOOST_FOREACH(const CDarkSendEntry v, entries) { + BOOST_FOREACH(const CDarkSendEntryVin s, v.sev){ + if(!s.isSigSet) return false; + } + } + return true; +} + +// +// Execute a darksend denomination via a masternode. +// This is only ran from clients +// +void CDarkSendPool::SendDarksendDenominate(std::vector& vin, std::vector& vout, int64_t amount){ + if(darkSendPool.txCollateral == CTransaction()){ + LogPrintf ("CDarksendPool:SendDarksendDenominate() - Darksend collateral not set"); + return; + } + + // lock the funds we're going to use + BOOST_FOREACH(CTxIn in, txCollateral.vin) + lockedCoins.push_back(in); + + BOOST_FOREACH(CTxIn in, vin) + lockedCoins.push_back(in); + + //BOOST_FOREACH(CTxOut o, vout) + // LogPrintf(" vout - %s\n", o.ToString().c_str()); + + + // we should already be connected to a masternode + if(!sessionFoundMasternode){ + LogPrintf("CDarkSendPool::SendDarksendDenominate() - No masternode has been selected yet.\n"); + UnlockCoins(); + SetNull(true); + return; + } + + if (!CheckDiskSpace()) + return; + + if(fMasterNode) { + LogPrintf("CDarkSendPool::SendDarksendDenominate() - DarkSend from a masternode is not supported currently.\n"); + return; + } + + UpdateState(POOL_STATUS_ACCEPTING_ENTRIES); + + LogPrintf("CDarkSendPool::SendDarksendDenominate() - Added transaction to pool.\n"); + + ClearLastMessage(); + + //check it against the memory pool to make sure it's valid + { + int64_t nValueOut = 0; + + CValidationState state; + CTransaction tx; + + BOOST_FOREACH(const CTxOut o, vout){ + nValueOut += o.nValue; + tx.vout.push_back(o); + } + + BOOST_FOREACH(const CTxIn i, vin){ + tx.vin.push_back(i); + + if(fDebug) LogPrintf("dsi -- tx in %s\n", i.ToString().c_str()); + } + + if(AcceptableInputs(mempool, state, tx)){ + LogPrintf("dsi -- transaction not valid! %s \n", tx.ToString().c_str()); + return; + } + } + + // store our entry for later use + CDarkSendEntry e; + e.Add(vin, amount, txCollateral, vout); + myEntries.push_back(e); + + // relay our entry to the master node + RelayDarkSendIn(vin, amount, txCollateral, vout); + Check(); +} + +// Incoming message from masternode updating the progress of darksend +// newAccepted: -1 mean's it'n not a "transaction accepted/not accepted" message, just a standard update +// 0 means transaction was not accepted +// 1 means transaction was accepted + +bool CDarkSendPool::StatusUpdate(int newState, int newEntriesCount, int newAccepted, std::string& error, int newSessionID){ + if(fMasterNode) return false; + if(state == POOL_STATUS_ERROR || state == POOL_STATUS_SUCCESS) return false; + + UpdateState(newState); + entriesCount = newEntriesCount; + + if(newAccepted != -1) { + lastEntryAccepted = newAccepted; + countEntriesAccepted += newAccepted; + if(newAccepted == 0){ + UpdateState(POOL_STATUS_ERROR); + lastMessage = error; + } + + if(newAccepted == 1) { + sessionID = newSessionID; + LogPrintf("CDarkSendPool::StatusUpdate - set sessionID to %d\n", sessionID); + sessionFoundMasternode = true; + } + } + + if(newState == POOL_STATUS_ACCEPTING_ENTRIES){ + if(newAccepted == 1){ + LogPrintf("CDarkSendPool::StatusUpdate - entry accepted! \n"); + sessionFoundMasternode = true; + //wait for other users. Masternode will report when ready + UpdateState(POOL_STATUS_QUEUE); + } else if (newAccepted == 0 && sessionID == 0 && !sessionFoundMasternode) { + LogPrintf("CDarkSendPool::StatusUpdate - entry not accepted by masternode \n"); + UnlockCoins(); + DoAutomaticDenominating(); //try another masternode + } + if(sessionFoundMasternode) return true; + } + + return true; +} + +// +// After we receive the finalized transaction from the masternode, we must +// check it to make sure it's what we want, then sign it if we agree. +// If we refuse to sign, it's possible we'll be charged collateral +// +bool CDarkSendPool::SignFinalTransaction(CTransaction& finalTransactionNew, CNode* node){ + if(fDebug) LogPrintf("CDarkSendPool::AddFinalTransaction - Got Finalized Transaction\n"); + + if(!finalTransaction.vin.empty()){ + LogPrintf("CDarkSendPool::AddFinalTransaction - Rejected Final Transaction!\n"); + return false; + } + + finalTransaction = finalTransactionNew; + LogPrintf("CDarkSendPool::SignFinalTransaction %s\n", finalTransaction.ToString().c_str()); + + vector sigs; + + //make sure my inputs/outputs are present, otherwise refuse to sign + BOOST_FOREACH(const CDarkSendEntry e, myEntries) { + BOOST_FOREACH(const CDarkSendEntryVin s, e.sev) { + /* Sign my transaction and all outputs */ + int mine = -1; + CScript prevPubKey = CScript(); + CTxIn vin = CTxIn(); + + for(unsigned int i = 0; i < finalTransaction.vin.size(); i++){ + if(finalTransaction.vin[i] == s.vin){ + mine = i; + prevPubKey = s.vin.prevPubKey; + vin = s.vin; + } + } + + if(mine >= 0){ //might have to do this one input at a time? + int foundOutputs = 0; + int64_t nValue1 = 0; + int64_t nValue2 = 0; + + for(unsigned int i = 0; i < finalTransaction.vout.size(); i++){ + BOOST_FOREACH(const CTxOut o, e.vout) { + if(finalTransaction.vout[i] == o){ + foundOutputs++; + nValue1 += finalTransaction.vout[i].nValue; + } + } + } + + BOOST_FOREACH(const CTxOut o, e.vout) + nValue2 += o.nValue; + + int targetOuputs = e.vout.size(); + if(foundOutputs < targetOuputs || nValue1 != nValue2) { + // in this case, something went wrong and we'll refuse to sign. It's possible we'll be charged collateral. But that's + // better then signing if the transaction doesn't look like what we wanted. + LogPrintf("CDarkSendPool::Sign - My entries are not correct! Refusing to sign. %d entries %d target. \n", foundOutputs, targetOuputs); + return false; + } + + if(fDebug) LogPrintf("CDarkSendPool::Sign - Signing my input %i\n", mine); + if(!SignSignature(*pwalletMain, prevPubKey, finalTransaction, mine, int(SIGHASH_ALL|SIGHASH_ANYONECANPAY))) { // changes scriptSig + if(fDebug) LogPrintf("CDarkSendPool::Sign - Unable to sign my own transaction! \n"); + // not sure what to do here, it will timeout...? + } + + sigs.push_back(finalTransaction.vin[mine]); + if(fDebug) LogPrintf(" -- dss %d %d %s\n", mine, (int)sigs.size(), finalTransaction.vin[mine].scriptSig.ToString().c_str()); + } + + } + + if(fDebug) LogPrintf("CDarkSendPool::Sign - txNew:\n%s", finalTransaction.ToString().c_str()); + } + + // push all of our signatures to the masternode + if(sigs.size() > 0 && node != NULL) + node->PushMessage("dss", sigs); + + return true; +} + +bool CDarkSendPool::GetBlockHash(uint256& hash, int nBlockHeight) +{ + if(unitTest){ + hash.SetHex("00000000001432b4910722303bff579d0445fa23325bdc34538bdb226718ba79"); + return true; + } + + const CBlockIndex *BlockLastSolved = chainActive.Tip(); + const CBlockIndex *BlockReading = chainActive.Tip(); + + if (BlockLastSolved == NULL || BlockLastSolved->nHeight == 0) { return false; } + + //printf(" nBlockHeight2 %d %d\n", nBlockHeight, chainActive.Tip()->nHeight+1); + + for (unsigned int i = 1; BlockReading && BlockReading->nHeight > 0; i++) { + if(BlockReading->nHeight == nBlockHeight) { + hash = BlockReading->GetBlockHash(); + return true; + } + + if (BlockReading->pprev == NULL) { assert(BlockReading); break; } + BlockReading = BlockReading->pprev; + } + + return false; +} + +//Get the last hash that matches the modulus given. Processed in reverse order +bool CDarkSendPool::GetLastValidBlockHash(uint256& hash, int mod, int nBlockHeight) +{ + if(unitTest){ + hash.SetHex("00000000001432b4910722303bff579d0445fa23325bdc34538bdb226718ba79"); + return true; + } + + const CBlockIndex *BlockLastSolved = chainActive.Tip(); + const CBlockIndex *BlockReading = chainActive.Tip(); + + if (BlockLastSolved == NULL || BlockLastSolved->nHeight == 0) { return false; } + + int nBlocksAgo = 0; + if(nBlockHeight > 0) nBlocksAgo = (chainActive.Tip()->nHeight+1)-nBlockHeight; + assert(nBlocksAgo >= 0); + + int n = 0; + for (unsigned int i = 1; BlockReading && BlockReading->nHeight > 0; i++) { + if(BlockReading->nHeight % mod == 0) { + if(n >= nBlocksAgo){ + hash = BlockReading->GetBlockHash(); + return true; + } + n++; + } + + if (BlockReading->pprev == NULL) { assert(BlockReading); break; } + BlockReading = BlockReading->pprev; + } + + return false; +} + +void CDarkSendPool::NewBlock() +{ + if(fDebug) LogPrintf("CDarkSendPool::NewBlock \n"); + + if(IsInitialBlockDownload()) return; + + masternodePayments.ProcessBlock(chainActive.Tip()->nHeight+10); + + if(!fEnableDarksend) return; + + if(!fMasterNode){ + //denominate all non-denominated inputs every 25 minutes. + if(chainActive.Tip()->nHeight % 10 == 0) UnlockCoins(); + ProcessMasternodeConnections(); + } +} + +// Darksend transaction was completed (failed or successed) +void CDarkSendPool::CompletedTransaction(bool error, std::string lastMessageNew) +{ + if(fMasterNode) return; + + if(error){ + LogPrintf("CompletedTransaction -- error \n"); + UpdateState(POOL_STATUS_ERROR); + } else { + LogPrintf("CompletedTransaction -- success \n"); + UpdateState(POOL_STATUS_SUCCESS); + + myEntries.clear(); + + // To avoid race conditions, we'll only let DS run once per block + cachedLastSuccess = chainActive.Tip()->nHeight; + splitUpInARow = 0; + } + lastMessage = lastMessageNew; + + completedTransaction = true; + Check(); + UnlockCoins(); +} + +void CDarkSendPool::ClearLastMessage() +{ + lastMessage = ""; +} + +// +// Passively run Darksend in the background to anonymize funds based on the given configuration. +// +// This does NOT run by default for daemons, only for QT. +// +bool CDarkSendPool::DoAutomaticDenominating(bool fDryRun, bool ready) +{ + if(fMasterNode) return false; + if(state == POOL_STATUS_ERROR || state == POOL_STATUS_SUCCESS) return false; + + if(chainActive.Tip()->nHeight-cachedLastSuccess < minBlockSpacing) { + LogPrintf("CDarkSendPool::DoAutomaticDenominating - Last successful darksend was too recent\n"); + return false; + } + if(!fEnableDarksend) { + if(fDebug) LogPrintf("CDarkSendPool::DoAutomaticDenominating - Darksend is disabled\n"); + return false; + } + + if (!fDryRun && pwalletMain->IsLocked()){ + return false; + } + + if(darkSendPool.GetState() != POOL_STATUS_ERROR && darkSendPool.GetState() != POOL_STATUS_SUCCESS){ + if(darkSendPool.GetMyTransactionCount() > 0){ + return true; + } + } + + // ** find the coins we'll use + std::vector vCoins; + int64_t nValueMin = 0.01*COIN; + int64_t nValueMax = DARKSEND_POOL_MAX; + int64_t nValueIn = 0; + int minRounds = -2; //non denominated funds are rounds of less than 0 + int maxRounds = 2; + int maxAmount = DARKSEND_POOL_MAX/COIN; + bool hasFeeInput = false; + + // if we have more denominated funds (of any maturity) than the nAnonymizeDarkcoinAmount, we should use use those + if(pwalletMain->GetDenominatedBalance(true) >= nAnonymizeDarkcoinAmount*COIN || + pwalletMain->GetDenominatedBalance(true) >= pwalletMain->GetBalance()*.9) { + minRounds = 0; + maxRounds = nDarksendRounds; + } + //if we're set to less than a thousand, don't submit for than that to the pool + if(nAnonymizeDarkcoinAmount < DARKSEND_POOL_MAX/COIN) maxAmount = nAnonymizeDarkcoinAmount; + + int64_t balanceNeedsAnonymized = pwalletMain->GetBalance() - pwalletMain->GetAnonymizedBalance(); + if(balanceNeedsAnonymized > maxAmount*COIN) balanceNeedsAnonymized= maxAmount*COIN; + if(balanceNeedsAnonymized < COIN*2.5 || + (vecDisabledDenominations.size() > 0 && balanceNeedsAnonymized < COIN*12.5)){ + LogPrintf("DoAutomaticDenominating : No funds detected in need of denominating \n"); + return false; + } + + // if the balance is more the pool max, take the pool max + if(balanceNeedsAnonymized > nValueMax) { + balanceNeedsAnonymized = nValueMax; + } + + // select coins that should be given to the pool + if (!pwalletMain->SelectCoinsDark(nValueMin, maxAmount*COIN, vCoins, nValueIn, minRounds, maxRounds, hasFeeInput)) + { + nValueIn = 0; + vCoins.clear(); + + // look for inputs larger than the max amount, if we find anything we need to split it up + if (pwalletMain->SelectCoinsDark(maxAmount*COIN, 9999999*COIN, vCoins, nValueIn, minRounds, maxRounds, hasFeeInput)) + { + if(!fDryRun) SplitUpMoney(); + return true; + } + + LogPrintf("DoAutomaticDenominating : No funds detected in need of denominating (2)\n"); + return false; + } + + // the darksend pool can only take 2.5DRK minimum + if(nValueIn < COIN*2.5 || + (vecDisabledDenominations.size() > 0 && nValueIn < COIN*12.5) + ){ + //simply look for non-denominated coins + if (pwalletMain->SelectCoinsDark(maxAmount*COIN, 9999999*COIN, vCoins, nValueIn, minRounds, maxRounds, hasFeeInput)) + { + if(!fDryRun) SplitUpMoney(); + return true; + } + + LogPrintf("DoAutomaticDenominating : Too little to denominate \n"); + return false; + } + + //check to see if we have the fee sized inputs, it requires these + if(!pwalletMain->HasDarksendFeeInputs()){ + if(!fDryRun) SplitUpMoney(true); + return true; + } + + if(fDryRun) return true; + + if(vecDisabledDenominations.size() == 0){ + //if we have 20x 0.1DRk and 1DRK inputs, we can start just anonymizing 10DRK inputs. + if(pwalletMain->CountInputsWithAmount((1 * COIN)+1) >= 20 && + pwalletMain->CountInputsWithAmount((.1 * COIN)+1) >= 20){ + vecDisabledDenominations.push_back((1 * COIN)+1); + vecDisabledDenominations.push_back((.1 * COIN)+1); + } + } + + // initial phase, find a masternode + if(!sessionFoundMasternode){ + int nUseQueue = rand()%100; + + sessionTotalValue = pwalletMain->GetTotalValue(vCoins); + + //randomize the amounts we mix + // if we have minRounds set, or if our non-demon is less than 5% of denom coins + if(minRounds == 0 || + pwalletMain->GetDenominatedBalance(true) * 0.05 > pwalletMain->GetDenominatedBalance(false)) { + for(int a = 0; a < 10; a++){ //try 10 amounts and see if we match a queue + int r = (rand()%(maxAmount-(nValueMin/COIN)))+(nValueMin/COIN); + + vCoins.clear(); + nValueIn = 0; + if (pwalletMain->SelectCoinsDark(nValueMin, r*COIN, vCoins, nValueIn, minRounds, maxRounds, hasFeeInput)){ + sessionTotalValue = pwalletMain->GetTotalValue(vCoins); + + // if it's in the queue, take it + if(nUseQueue > 33 && vecDarksendQueue.size() > 0){ + BOOST_FOREACH(CDarksendQueue& dsq, vecDarksendQueue){ + CService addr; + if(dsq.time == 0) continue; + if(!dsq.GetAddress(addr)) continue; + + std::vector vecAmounts; + pwalletMain->ConvertList(vCoins, vecAmounts); + + if(dsq.nDenom == GetDenominationsByAmounts(vecAmounts)) { + break; + } + } + } else { + break; + } + + } + } + } + if(sessionTotalValue > maxAmount*COIN) sessionTotalValue = maxAmount*COIN; + + double fDarkcoinSubmitted = sessionTotalValue / COIN; + + LogPrintf("Submiting Darksend for %f DRK\n", fDarkcoinSubmitted); + + if(pwalletMain->GetDenominatedBalance(true, true) > 0){ //get denominated unconfirmed inputs + LogPrintf("DoAutomaticDenominating -- Found unconfirmed denominated outputs, will wait till they confirm to continue.\n"); + return false; + } + + //don't use the queues all of the time for mixing + if(nUseQueue > 33){ + + // Look through the queues and see if anything matches + BOOST_FOREACH(CDarksendQueue& dsq, vecDarksendQueue){ + CService addr; + if(dsq.time == 0) continue; + if(!dsq.GetAddress(addr)) continue; + if(dsq.IsExpired()) continue; + + int protocolVersion; + if(!dsq.GetProtocolVersion(protocolVersion)) continue; + if(protocolVersion < MIN_PEER_PROTO_VERSION) continue; + + //don't reuse masternodes + BOOST_FOREACH(CTxIn usedVin, vecMasternodesUsed){ + if(dsq.vin == usedVin) { + continue; + } + } + + // If we don't match the denominations, we don't want to submit our inputs + if(dsq.nDenom != GetDenominationsByAmount(sessionTotalValue)) { + if(fDebug) LogPrintf(" dsq.nDenom != GetDenominationsByAmount %d %d \n", dsq.nDenom, GetDenominationsByAmount(sessionTotalValue)); + continue; + } + dsq.time = 0; //remove node + + // connect to masternode and submit the queue request + if(ConnectNode((CAddress)addr, NULL, true)){ + submittedToMasternode = addr; + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + if(submittedToMasternode != pnode->addr) continue; + + std::string strReason; + if(txCollateral == CTransaction()){ + if(!pwalletMain->CreateCollateralTransaction(txCollateral, strReason)){ + LogPrintf("DoAutomaticDenominating -- dsa error:%s\n", strReason.c_str()); + return false; + } + } + + vecMasternodesUsed.push_back(dsq.vin); + + if(minRounds >= 0){ + //use same denominations + std::vector vecAmounts; + pwalletMain->ConvertList(vCoins, vecAmounts); + sessionDenom = GetDenominationsByAmounts(vecAmounts); + } else { + //use all possible denominations + sessionDenom = GetDenominationsByAmount(sessionTotalValue); + } + + pnode->PushMessage("dsa", sessionDenom, txCollateral); + LogPrintf("DoAutomaticDenominating --- connected (from queue), sending dsa for %d %d - %s\n", sessionDenom, GetDenominationsByAmount(sessionTotalValue), pnode->addr.ToString().c_str()); + return true; + } + } else { + LogPrintf("DoAutomaticDenominating --- error connecting \n"); + return DoAutomaticDenominating(); + } + } + } + + // otherwise, try one randomly + if(sessionTries++ < 10){ + //pick a random masternode to use + int max_value = darkSendMasterNodes.size(); + if(max_value <= 0) return false; + int i = (rand() % max_value); + + //don't reuse masternodes + BOOST_FOREACH(CTxIn usedVin, vecMasternodesUsed) { + if(darkSendMasterNodes[i].vin == usedVin){ + return DoAutomaticDenominating(); + } + } + if(darkSendMasterNodes[i].protocolVersion < MIN_PEER_PROTO_VERSION) { + return DoAutomaticDenominating(); + } + + if(darkSendMasterNodes[i].nLastDsq != 0 && + darkSendMasterNodes[i].nLastDsq + CountMasternodesAboveProtocol(darkSendPool.MIN_PEER_PROTO_VERSION)/5 > darkSendPool.nDsqCount){ + return DoAutomaticDenominating(); + } + + lastTimeChanged = GetTimeMillis(); + LogPrintf("DoAutomaticDenominating -- attempt %d connection to masternode %s\n", sessionTries, darkSendMasterNodes[i].addr.ToString().c_str()); + if(ConnectNode((CAddress)darkSendMasterNodes[i].addr, NULL, true)){ + submittedToMasternode = darkSendMasterNodes[i].addr; + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + if(darkSendMasterNodes[i].addr != pnode->addr) continue; + + std::string strReason; + if(txCollateral == CTransaction()){ + if(!pwalletMain->CreateCollateralTransaction(txCollateral, strReason)){ + LogPrintf("DoAutomaticDenominating -- dsa error:%s\n", strReason.c_str()); + return false; + } + } + + vecMasternodesUsed.push_back(darkSendMasterNodes[i].vin); + + if(minRounds >= 0){ + //use same denominations + std::vector vecAmounts; + pwalletMain->ConvertList(vCoins, vecAmounts); + sessionDenom = GetDenominationsByAmounts(vecAmounts); + } else { + //use all possible denominations + sessionDenom = GetDenominationsByAmount(sessionTotalValue); + } + + pnode->PushMessage("dsa", sessionDenom, txCollateral); + LogPrintf("DoAutomaticDenominating --- connected, sending dsa for %d - denom %d\n", sessionDenom, GetDenominationsByAmount(sessionTotalValue)); + return true; + } + } else { + LogPrintf("DoAutomaticDenominating --- error connecting \n"); + return DoAutomaticDenominating(); + } + } else { + return false; + } + } + + if(!ready) return true; + + if(sessionDenom == 0) return true; + + // Submit transaction to the pool if we get here, use sessionDenom so we use the same amount of money + std::string strError = pwalletMain->PrepareDarksendDenominate(minRounds, maxRounds, sessionTotalValue); + LogPrintf("DoAutomaticDenominating : Running darksend denominate. Return '%s'\n", strError.c_str()); + + if(strError == "") return true; + + LogPrintf("DoAutomaticDenominating : Error running denominate, %s\n", strError.c_str()); + return false; +} + +bool CDarkSendPool::SendRandomPaymentToSelf() +{ + int64_t nBalance = pwalletMain->GetBalance(); + int64_t nPayment = (nBalance*0.35) + (rand() % nBalance); + + if(nPayment > nBalance) nPayment = nBalance-(0.1*COIN); + + // make our change address + CReserveKey reservekey(pwalletMain); + + CScript scriptChange; + CPubKey vchPubKey; + assert(reservekey.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked + scriptChange.SetDestination(vchPubKey.GetID()); + + CWalletTx wtx; + int64_t nFeeRet = 0; + std::string strFail = ""; + vector< pair > vecSend; + + // ****** Add fees ************ / + vecSend.push_back(make_pair(scriptChange, nPayment)); + + CCoinControl *coinControl=NULL; + bool success = pwalletMain->CreateTransaction(vecSend, wtx, reservekey, nFeeRet, strFail, coinControl, ONLY_DENOMINATED); + if(!success){ + LogPrintf("SendRandomPaymentToSelf: Error - %s\n", strFail.c_str()); + return false; + } + + pwalletMain->CommitTransaction(wtx, reservekey); + + LogPrintf("SendRandomPaymentToSelf Success: tx %s\n", wtx.GetHash().GetHex().c_str()); + + return true; +} + +// Split up large inputs or create fee sized inputs +bool CDarkSendPool::SplitUpMoney(bool justCollateral) +{ + if((chainActive.Tip()->nHeight - lastSplitUpBlock) < 10){ + LogPrintf("SplitUpMoney - Too soon to split up again\n"); + return false; + } + + if(splitUpInARow >= 2){ + LogPrintf("Error: Darksend SplitUpMoney was called multiple times in a row. This should not happen. Please submit a detailed explanation of the steps it took to create this error and submit to evan@darkcoin.io. \n"); + fEnableDarksend = false; + return false; + } + + int64_t nTotalBalance = pwalletMain->GetDenominatedBalance(false); + if(justCollateral && nTotalBalance > 1*COIN) nTotalBalance = 1*COIN; + int64_t nTotalOut = 0; + lastSplitUpBlock = chainActive.Tip()->nHeight; + + LogPrintf("DoAutomaticDenominating: Split up large input (justCollateral %d):\n", justCollateral); + LogPrintf(" -- nTotalBalance %d\n", nTotalBalance); + LogPrintf(" -- denom %d \n", pwalletMain->GetDenominatedBalance(false)); + + // make our change address + CReserveKey reservekey(pwalletMain); + + CScript scriptChange; + CPubKey vchPubKey; + assert(reservekey.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked + scriptChange.SetDestination(vchPubKey.GetID()); + + CWalletTx wtx; + int64_t nFeeRet = 0; + std::string strFail = ""; + vector< pair > vecSend; + + int64_t a = 1*COIN; + + // ****** Add fees ************ / + vecSend.push_back(make_pair(scriptChange, (DARKSEND_COLLATERAL*5)+DARKSEND_FEE)); + nTotalOut += (DARKSEND_COLLATERAL*5)+DARKSEND_FEE; + vecSend.push_back(make_pair(scriptChange, (DARKSEND_COLLATERAL*5)+DARKSEND_FEE)); + nTotalOut += (DARKSEND_COLLATERAL*5)+DARKSEND_FEE; + + // ****** Add outputs in bases of two from 1 darkcoin *** / + if(!justCollateral){ + bool continuing = true; + + while(continuing){ + if(nTotalOut + a < nTotalBalance){ + //LogPrintf(" nTotalOut %d, added %d\n", nTotalOut, a); + + vecSend.push_back(make_pair(scriptChange, a)); + nTotalOut += a; + } else { + continuing = false; + } + + a = a * 2; + } + } + + if((justCollateral && nTotalOut <= 0.1*COIN) || vecSend.size() < 1) { + LogPrintf("SplitUpMoney: Not enough outputs to make a transaction\n"); + return false; + } + if((!justCollateral && nTotalOut <= 1.1*COIN) || vecSend.size() < 1){ + LogPrintf("SplitUpMoney: Not enough outputs to make a transaction\n"); + return false; + } + + CCoinControl *coinControl=NULL; + bool success = pwalletMain->CreateTransaction(vecSend, wtx, reservekey, + nFeeRet, strFail, coinControl, justCollateral ? ALL_COINS : ONLY_NONDENOMINATED); + if(!success){ + LogPrintf("SplitUpMoney: Error - %s\n", strFail.c_str()); + return false; + } + + pwalletMain->CommitTransaction(wtx, reservekey); + + LogPrintf("SplitUpMoney Success: tx %s\n", wtx.GetHash().GetHex().c_str()); + + splitUpInARow++; + return true; +} + +bool CDarkSendPool::IsCompatibleWithEntries(std::vector vout) +{ + BOOST_FOREACH(const CDarkSendEntry v, entries) { + LogPrintf(" IsCompatibleWithEntries %d %d\n", GetDenominations(vout), GetDenominations(v.vout)); +/* + BOOST_FOREACH(CTxOut o1, vout) + LogPrintf(" vout 1 - %s\n", o1.ToString().c_str()); + + BOOST_FOREACH(CTxOut o2, v.vout) + LogPrintf(" vout 2 - %s\n", o2.ToString().c_str()); +*/ + if(GetDenominations(vout) != GetDenominations(v.vout)) return false; + } + + return true; +} + +bool CDarkSendPool::IsCompatibleWithSession(int64_t nDenom, CTransaction txCollateral, std::string& strReason) +{ + LogPrintf("CDarkSendPool::IsCompatibleWithSession - sessionDenom %d sessionUsers %d\n", sessionDenom, sessionUsers); + + if (!unitTest && !IsCollateralValid(txCollateral)){ + if(fDebug) LogPrintf ("CDarkSendPool::IsCompatibleWithSession - collateral not valid!\n"); + strReason = "collateral not valid"; + return false; + } + + if(sessionUsers < 0) sessionUsers = 0; + + if(sessionUsers == 0) { + sessionDenom = nDenom; + sessionUsers++; + lastTimeChanged = GetTimeMillis(); + entries.clear(); + + if(!unitTest){ + //broadcast that I'm accepting entries, only if it's the first entry though + CDarksendQueue dsq; + dsq.nDenom = nDenom; + dsq.vin = activeMasternode.vinMasternode; + dsq.time = GetTime(); + dsq.Sign(); + dsq.Relay(); + } + + UpdateState(POOL_STATUS_QUEUE); + vecSessionCollateral.push_back(txCollateral); + return true; + } + + if((state != POOL_STATUS_ACCEPTING_ENTRIES && state != POOL_STATUS_QUEUE) || sessionUsers >= GetMaxPoolTransactions()){ + if((state != POOL_STATUS_ACCEPTING_ENTRIES && state != POOL_STATUS_QUEUE)) strReason = "incompatible mode"; + if(sessionUsers >= GetMaxPoolTransactions()) strReason = "masternode queue is full"; + LogPrintf("CDarkSendPool::IsCompatibleWithSession - incompatible mode, return false %d %d\n", state != POOL_STATUS_ACCEPTING_ENTRIES, sessionUsers >= GetMaxPoolTransactions()); + return false; + } + + if(nDenom != sessionDenom) { + strReason = "no matching denominations found for mixing"; + return false; + } + + LogPrintf("CDarkSendPool::IsCompatibleWithSession - compatible\n"); + + sessionUsers++; + lastTimeChanged = GetTimeMillis(); + vecSessionCollateral.push_back(txCollateral); + + return true; +} + +// return a bitshifted integer representing the denominations in this list +int CDarkSendPool::GetDenominations(const std::vector& vout){ + std::vector > denomUsed; + + // make a list of denominations, with zero uses + BOOST_FOREACH(int64_t d, darkSendDenominations) + denomUsed.push_back(make_pair(d, 0)); + + // look for denominations and update uses to 1 + bool found_non_denom = false; + BOOST_FOREACH(CTxOut out, vout){ + bool found = false; + BOOST_FOREACH (PAIRTYPE(int64_t, int)& s, denomUsed){ + if (out.nValue == s.first){ + s.second = 1; + found = true; + } + } + if(!found) found_non_denom = true; + } + + //if other inputs are in here, flip the last bit + if(found_non_denom){ + denomUsed.push_back(make_pair(0, 1)); + } + + int denom = 0; + int c = 0; + // if the denomination is used, shift the bit on. + // then move to the next + BOOST_FOREACH (PAIRTYPE(int64_t, int)& s, denomUsed) + denom |= s.second << c++; + + // Function returns as follows: + // + // bit 0 - 500DRK+1 ( bit on if present ) + // bit 1 - 100DRK+1 + // bit 2 - 10DRK+1 + // bit 3 - 1DRK+1 + // bit 4 - fee + // bit 5 - other sizes + + return denom; +} + +int CDarkSendPool::GetDenominationsByAmounts(std::vector& vecAmount){ + CScript e = CScript(); + std::vector vout1; + + // Make outputs by looping through denominations, from small to large + BOOST_REVERSE_FOREACH(int64_t v, vecAmount){ + int nOutputs = 0; + + CTxOut o(v, e); + vout1.push_back(o); + nOutputs++; + } + + return GetDenominations(vout1); +} + +int CDarkSendPool::GetDenominationsByAmount(int64_t nAmount){ + CScript e = CScript(); + int64_t nValueLeft = nAmount; + + std::vector vout1; + + // Make outputs by looping through denominations, from small to large + BOOST_REVERSE_FOREACH(int64_t v, darkSendDenominations){ + int nOutputs = 0; + + if(std::find(vecDisabledDenominations.begin(), vecDisabledDenominations.end(), v) != vecDisabledDenominations.end()) + continue; + + // add each output up to 10 times until it can't be added again + while(nValueLeft - v >= 0 && nOutputs <= 10) { + CTxOut o(v, e); + vout1.push_back(o); + nValueLeft -= v; + nOutputs++; + } + } + + //add non-denom left overs as change + if(nValueLeft > 0){ + CTxOut o(nValueLeft, e); + vout1.push_back(o); + } + + return GetDenominations(vout1); +} + +bool CDarkSendSigner::IsVinAssociatedWithPubkey(CTxIn& vin, CPubKey& pubkey){ + CScript payee2; + payee2.SetDestination(pubkey.GetID()); + + CTransaction txVin; + uint256 hash; + if(GetTransaction(vin.prevout.hash, txVin, hash, true)){ + BOOST_FOREACH(CTxOut out, txVin.vout){ + if(out.nValue == 1000*COIN){ + if(out.scriptPubKey == payee2) return true; + } + } + } + + return false; +} + +bool CDarkSendSigner::SetKey(std::string strSecret, std::string& errorMessage, CKey& key, CPubKey& pubkey){ + CBitcoinSecret vchSecret; + bool fGood = vchSecret.SetString(strSecret); + + if (!fGood) { + errorMessage = "Invalid private key"; + return false; + } + + key = vchSecret.GetKey(); + pubkey = key.GetPubKey(); + + return true; +} + +bool CDarkSendSigner::SignMessage(std::string strMessage, std::string& errorMessage, vector& vchSig, CKey key) +{ + CHashWriter ss(SER_GETHASH, 0); + ss << strMessageMagic; + ss << strMessage; + + if (!key.SignCompact(ss.GetHash(), vchSig)) { + errorMessage = "Sign failed"; + return false; + } + + return true; +} + +bool CDarkSendSigner::VerifyMessage(CPubKey pubkey, vector& vchSig, std::string strMessage, std::string& errorMessage) +{ + CHashWriter ss(SER_GETHASH, 0); + ss << strMessageMagic; + ss << strMessage; + + CPubKey pubkey2; + if (!pubkey2.RecoverCompact(ss.GetHash(), vchSig)) { + errorMessage = "Error recovering pubkey"; + return false; + } + + return (pubkey2.GetID() == pubkey.GetID()); +} + +bool CDarksendQueue::Sign() +{ + if(!fMasterNode) return false; + + std::string strMessage = vin.ToString() + boost::lexical_cast(nDenom) + boost::lexical_cast(time) + boost::lexical_cast(ready); + + CKey key2; + CPubKey pubkey2; + std::string errorMessage = ""; + + if(!darkSendSigner.SetKey(strMasterNodePrivKey, errorMessage, key2, pubkey2)) + { + LogPrintf("Invalid masternodeprivkey: '%s'\n", errorMessage.c_str()); + exit(0); + } + + if(!darkSendSigner.SignMessage(strMessage, errorMessage, vchSig, key2)) { + LogPrintf("CDarksendQueue():Relay - Sign message failed"); + return false; + } + + if(!darkSendSigner.VerifyMessage(pubkey2, vchSig, strMessage, errorMessage)) { + LogPrintf("CDarksendQueue():Relay - Verify message failed"); + return false; + } + + return true; +} + +bool CDarksendQueue::Relay() +{ + + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes){ + // always relay to everyone + pnode->PushMessage("dsq", (*this)); + } + + return true; +} + +bool CDarksendQueue::CheckSignature() +{ + BOOST_FOREACH(CMasterNode& mn, darkSendMasterNodes) { + + if(mn.vin == vin) { + std::string strMessage = vin.ToString() + boost::lexical_cast(nDenom) + boost::lexical_cast(time) + boost::lexical_cast(ready); + + std::string errorMessage = ""; + if(!darkSendSigner.VerifyMessage(mn.pubkey2, vchSig, strMessage, errorMessage)){ + return error("Got bad masternode address signature %s \n", vin.ToString().c_str()); + } + + return true; + } + } + + return false; +} + + +void ThreadCheckDarkSendPool() +{ + // Make this thread recognisable as the wallet flushing thread + RenameThread("bitcoin-darksend"); + + unsigned int c = 0; + while (true) + { + c++; + + MilliSleep(1000); + //LogPrintf("ThreadCheckDarkSendPool::check timeout\n"); + darkSendPool.CheckTimeout(); + + if(c % 60 == 0){ + vector::iterator it = darkSendMasterNodes.begin(); + while(it != darkSendMasterNodes.end()){ + (*it).Check(); + if((*it).enabled == 4 || (*it).enabled == 3){ + LogPrintf("Removing inactive masternode %s\n", (*it).addr.ToString().c_str()); + it = darkSendMasterNodes.erase(it); + } else { + ++it; + } + } + + masternodePayments.CleanPaymentList(); + CleanTransactionLocksList(); + } + + + //try to sync the masternode list and payment list every 20 seconds + if(c % 5 == 0 && RequestedMasterNodeList <= 2){ + bool fIsInitialDownload = IsInitialBlockDownload(); + if(!fIsInitialDownload) { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + if (pnode->nVersion >= darkSendPool.MIN_PEER_PROTO_VERSION) { + + //keep track of who we've asked for the list + if(pnode->HasFulfilledRequest("mnsync")) continue; + pnode->FulfilledRequest("mnsync"); + + LogPrintf("Successfully synced, asking for Masternode list and payment list\n"); + + pnode->PushMessage("dseg", CTxIn()); //request full mn list + pnode->PushMessage("mnget"); //sync payees + RequestedMasterNodeList++; + } + } + } + } + + /* + if(c % MASTERNODE_PING_SECONDS == 0){ + activeMasternode.RegisterAsMasterNode(false); + }*/ + + if(c % 60 == 0){ + //if we've used 1/5 of the masternode list, then clear the list. + if((int)vecMasternodesUsed.size() > (int)darkSendMasterNodes.size() / 5) + vecMasternodesUsed.clear(); + + } + + //auto denom every 2.5 minutes (liquidity provides try less often) + if(c % 60*(nLiquidityProvider+1) == 0){ + if(nLiquidityProvider!=0){ + int nRand = rand() % (101+nLiquidityProvider); + //about 1/100 chance of starting over after 4 rounds. + if(nRand == 50+nLiquidityProvider && pwalletMain->GetAverageAnonymizedRounds() > 8){ + darkSendPool.SendRandomPaymentToSelf(); + int nLeftToAnon = ((pwalletMain->GetBalance() - pwalletMain->GetAnonymizedBalance())/COIN)-3; + if(nLeftToAnon > 999) nLeftToAnon = 999; + nAnonymizeDarkcoinAmount = (rand() % nLeftToAnon)+3; + } else { + darkSendPool.DoAutomaticDenominating(); + } + } else { + darkSendPool.DoAutomaticDenominating(); + } + } + } +} diff --git a/src/darksend.h b/src/darksend.h new file mode 100644 index 0000000000..1b7af9a97b --- /dev/null +++ b/src/darksend.h @@ -0,0 +1,433 @@ + +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef DARKSEND_H +#define DARKSEND_H + +#include "core.h" +#include "masternode.h" +#include "main.h" +#include "activemasternode.h" + +class CTxIn; +class CDarkSendPool; +class CDarkSendSigner; +class CMasterNodeVote; +class CBitcoinAddress; +class CDarksendQueue; +class CDarksendBroadcastTx; +class CActiveMasternode; + +#define POOL_MAX_TRANSACTIONS 3 // wait for X transactions to merge and publish +#define POOL_STATUS_UNKNOWN 0 // waiting for update +#define POOL_STATUS_IDLE 1 // waiting for update +#define POOL_STATUS_QUEUE 2 // waiting in a queue +#define POOL_STATUS_ACCEPTING_ENTRIES 3 // accepting entries +#define POOL_STATUS_FINALIZE_TRANSACTION 4 // master node will broadcast what it accepted +#define POOL_STATUS_SIGNING 5 // check inputs/outputs, sign final tx +#define POOL_STATUS_TRANSMISSION 6 // transmit transaction +#define POOL_STATUS_ERROR 7 // error +#define POOL_STATUS_SUCCESS 8 // success + +// status update message constants +#define MASTERNODE_ACCEPTED 1 +#define MASTERNODE_REJECTED 0 +#define MASTERNODE_RESET -1 + +#define DARKSEND_QUEUE_TIMEOUT 120 +#define DARKSEND_SIGNING_TIMEOUT 30 + +extern CDarkSendPool darkSendPool; +extern CDarkSendSigner darkSendSigner; +extern std::vector vecDarksendQueue; +extern std::string strMasterNodePrivKey; +extern map mapDarksendBroadcastTxes; +extern CActiveMasternode activeMasternode; + +//specific messages for the Darksend protocol +void ProcessMessageDarksend(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); + +// get the darksend chain depth for a given input +int GetInputDarksendRounds(CTxIn in, int rounds=0); + + +// An input in the darksend pool +class CDarkSendEntryVin +{ +public: + bool isSigSet; + CTxIn vin; + + CDarkSendEntryVin() + { + isSigSet = false; + vin = CTxIn(); + } +}; + +// A clients transaction in the darksend pool +class CDarkSendEntry +{ +public: + bool isSet; + std::vector sev; + int64_t amount; + CTransaction collateral; + std::vector vout; + CTransaction txSupporting; + int64_t addedTime; + + CDarkSendEntry() + { + isSet = false; + collateral = CTransaction(); + amount = 0; + } + + bool Add(const std::vector vinIn, int64_t amountIn, const CTransaction collateralIn, const std::vector voutIn) + { + if(isSet){return false;} + + BOOST_FOREACH(const CTxIn v, vinIn) { + CDarkSendEntryVin s = CDarkSendEntryVin(); + s.vin = v; + sev.push_back(s); + } + vout = voutIn; + amount = amountIn; + collateral = collateralIn; + isSet = true; + addedTime = GetTime(); + + return true; + } + + bool AddSig(const CTxIn& vin) + { + BOOST_FOREACH(CDarkSendEntryVin& s, sev) { + if(s.vin.prevout == vin.prevout && s.vin.nSequence == vin.nSequence){ + if(s.isSigSet){return false;} + s.vin.scriptSig = vin.scriptSig; + s.vin.prevPubKey = vin.prevPubKey; + s.isSigSet = true; + + return true; + } + } + + return false; + } + + bool IsExpired() + { + return (GetTime() - addedTime) > DARKSEND_QUEUE_TIMEOUT;// 120 seconds + } +}; + +// +// A currently inprogress darksend merge and denomination information +// +class CDarksendQueue +{ +public: + CTxIn vin; + int64_t time; + int nDenom; + bool ready; //ready for submit + std::vector vchSig; + + CDarksendQueue() + { + nDenom = 0; + vin = CTxIn(); + time = 0; + vchSig.clear(); + ready = false; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(nDenom); + READWRITE(vin); + READWRITE(time); + READWRITE(ready); + READWRITE(vchSig); + ) + + bool GetAddress(CService &addr) + { + BOOST_FOREACH(CMasterNode mn, darkSendMasterNodes) { + if(mn.vin == vin){ + addr = mn.addr; + return true; + } + } + return false; + } + + bool GetProtocolVersion(int &protocolVersion) + { + BOOST_FOREACH(CMasterNode mn, darkSendMasterNodes) { + if(mn.vin == vin){ + protocolVersion = mn.protocolVersion; + return true; + } + } + return false; + } + + bool Sign(); + bool Relay(); + + bool IsExpired() + { + return (GetTime() - time) > DARKSEND_QUEUE_TIMEOUT;// 120 seconds + } + + bool CheckSignature(); + +}; + +// store darksend tx signature information +class CDarksendBroadcastTx +{ +public: + CTransaction tx; + CTxIn vin; + vector vchSig; + int64_t sigTime; +}; + +// +// Helper object for signing and checking signatures +// +class CDarkSendSigner +{ +public: + bool IsVinAssociatedWithPubkey(CTxIn& vin, CPubKey& pubkey); + bool SetKey(std::string strSecret, std::string& errorMessage, CKey& key, CPubKey& pubkey); + bool SignMessage(std::string strMessage, std::string& errorMessage, std::vector& vchSig, CKey key); + bool VerifyMessage(CPubKey pubkey, std::vector& vchSig, std::string strMessage, std::string& errorMessage); +}; + +class CDarksendSession +{ + +}; + +// +// Used to keep track of current status of darksend pool +// +class CDarkSendPool +{ +public: + static const int MIN_PEER_PROTO_VERSION = 70051; + + // clients entries + std::vector myEntries; + // masternode entries + std::vector entries; + // the finalized transaction ready for signing + CTransaction finalTransaction; + + int64_t lastTimeChanged; + int64_t lastAutoDenomination; + + unsigned int state; + unsigned int entriesCount; + unsigned int lastEntryAccepted; + unsigned int countEntriesAccepted; + + // where collateral should be made out to + CScript collateralPubKey; + + std::vector lockedCoins; + + uint256 masterNodeBlockHash; + + std::string lastMessage; + bool completedTransaction; + bool unitTest; + CService submittedToMasternode; + + int sessionID; + int sessionDenom; //Users must submit an denom matching this + int sessionUsers; //N Users have said they'll join + bool sessionFoundMasternode; //If we've found a compatible masternode + int sessionTries; + int64_t sessionTotalValue; //used for autoDenom + std::vector vecSessionCollateral; + + int lastSplitUpBlock; + int splitUpInARow; // how many splits we've done since a success? + int cachedLastSuccess; + int cachedNumBlocks; //used for the overview screen + int minBlockSpacing; //required blocks between mixes + CTransaction txCollateral; + + std::vector vecDisabledDenominations; + + //incremented whenever a DSQ comes through + int64_t nDsqCount; + + CDarkSendPool() + { + /* DarkSend uses collateral addresses to trust parties entering the pool + to behave themselves. If they don't it takes their money. */ + + std::string strAddress = ""; + if(!(Params().NetworkID() == CChainParams::TESTNET)) { + strAddress = "Xq19GqFvajRrEdDHYRKGYjTsQfpV5jyipF"; + } else { + strAddress = "mxE2Rp3oYpSEFdsN5TdHWhZvEHm3PJQQVm"; + } + + lastSplitUpBlock = 0; + cachedLastSuccess = 0; + cachedNumBlocks = 0; + unitTest = false; + splitUpInARow = 0; + txCollateral = CTransaction(); + minBlockSpacing = 1; + nDsqCount = 0; + vecDisabledDenominations.clear(); + + SetCollateralAddress(strAddress); + SetNull(); + } + + void SetMinBlockSpacing(int minBlockSpacingIn){ + minBlockSpacing = minBlockSpacingIn; + } + + bool SetCollateralAddress(std::string strAddress); + void SetNull(bool clearEverything=false); + + void UnlockCoins(); + + bool IsNull() const + { + return (state == POOL_STATUS_ACCEPTING_ENTRIES && entries.empty() && myEntries.empty()); + } + + int GetState() const + { + return state; + } + + int GetEntriesCount() const + { + if(fMasterNode){ + return entries.size(); + } else { + return entriesCount; + } + } + + int GetLastEntryAccepted() const + { + return lastEntryAccepted; + } + + int GetCountEntriesAccepted() const + { + return countEntriesAccepted; + } + + int GetMyTransactionCount() const + { + return myEntries.size(); + } + + void UpdateState(unsigned int newState) + { + if (fMasterNode && (newState == POOL_STATUS_ERROR || newState == POOL_STATUS_SUCCESS)){ + LogPrintf("CDarkSendPool::UpdateState() - Can't set state to ERROR or SUCCESS as a masternode. \n"); + return; + } + + LogPrintf("CDarkSendPool::UpdateState() == %d | %d \n", state, newState); + if(state != newState){ + lastTimeChanged = GetTimeMillis(); + if(fMasterNode) { + RelayDarkSendStatus(darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_RESET); + } + } + state = newState; + } + + int GetMaxPoolTransactions() + { + //if we're on testnet, just use two transactions per merge + if(Params().NetworkID() == CChainParams::TESTNET) return 2; + + //use the production amount + return POOL_MAX_TRANSACTIONS; + } + + //Do we have enough users to take entries? + bool IsSessionReady(){ + return sessionUsers >= GetMaxPoolTransactions(); + } + + // Are these outputs compatible with other client in the pool? + bool IsCompatibleWithEntries(std::vector vout); + // Is this amount compatible with other client in the pool? + bool IsCompatibleWithSession(int64_t nAmount, CTransaction txCollateral, std::string& strReason); + + // Passively run Darksend in the background according to the configuration in settings (only for QT) + bool DoAutomaticDenominating(bool fDryRun=false, bool ready=false); + + + // check for process in Darksend + void Check(); + // charge fees to bad actors + void ChargeFees(); + // rarely charge fees to pay miners + void ChargeRandomFees(); + void CheckTimeout(); + // check to make sure a signature matches an input in the pool + bool SignatureValid(const CScript& newSig, const CTxIn& newVin); + // if the collateral is valid given by a client + bool IsCollateralValid(const CTransaction& txCollateral); + // add a clients entry to the pool + bool AddEntry(const std::vector& newInput, const int64_t& nAmount, const CTransaction& txCollateral, const std::vector& newOutput, std::string& error); + // add signature to a vin + bool AddScriptSig(const CTxIn& newVin); + // are all inputs signed? + bool SignaturesComplete(); + // as a client, send a transaction to a masternode to start the denomination process + void SendDarksendDenominate(std::vector& vin, std::vector& vout, int64_t amount); + // get masternode updates about the progress of darksend + bool StatusUpdate(int newState, int newEntriesCount, int newAccepted, std::string& error, int newSessionID=0); + + // as a client, check and sign the final transaction + bool SignFinalTransaction(CTransaction& finalTransactionNew, CNode* node); + + // get block hash by height + bool GetBlockHash(uint256& hash, int nBlockHeight); + // get the last valid block hash for a given modulus + bool GetLastValidBlockHash(uint256& hash, int mod=1, int nBlockHeight=0); + // process a new block + void NewBlock(); + void CompletedTransaction(bool error, std::string lastMessageNew); + void ClearLastMessage(); + // used for liquidity providers + bool SendRandomPaymentToSelf(); + // split up large inputs or make fee sized inputs + bool SplitUpMoney(bool justCollateral=false); + // get the denominations for a list of outputs (returns a bitshifted integer) + int GetDenominations(const std::vector& vout); + // get the denominations for a specific amount of darkcoin. + int GetDenominationsByAmount(int64_t nAmount); + int GetDenominationsByAmounts(std::vector& vecAmount); +}; + + +void ConnectToDarkSendMasterNodeWinner(); + +void ThreadCheckDarkSendPool(); + +#endif diff --git a/src/init.cpp b/src/init.cpp index 883d7cb87a..62074434e6 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -20,6 +20,7 @@ #include "txdb.h" #include "ui_interface.h" #include "util.h" +#include "activemasternode.h" #ifdef ENABLE_WALLET #include "db.h" #include "wallet.h" @@ -1079,7 +1080,84 @@ bool AppInit2(boost::thread_group& threadGroup) } threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles)); - // ********************************************************* Step 10: load peers + // ********************************************************* Step 10: setup DarkSend + + //string strNode = "23.23.186.131"; + //CAddress addr; + //ConnectNode(addr, strNode.c_str(), true); + + fMasterNode = GetBoolArg("-masternode", false); + if(fMasterNode) { + LogPrintf("IS DARKSEND MASTER NODE\n"); + strMasterNodeAddr = GetArg("-masternodeaddr", ""); + + LogPrintf(" addr %s\n", strMasterNodeAddr.c_str()); + + if(!strMasterNodeAddr.empty()){ + CService addrTest = CService(strMasterNodeAddr); + if (!addrTest.IsValid()) { + printf("Invalid -masternodeaddr address: '%s'\n", mapArgs["-strMasterNodeAddr"].c_str()); + exit(0); + } + } + + strMasterNodePrivKey = GetArg("-masternodeprivkey", ""); + if(!strMasterNodePrivKey.empty()){ + std::string errorMessage; + + CKey key; + CPubKey pubkey; + + if(!darkSendSigner.SetKey(strMasterNodePrivKey, errorMessage, key, pubkey)) + { + return InitError(_("Invalid masternodeprivkey. Please see documenation.")); + } + + activeMasternode.pubkeyMasterNode2 = pubkey; + + } else { + return InitError(_("You must specify a masternodeprivkey in the configuration. Please see documentation for help.")); + } + } + + fEnableDarksend = GetBoolArg("-enabledarksend", false); + + nDarksendRounds = GetArg("-darksendrounds", 2); + if(nDarksendRounds > 8) nDarksendRounds = 8; + if(nDarksendRounds < 1) nDarksendRounds = 1; + + nLiquidityProvider = GetArg("-liquidityprovider", 0); //1-100 + if(nLiquidityProvider != 0) { + darkSendPool.SetMinBlockSpacing(std::min(nLiquidityProvider,100)*15); + fEnableDarksend = true; + nDarksendRounds = 99999; + } + + nAnonymizeDarkcoinAmount = GetArg("-anonymizedarkcoinamount", 0); + if(nAnonymizeDarkcoinAmount > 999999) nAnonymizeDarkcoinAmount = 999999; + if(nAnonymizeDarkcoinAmount < 2) nAnonymizeDarkcoinAmount = 2; + + bool fEnableInstantX = GetBoolArg("-enableinstantx", true); + if(fEnableInstantX){ + nInstantXDepth = GetArg("-instantxdepth", 1); + if(nInstantXDepth > 60) nInstantXDepth = 60; + if(nInstantXDepth < 0) nAnonymizeDarkcoinAmount = 0; + } else { + nInstantXDepth = 0; + } + + LogPrintf("nInstantXDepth %d\n", nInstantXDepth); + LogPrintf("Darksend rounds %d\n", nDarksendRounds); + LogPrintf("Anonymize Darkcoin Amount %d\n", nAnonymizeDarkcoinAmount); + + darkSendDenominations.push_back( (100 * COIN)+1 ); + darkSendDenominations.push_back( (10 * COIN)+1 ); + darkSendDenominations.push_back( (1 * COIN)+1 ); + darkSendDenominations.push_back( (.1 * COIN)+1 ); + + threadGroup.create_thread(boost::bind(&ThreadCheckDarkSendPool)); + + // ********************************************************* Step 11: load peers uiInterface.InitMessage(_("Loading addresses...")); @@ -1094,7 +1172,7 @@ bool AppInit2(boost::thread_group& threadGroup) LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman.size(), GetTimeMillis() - nStart); - // ********************************************************* Step 11: start node + // ********************************************************* Step 12: start node if (!CheckDiskSpace()) return false; @@ -1106,7 +1184,7 @@ bool AppInit2(boost::thread_group& threadGroup) //// debug print LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size()); - LogPrintf("nBestHeight = %d\n", chainActive.Height()); + LogPrintf("chainActive.Tip()->nHeight = %d\n", chainActive.Height()); #ifdef ENABLE_WALLET LogPrintf("setKeyPool.size() = %u\n", pwalletMain ? pwalletMain->setKeyPool.size() : 0); LogPrintf("mapWallet.size() = %u\n", pwalletMain ? pwalletMain->mapWallet.size() : 0); @@ -1125,7 +1203,7 @@ bool AppInit2(boost::thread_group& threadGroup) GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", -1)); #endif - // ********************************************************* Step 12: finished + // ********************************************************* Step 13: finished uiInterface.InitMessage(_("Done loading")); diff --git a/src/instantx.cpp b/src/instantx.cpp new file mode 100644 index 0000000000..0316c25def --- /dev/null +++ b/src/instantx.cpp @@ -0,0 +1,459 @@ + + + +#include "bignum.h" +#include "sync.h" +#include "net.h" +#include "key.h" +#include "util.h" +#include "script.h" +#include "base58.h" +#include "protocol.h" +#include "instantx.h" +#include "masternode.h" +#include "activemasternode.h" +#include "darksend.h" +#include + +using namespace std; +using namespace boost; + +std::vector vecTxLocks; + +std::map mapTxLockReq; +std::map mapTxLockReqRejected; +std::map mapTxLocks; + +#define INSTANTX_SIGNATURES_REQUIRED 2 + +//txlock - Locks transaction +// +//step 1.) Broadcast intention to lock transaction inputs, "txlreg", CTransaction +//step 2.) Top 10 masternodes, open connect to top 1 masternode. Send "txvote", CTransaction, Signature, Approve +//step 3.) Top 1 masternode, waits for 10 messages. Upon success, sends "txlock' + +void ProcessMessageInstantX(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) +{ + return; + + if (strCommand == "txlreq") + { + //LogPrintf("ProcessMessageInstantX::txlreq\n"); + CDataStream vMsg(vRecv); + CTransaction tx; + vRecv >> tx; + + CInv inv(MSG_TXLOCK_REQUEST, tx.GetHash()); + pfrom->AddInventoryKnown(inv); + + if(mapTxLockReq.count(inv.hash) || mapTxLockReqRejected.count(inv.hash)){ + return; + } + + int nTxAge = GetInputAge(tx.vin[0]); + if(nTxAge < 5){ + LogPrintf("ProcessMessageInstantX::txlreq - Transaction not found / too new: %s\n", tx.GetHash().ToString().c_str()); + return; + } + int nBlockHeight = chainActive.Tip()->nHeight - nTxAge; //calculate the height + + BOOST_FOREACH(const CTxOut o, tx.vout){ + if(!o.scriptPubKey.IsNormalPaymentScript()){ + LogPrintf ("ProcessMessageInstantX::txlreq - Invalid Script %s\n", tx.ToString().c_str()); + return; + } + } + + bool fMissingInputs = false; + CValidationState state; + + if (AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) + { + RelayTransactionLockReq(tx, inv.hash); + DoConsensusVote(tx, true, nBlockHeight); + + mapTxLockReq.insert(make_pair(inv.hash, tx)); + + LogPrintf("ProcessMessageInstantX::txlreq - Transaction Lock Request: %s %s : accepted %s\n", + pfrom->addr.ToString().c_str(), pfrom->cleanSubVer.c_str(), + tx.GetHash().ToString().c_str() + ); + + return; + + } else { + mapTxLockReqRejected.insert(make_pair(inv.hash, tx)); + + // can we get the conflicting transaction as proof? + + RelayTransactionLockReq(tx, inv.hash); + DoConsensusVote(tx, false, nBlockHeight); + + LogPrintf("ProcessMessageInstantX::txlreq - Transaction Lock Request: %s %s : rejected %s\n", + pfrom->addr.ToString().c_str(), pfrom->cleanSubVer.c_str(), + tx.GetHash().ToString().c_str() + ); + + //record prevout, increment the amount of times seen. Ban if over 100 + + return; + } + } + else if (strCommand == "txlvote") //InstantX Lock Consensus Votes + { + CConsensusVote ctx; + vRecv >> ctx; + + ProcessConsensusVote(ctx); + + return; + } + else if (strCommand == "txlock") //InstantX Lock Transaction Inputs + { + LogPrintf("ProcessMessageInstantX::txlock\n"); + + CDataStream vMsg(vRecv); + CTransactionLock ctxl; + vRecv >> ctxl; + + CInv inv(MSG_TXLOCK, ctxl.GetHash()); + pfrom->AddInventoryKnown(inv); + + LogPrintf(" -- ProcessMessageInstantX::txlock %d %s\n", mapTxLocks.count(inv.hash), inv.hash.ToString().c_str()); + + + if(!mapTxLocks.count(inv.hash)){ + if(ctxl.CountSignatures() < INSTANTX_SIGNATURES_REQUIRED){ + LogPrintf("InstantX::txlock - not enough signatures\n"); + return; + } + if(!ctxl.SignaturesValid()){ + LogPrintf("InstantX::txlock - got invalid TransactionLock, rejected\n"); + return; + } + if(!ctxl.AllInFavor()){ + LogPrintf("InstantX::txlock - not all in favor of lock, rejected\n"); + return; + } + + BOOST_FOREACH(const CTxOut o, ctxl.tx.vout){ + if(!o.scriptPubKey.IsNormalPaymentScript()){ + LogPrintf ("ProcessMessageInstantX::cxlock - Invalid Script %s\n", ctxl.tx.ToString().c_str()); + return; + } + } + mapTxLocks.insert(make_pair(inv.hash, ctxl)); + + //we should have the lock request in place + if(!mapTxLockReq.count(inv.hash)){ + //if we don't + bool fMissingInputs = false; + CValidationState state; + + if (AcceptToMemoryPool(mempool, state, ctxl.tx, true, &fMissingInputs)) + { + mapTxLockReq.insert(make_pair(inv.hash, ctxl.tx)); + + LogPrintf("ProcessMessageInstantX::txlock - Transaction Lock Request: %s %s : accepted (no reversing) %s\n", + pfrom->addr.ToString().c_str(), pfrom->cleanSubVer.c_str(), + ctxl.tx.GetHash().ToString().c_str() + ); + + } else { + // we have a conflicting transaction (an attack) + CValidationState state; + DisconnectBlockAndInputs(state, ctxl.tx); + + if (AcceptToMemoryPool(mempool, state, ctxl.tx, true, &fMissingInputs)) + { + mapTxLockReq.insert(make_pair(inv.hash, ctxl.tx)); + + LogPrintf("ProcessMessageInstantX::txlock - Transaction Lock Request: %s %s : accepted (reversed) %s\n", + pfrom->addr.ToString().c_str(), pfrom->cleanSubVer.c_str(), + ctxl.tx.GetHash().ToString().c_str() + ); + } else { + LogPrintf("ProcessMessageInstantX::txlock - Transaction Lock Request: %s %s : rejected (reversed) %s\n", + pfrom->addr.ToString().c_str(), pfrom->cleanSubVer.c_str(), + ctxl.tx.GetHash().ToString().c_str() + ); + } + } + } + + //broadcast the new lock + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + if(!pnode->fRelayTxes) + continue; + + pnode->PushMessage("txlock", ctxl); + } + + pwalletMain->UpdatedTransaction(ctxl.GetHash()); + + LogPrintf("InstantX :: Got Transaction Lock: %s %s : accepted %s\n", + pfrom->addr.ToString().c_str(), pfrom->cleanSubVer.c_str(), + ctxl.GetHash().ToString().c_str() + ); + } + } +} + +// check if we need to vote on this transaction +void DoConsensusVote(CTransaction& tx, bool approved, int64_t nBlockHeight) +{ + if(!fMasterNode) { + LogPrintf("InstantX::DoConsensusVote - Not masternode\n"); + return; + } + + int winner = GetCurrentMasterNode(1, nBlockHeight); + int n = GetMasternodeRank(activeMasternode.vinMasternode, nBlockHeight, MIN_INSTANTX_PROTO_VERSION); + + if(n == -1 || winner == -1) + { + LogPrintf("InstantX::DoConsensusVote - Unknown Masternode\n"); + return; + } + + if(n == 1) + { // winner, I'll be keeping track of this + LogPrintf("InstantX::DoConsensusVote - Managing Masternode\n"); + CTransactionLock newLock; + newLock.nBlockHeight = nBlockHeight; + newLock.tx = tx; + vecTxLocks.push_back(newLock); + } + + CConsensusVote ctx; + ctx.vinMasternode = activeMasternode.vinMasternode; + ctx.approved = approved; + ctx.txHash = tx.GetHash(); + ctx.nBlockHeight = nBlockHeight; + if(!ctx.Sign()){ + LogPrintf("InstantX::DoConsensusVote - Failed to sign consensus vote\n"); + return; + } + if(!ctx.SignatureValid()) { + LogPrintf("InstantX::DoConsensusVote - Signature invalid\n"); + return; + } + + + if(n == 1){ //I'm the winner + ProcessConsensusVote(ctx); + } else if(n <= 10){ // not winner, but in the top10 + if(ConnectNode((CAddress)darkSendMasterNodes[winner].addr, NULL, true)){ + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + if(darkSendMasterNodes[winner].addr != pnode->addr) continue; + + pnode->PushMessage("txlvote", ctx); + LogPrintf("InstantX::DoConsensusVote --- connected, sending vote %s\n", pnode->addr.ToString().c_str()); + return; + } + } else { + LogPrintf("InstantX::DoConsensusVote --- error connecting \n"); + return; + } + } +} + +//received a consensus vote +void ProcessConsensusVote(CConsensusVote& ctx) +{ + if(!fMasterNode) { + LogPrintf("InstantX::ProcessConsensusVote - Not masternode\n"); + return; + } + + int winner = GetCurrentMasterNode(1, ctx.nBlockHeight); + if(winner == -1) { + LogPrintf("InstantX::ProcessConsensusVote - Can't detect winning masternode\n"); + return; + } + + //We're not the winning masternode + if(darkSendMasterNodes[winner].vin != activeMasternode.vinMasternode) { + LogPrintf("InstantX::ProcessConsensusVote - I'm not the winning masternode\n"); + return; + } + + int n = GetMasternodeRank(ctx.vinMasternode, ctx.nBlockHeight, MIN_INSTANTX_PROTO_VERSION); + + if(n == -1) + { + LogPrintf("InstantX::ProcessConsensusVote - Unknown Masternode\n"); + return; + } + + if(n > 10) + { + LogPrintf("InstantX::ProcessConsensusVote - Masternode not in the top 10\n"); + return; + } + + if(!ctx.SignatureValid()) { + LogPrintf("InstantX::ProcessConsensusVote - Signature invalid\n"); + //don't ban, it could just be a non-synced masternode + return; + } + + //compile consessus vote + BOOST_FOREACH(CTransactionLock& ctxl, vecTxLocks){ + if(ctxl.nBlockHeight == ctx.nBlockHeight){ + ctxl.AddSignature(ctx); + if(ctxl.CountSignatures() >= INSTANTX_SIGNATURES_REQUIRED){ + LogPrintf("InstantX::ProcessConsensusVote - Transaction Lock Is Complete, broadcasting!\n"); + + CInv inv(MSG_TXLOCK, ctxl.GetHash()); + mapTxLocks.insert(make_pair(inv.hash, ctxl)); + + //broadcast the new lock + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes){ + pnode->PushMessage("txlock", ctxl); + } + } + return; + } + } + + return; +} + +void CleanTransactionLocksList() +{ + if(chainActive.Tip() == NULL) return; + + std::map::iterator it = mapTxLocks.begin(); + + while(it != mapTxLocks.end()) { + if(chainActive.Tip()->nHeight - it->second.nBlockHeight > 3){ //keep them for an hour + LogPrintf("Removing old transaction lock %s\n", it->second.GetHash().ToString().c_str()); + mapTxLocks.erase(it++); + } else { + it++; + } + } + +} + +bool CConsensusVote::SignatureValid() +{ + std::string errorMessage; + std::string strMessage = txHash.ToString().c_str() + boost::lexical_cast(nBlockHeight) + boost::lexical_cast(approved); + LogPrintf("verify strMessage %s \n", strMessage.c_str()); + + int n = GetMasternodeByVin(vinMasternode); + + if(n == -1) + { + LogPrintf("InstantX::CConsensusVote::SignatureValid() - Unknown Masternode\n"); + return false; + } + + LogPrintf("verify addr %s \n", darkSendMasterNodes[0].addr.ToString().c_str()); + LogPrintf("verify addr %s \n", darkSendMasterNodes[1].addr.ToString().c_str()); + LogPrintf("verify addr %d %s \n", n, darkSendMasterNodes[n].addr.ToString().c_str()); + + CScript pubkey; + pubkey.SetDestination(darkSendMasterNodes[n].pubkey2.GetID()); + CTxDestination address1; + ExtractDestination(pubkey, address1); + CBitcoinAddress address2(address1); + LogPrintf("verify pubkey2 %s \n", address2.ToString().c_str()); + + if(!darkSendSigner.VerifyMessage(darkSendMasterNodes[n].pubkey2, vchMasterNodeSignature, strMessage, errorMessage)) { + LogPrintf("InstantX::CConsensusVote::SignatureValid() - Verify message failed\n"); + return false; + } + + return true; +} + +bool CConsensusVote::Sign() +{ + std::string errorMessage; + + CKey key2; + CPubKey pubkey2; + std::string strMessage = txHash.ToString().c_str() + boost::lexical_cast(nBlockHeight) + boost::lexical_cast(approved); + LogPrintf("signing strMessage %s \n", strMessage.c_str()); + LogPrintf("signing privkey %s \n", strMasterNodePrivKey.c_str()); + + if(!darkSendSigner.SetKey(strMasterNodePrivKey, errorMessage, key2, pubkey2)) + { + LogPrintf("Invalid masternodeprivkey: '%s'\n", errorMessage.c_str()); + exit(0); + } + + CScript pubkey; + pubkey.SetDestination(pubkey2.GetID()); + CTxDestination address1; + ExtractDestination(pubkey, address1); + CBitcoinAddress address2(address1); + LogPrintf("signing pubkey2 %s \n", address2.ToString().c_str()); + + if(!darkSendSigner.SignMessage(strMessage, errorMessage, vchMasterNodeSignature, key2)) { + LogPrintf("CActiveMasternode::RegisterAsMasterNode() - Sign message failed"); + return false; + } + + if(!darkSendSigner.VerifyMessage(pubkey2, vchMasterNodeSignature, strMessage, errorMessage)) { + LogPrintf("CActiveMasternode::RegisterAsMasterNode() - Verify message failed"); + return false; + } + + return true; +} + + +bool CTransactionLock::SignaturesValid() +{ + + BOOST_FOREACH(CConsensusVote vote, vecConsensusVotes) + { + int n = GetMasternodeRank(vote.vinMasternode, vote.nBlockHeight, MIN_INSTANTX_PROTO_VERSION); + + if(n == -1) + { + LogPrintf("InstantX::DoConsensusVote - Unknown Masternode\n"); + return false; + } + + if(n > 10) + { + LogPrintf("InstantX::DoConsensusVote - Masternode not in the top 10\n"); + return false; + } + + if(!vote.SignatureValid()){ + LogPrintf("InstantX::CTransactionLock::SignaturesValid - Signature not valid\n"); + return false; + } + } + + return true; +} + +bool CTransactionLock::AllInFavor() +{ + BOOST_FOREACH(CConsensusVote vote, vecConsensusVotes) + if(vote.approved == false) return false; + + return true; +} + +void CTransactionLock::AddSignature(CConsensusVote cv) +{ + vecConsensusVotes.push_back(cv); +} + +int CTransactionLock::CountSignatures() +{ + return vecConsensusVotes.size(); +} \ No newline at end of file diff --git a/src/instantx.h b/src/instantx.h new file mode 100644 index 0000000000..17934534c8 --- /dev/null +++ b/src/instantx.h @@ -0,0 +1,89 @@ + +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef INSTANTX_H +#define INSTANTX_H + +#include "bignum.h" +#include "sync.h" +#include "net.h" +#include "key.h" +#include "core.h" +#include "util.h" +#include "script.h" +#include "base58.h" +#include "main.h" + +using namespace std; +using namespace boost; + +class CConsensusVote; +class CTransaction; +class CTransactionLock; + +static const int MIN_INSTANTX_PROTO_VERSION = 70047; + +extern map mapTxLockReq; +extern map mapTxLocks; + +void ProcessMessageInstantX(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); + +//check if we need to vote on this transaction +void DoConsensusVote(CTransaction& tx, bool approved, int64_t nBlockHeight); + +//process consensus vote message +void ProcessConsensusVote(CConsensusVote& ctx); + +// keep transaction locks in memory for an hour +void CleanTransactionLocksList(); + +class CConsensusVote +{ +public: + CTxIn vinMasternode; + bool approved; + uint256 txHash; + std::vector vchMasterNodeSignature; + int nBlockHeight; + + bool SignatureValid(); + bool Sign(); + + IMPLEMENT_SERIALIZE + ( + READWRITE(txHash); + READWRITE(vinMasternode); + READWRITE(approved); + READWRITE(vchMasterNodeSignature); + READWRITE(nBlockHeight); + ) +}; + +class CTransactionLock +{ +public: + int nBlockHeight; + CTransaction tx; + std::vector vecConsensusVotes; + + bool SignaturesValid(); + int CountSignatures(); + bool AllInFavor(); + void AddSignature(CConsensusVote cv); + uint256 GetHash() + { + return tx.GetHash(); + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(tx); + READWRITE(nBlockHeight); + READWRITE(vecConsensusVotes); + ) +}; + + +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index b199bcebf6..f2c91a4499 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,6 +12,9 @@ #include "checkpoints.h" #include "checkqueue.h" #include "init.h" +#include "instantx.h" +#include "darksend.h" +#include "masternode.h" #include "net.h" #include "txdb.h" #include "txmempool.h" @@ -74,7 +77,7 @@ void EraseOrphansFor(NodeId peer); // Constant stuff for coinbase transactions we create: CScript COINBASE_FLAGS; -const string strMessageMagic = "Bitcoin Signed Message:\n"; +const string strMessageMagic = "DarkCoin Signed Message:\n"; // Internal stuff namespace { @@ -746,10 +749,29 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) return chainActive.Height() - pindex->nHeight + 1; } +int GetInputAge(CTxIn& vin) +{ + // Fetch previous transactions (inputs): + CCoinsView viewDummy; + CCoinsViewCache view(viewDummy); + { + LOCK(mempool.cs); + CCoinsViewCache &viewChain = *pcoinsTip; + CCoinsViewMemPool viewMempool(viewChain, mempool); + view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view + const uint256& prevHash = vin.prevout.hash; + CCoins coins; + view.GetCoins(prevHash, coins); // this is certainly allowed to fail + view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long + } + if(!view.HaveCoins(vin.prevout.hash)) return -1; + const CCoins &coins = view.GetCoins(vin.prevout.hash); + return (chainActive.Tip()->nHeight+1) - coins.nHeight; +} bool CheckTransaction(const CTransaction& tx, CValidationState &state) @@ -845,7 +867,7 @@ int64_t GetMinFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree, bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fRejectInsaneFee) + bool* pfMissingInputs, bool fRejectInsaneFee, bool ignoreFees) { AssertLockHeld(cs_main); if (pfMissingInputs) @@ -938,34 +960,36 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa unsigned int nSize = entry.GetTxSize(); // Don't accept it if it can't get into a block - int64_t txMinFee = GetMinFee(tx, nSize, true, GMF_RELAY); - if (fLimitFree && nFees < txMinFee) - return state.DoS(0, error("AcceptToMemoryPool : not enough fees %s, %d < %d", - hash.ToString(), nFees, txMinFee), - REJECT_INSUFFICIENTFEE, "insufficient fee"); + if(!ignoreFees){ + int64_t txMinFee = GetMinFee(tx, nSize, true, GMF_RELAY); + if (fLimitFree && nFees < txMinFee) + return state.DoS(0, error("AcceptToMemoryPool : not enough fees %s, %d < %d", + hash.ToString(), nFees, txMinFee), + REJECT_INSUFFICIENTFEE, "insufficient fee"); - // Continuously rate-limit free transactions - // This mitigates 'penny-flooding' -- sending thousands of free transactions just to - // be annoying or make others' transactions take longer to confirm. - if (fLimitFree && nFees < CTransaction::nMinRelayTxFee) - { - static CCriticalSection csFreeLimiter; - static double dFreeCount; - static int64_t nLastTime; - int64_t nNow = GetTime(); + // Continuously rate-limit free transactions + // This mitigates 'penny-flooding' -- sending thousands of free transactions just to + // be annoying or make others' transactions take longer to confirm. + if (fLimitFree && nFees < CTransaction::nMinRelayTxFee) + { + static CCriticalSection csFreeLimiter; + static double dFreeCount; + static int64_t nLastTime; + int64_t nNow = GetTime(); - LOCK(csFreeLimiter); + LOCK(csFreeLimiter); - // Use an exponentially decaying ~10-minute window: - dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); - nLastTime = nNow; - // -limitfreerelay unit is thousand-bytes-per-minute - // At default rate it would take over a month to fill 1GB - if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000) - return state.DoS(0, error("AcceptToMemoryPool : free transaction rejected by rate limiter"), - REJECT_INSUFFICIENTFEE, "insufficient priority"); - LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); - dFreeCount += nSize; + // Use an exponentially decaying ~10-minute window: + dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); + nLastTime = nNow; + // -limitfreerelay unit is thousand-bytes-per-minute + // At default rate it would take over a month to fill 1GB + if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000) + return state.DoS(0, error("AcceptToMemoryPool : free transaction rejected by rate limiter"), + REJECT_INSUFFICIENTFEE, "insufficient priority"); + LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); + dFreeCount += nSize; + } } if (fRejectInsaneFee && nFees > CTransaction::nMinRelayTxFee * 10000) @@ -988,6 +1012,123 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return true; } +bool AcceptableInputs(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool ignoreFees) +{ + AssertLockHeld(cs_main); + + if (!CheckTransaction(tx, state)) + return error("AcceptToMemoryPool: : CheckTransaction failed"); + + // Coinbase is only valid in a block, not as a loose transaction + if (tx.IsCoinBase()) + return state.DoS(100, error("AcceptToMemoryPool: : coinbase as individual tx"), + REJECT_INVALID, "coinbase"); + + // is it already in the memory pool? + uint256 hash = tx.GetHash(); + if (pool.exists(hash)) + return false; + + // Check for conflicts with in-memory transactions + { + LOCK(pool.cs); // protect pool.mapNextTx + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + COutPoint outpoint = tx.vin[i].prevout; + if (pool.mapNextTx.count(outpoint)) + { + // Disable replacement feature for now + return false; + } + } + } + + { + CCoinsView dummy; + CCoinsViewCache view(dummy); + + { + LOCK(pool.cs); + CCoinsViewMemPool viewMemPool(*pcoinsTip, pool); + view.SetBackend(viewMemPool); + + // do we already have it? + if (view.HaveCoins(hash)) + return false; + + // do all inputs exist? + // Note that this does not check for the presence of actual outputs (see the next check for that), + // only helps filling in pfMissingInputs (to determine missing vs spent). + BOOST_FOREACH(const CTxIn txin, tx.vin) { + if (!view.HaveCoins(txin.prevout.hash)) { + return false; + } + } + + // are the actual inputs available? + if (!view.HaveInputs(tx)) + return state.Invalid(error("AcceptToMemoryPool : inputs already spent"), + REJECT_DUPLICATE, "bad-txns-inputs-spent"); + + // Bring the best block into scope + view.GetBestBlock(); + + // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool + view.SetBackend(dummy); + } + + // Don't accept it if it can't get into a block + if(!ignoreFees){ + int64_t nValueIn = view.GetValueIn(tx); + int64_t nValueOut = tx.GetValueOut(); + int64_t nFees = nValueIn-nValueOut; + double dPriority = view.GetPriority(tx, chainActive.Height()); + + CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height()); + unsigned int nSize = entry.GetTxSize(); + + int64_t txMinFee = GetMinFee(tx, nSize, true, GMF_RELAY); + if (nFees < txMinFee) + return state.DoS(0, error("AcceptToMemoryPool : not enough fees %s, %d < %d", + hash.ToString(), nFees, txMinFee), + REJECT_INSUFFICIENTFEE, "insufficient fee"); + + // Continuously rate-limit free transactions + // This mitigates 'penny-flooding' -- sending thousands of free transactions just to + // be annoying or make others' transactions take longer to confirm. + if (nFees < CTransaction::nMinRelayTxFee) + { + static CCriticalSection csFreeLimiter; + static double dFreeCount; + static int64_t nLastTime; + int64_t nNow = GetTime(); + + LOCK(csFreeLimiter); + + // Use an exponentially decaying ~10-minute window: + dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); + nLastTime = nNow; + // -limitfreerelay unit is thousand-bytes-per-minute + // At default rate it would take over a month to fill 1GB + if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000) + return state.DoS(0, error("AcceptToMemoryPool : free transaction rejected by rate limiter"), + REJECT_INSUFFICIENTFEE, "insufficient priority"); + LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); + dFreeCount += nSize; + } + } + + // Check against previous transactions + // This is done last to help prevent CPU exhaustion denial-of-service attacks. + if (!CheckInputs(tx, state, view, false, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC)) + { + return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString()); + } + } + + return true; +} + int CMerkleTx::GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const { @@ -1447,14 +1588,12 @@ unsigned int static DarkGravityWave(const CBlockIndex* pindexLast, const CBlockH unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock) { - uint retarget = DIFF_BTC; + uint retarget = DIFF_DGW; - if (TestNet()) { - if (pindexLast->nHeight + 1 >= 256) retarget = DIFF_DGW; - } - else { + if (!TestNet()) { if (pindexLast->nHeight + 1 >= 34140) retarget = DIFF_DGW; else if (pindexLast->nHeight + 1 >= 15200) retarget = DIFF_KGW; + else retarget = DIFF_BTC; } // Default Bitcoin style retargeting @@ -1505,7 +1644,7 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead // Limit adjustment step //int64 nActualTimespan = pindexLast->GetBlockTime() - pindexFirst->GetBlockTime(); int64_t nActualTimespan = pindexLast->GetBlockTime() - pindexFirst->GetBlockTime(); - //LogPrintf(" nActualTimespan = %"PRI64d" before bounds\n", nActualTimespan); + //LogPrintf(" nActualTimespan = %d before bounds\n", nActualTimespan); LogPrintf(" nActualTimespan = %d before bounds\n", nActualTimespan); if (nActualTimespan < nTargetTimespan/4) nActualTimespan = nTargetTimespan/4; @@ -1523,7 +1662,7 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead /// debug print LogPrintf("GetNextWorkRequired RETARGET\n"); - //LogPrintf("nTargetTimespan = %"PRI64d" nActualTimespan = %"PRI64d"\n", nTargetTimespan, nActualTimespan); + //LogPrintf("nTargetTimespan = %d nActualTimespan = %d\n", nTargetTimespan, nActualTimespan); LogPrintf("nTargetTimespan = %d nActualTimespan = %d\n", nTargetTimespan, nActualTimespan); //LogPrintf("Before: %08x %s\n", pindexLast->nBits, CBigNum().SetCompact(pindexLast->nBits).getuint256().ToString().c_str()); LogPrintf("Before: %08x %s\n", pindexLast->nBits, CBigNum().SetCompact(pindexLast->nBits).getuint256().ToString()); @@ -1940,6 +2079,178 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex } } + +/* + DisconnectBlockAndInputs + + Remove conflicting blocks for successful InstantX transaction locks + This should be very rare (Probably will never happen) +*/ +bool DisconnectBlockAndInputs(CValidationState &state, CTransaction txLock) +{ +/* // All modifications to the coin state will be done in this cache. + // Only when all have succeeded, we push it to pcoinsTip. + CCoinsViewCache view(*pcoinsTip, true); + + CBlockIndex* BlockReading = chainActive.Tip(); + CBlockIndex* pindexNew = NULL; + + int HeightMin = chainActive.Tip()->nHeight-5; + bool foundConflictingTx = false; + + //remove anything conflicting in the memory pool + mempool.removeConflicts(txLock); + + // List of what to disconnect (typically nothing) + vector vDisconnect; + + for (unsigned int i = 1; BlockReading && BlockReading->nHeight > 0 && !foundConflictingTx; i++) { + vDisconnect.push_back(BlockReading); + pindexNew = BlockReading->pprev; //new best block + + CBlock block; + if (!block.ReadFromDisk(BlockReading)) + return state.Abort(_("Failed to read block")); + + // Queue memory transactions to resurrect. + // We only do this for blocks after the last checkpoint (reorganisation before that + // point should only happen with -reindex/-loadblock, or a misbehaving peer. + BOOST_FOREACH(const CTransaction& tx, block.vtx){ + if (!tx.IsCoinBase() && BlockReading->nHeight > HeightMin){ + BOOST_FOREACH(const CTxIn& in1, txLock.vin){ + BOOST_FOREACH(const CTxIn& in2, tx.vin){ + if(in1 == in2) foundConflictingTx = true; + } + } + } + } + + if (BlockReading->pprev == NULL) { assert(BlockReading); break; } + BlockReading = BlockReading->pprev; + } + + if(!foundConflictingTx) { + LogPrintf("DisconnectBlockAndInputs: Can't find a conflicting transaction to inputs\n"); + return false; + } + + if (vDisconnect.size() > 0) { + LogPrintf("REORGANIZE: Disconnect Conflicting Blocks %"PRIszu" blocks; %s..\n", vDisconnect.size(), pindexNew->GetBlockHash().ToString().c_str()); + BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) { + LogPrintf(" -- disconnect %s\n", pindex->GetBlockHash().ToString().c_str()); + } + } + + // Disconnect shorter branch + vector vResurrect; + BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) { + CBlock block; + if (!block.ReadFromDisk(pindex)) + return state.Abort(_("Failed to read block")); + int64 nStart = GetTimeMicros(); + if (!block.DisconnectBlock(state, pindex, view)) + return error("DisconnectBlockAndInputs/SetBestBlock() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().c_str()); + if (fBenchmark) + LogPrintf("- Disconnect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); + + // Queue memory transactions to resurrect. + // We only do this for blocks after the last checkpoint (reorganisation before that + // point should only happen with -reindex/-loadblock, or a misbehaving peer. + BOOST_FOREACH(const CTransaction& tx, block.vtx){ + if (!tx.IsCoinBase() && pindex->nHeight > HeightMin){ + bool isConflict = false; + BOOST_FOREACH(const CTxIn& in1, txLock.vin){ + BOOST_FOREACH(const CTxIn& in2, tx.vin){ + if(in1 != in2) isConflict = true; + } + } + if(!isConflict) vResurrect.push_back(tx); + } + } + + } + + // Make sure it's successfully written to disk before changing memory structure + bool fIsInitialDownload = IsInitialBlockDownload(); + if (!fIsInitialDownload || pcoinsTip->GetCacheSize() > nCoinCacheSize) { + // Typical CCoins structures on disk are around 100 bytes in size. + // Pushing a new one to the database can cause it to be written + // twice (once in the log, and once in the tables). This is already + // an overestimation, as most will delete an existing entry or + // overwrite one. Still, use a conservative safety factor of 2. + if (!CheckDiskSpace(100 * 2 * 2 * pcoinsTip->GetCacheSize())) + return state.Error(); + FlushBlockFile(); + pblocktree->Sync(); + if (!pcoinsTip->Flush()) + return state.Abort(_("Failed to write to coin database")); + } + + // At this point, all changes have been done to the database. + // Proceed by updating the memory structures. + + // Disconnect shorter branch + BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) + if (pindex->pprev) + pindex->pprev->pnext = NULL; + + // Resurrect memory transactions that were in the disconnected branch + BOOST_FOREACH(CTransaction& tx, vResurrect) { + // ignore validation errors in resurrected transactions + CValidationState stateDummy; + if (!tx.AcceptToMemoryPool(stateDummy, true, false)) + mempool.remove(tx, true); + } + + // Update best block in wallet (so we can detect restored wallets) + if ((pindexNew->nHeight % 20160) == 0 || (!fIsInitialDownload && (pindexNew->nHeight % 144) == 0)) + { + const CBlockLocator locator(pindexNew); + ::SetBestChain(locator); + } + + // New best block + hashBestChain = pindexNew->GetBlockHash(); + chainActive.Tip() = pindexNew; + pblockindexFBBHLast = NULL; + chainActive.Tip()->nHeight = chainActive.Tip()->nHeight; + nBestChainWork = pindexNew->nChainWork; + nTimeBestReceived = GetTime(); + nTransactionsUpdated++; + LogPrintf("DisconnectBlockAndInputs / SetBestChain: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f\n", + hashBestChain.ToString().c_str(), chainActive.Tip()->nHeight, log(nBestChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx, + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()).c_str(), + Checkpoints::GuessVerificationProgress(chainActive.Tip())); + + // Check the version of the last 100 blocks to see if we need to upgrade: + if (!fIsInitialDownload) + { + int nUpgraded = 0; + const CBlockIndex* pindex = chainActive.Tip(); + for (int i = 0; i < 100 && pindex != NULL; i++) + { + if (pindex->nVersion > CBlock::CURRENT_VERSION) + ++nUpgraded; + pindex = pindex->pprev; + } + if (nUpgraded > 0) + LogPrintf("DisconnectBlockAndInputs/SetBestChain: %d of last 100 blocks above version %d\n", nUpgraded, CBlock::CURRENT_VERSION); + if (nUpgraded > 100/2) + // strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user: + strMiscWarning = _("Warning: This version is obsolete, upgrade required!"); + } + + std::string strCmd = GetArg("-blocknotify", ""); + + if (!fIsInitialDownload && !strCmd.empty()) + { + boost::replace_all(strCmd, "%s", hashBestChain.GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } +*/ + return true; +} + void static FlushBlockFile(bool fFinalize = false) { LOCK(cs_LastBlockFile); @@ -2564,6 +2875,55 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo return state.DoS(100, error("CheckBlock() : more than one coinbase"), REJECT_INVALID, "bad-cb-multiple"); + bool MasternodePayments = false; + + if(Params().NetworkID() == CChainParams::TESTNET){ + if(block.nTime > START_MASTERNODE_PAYMENTS_TESTNET) MasternodePayments = true; + } else { + if(block.nTime > START_MASTERNODE_PAYMENTS) MasternodePayments = true; + } + + if(MasternodePayments) + { + LOCK2(cs_main, mempool.cs); + + if(chainActive.Tip()->GetBlockHash() == block.hashPrevBlock){ + int64_t masternodePaymentAmount = GetMasternodePayment(chainActive.Tip()->nHeight+1, block.vtx[0].GetValueOut()); + bool fIsInitialDownload = IsInitialBlockDownload(); + + // If we don't already have its previous block, skip masternode payment step + if (!fIsInitialDownload && chainActive.Tip() != NULL) + { + bool foundPaymentAmount = false; + bool foundPayee = false; + + CScript payee; + if(!masternodePayments.GetBlockPayee(chainActive.Tip()->nHeight+1, payee) || payee == CScript()){ + foundPayee = true; //doesn't require a specific payee + } + + for (unsigned int i = 0; i < block.vtx[0].vout.size(); i++) { + if(block.vtx[0].vout[i].nValue == masternodePaymentAmount ) + foundPaymentAmount = true; + if(block.vtx[0].vout[i].scriptPubKey == payee ) + foundPayee = true; + } + + if(!foundPaymentAmount || !foundPayee) { + CTxDestination address1; + ExtractDestination(payee, address1); + CBitcoinAddress address2(address1); + + LogPrintf("CheckBlock() : Couldn't find masternode payment(%d|%d) or payee(%d|%s) nHeight %d. \n", foundPaymentAmount, masternodePaymentAmount, foundPayee, address2.ToString().c_str(), chainActive.Tip()->nHeight+1); + return state.DoS(100, error("CheckBlock() : Couldn't find masternode payment or payee")); + } + } + } else { + LogPrintf("CheckBlock() : Skipping masternode payment check - nHeight %d Hash %s\n", chainActive.Tip()->nHeight+1, block.GetHash().ToString().c_str()); + } + } + + // Check transactions BOOST_FOREACH(const CTransaction& tx, block.vtx) if (!CheckTransaction(tx, state)) @@ -4309,13 +4669,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) LogPrint("net", "Reject %s\n", SanitizeString(ss.str())); } } - else { - // Ignore unknown commands for extensibility + //probably one the extensions + ProcessMessageDarksend(pfrom, strCommand, vRecv); + ProcessMessageMasternode(pfrom, strCommand, vRecv); + ProcessMessageInstantX(pfrom, strCommand, vRecv); } + // Update the last seen time for this node's address if (pfrom->fNetworkNode) if (strCommand == "version" || strCommand == "addr" || strCommand == "inv" || strCommand == "getdata" || strCommand == "ping") diff --git a/src/main.h b/src/main.h index 144a6fb845..27753e788a 100644 --- a/src/main.h +++ b/src/main.h @@ -30,9 +30,6 @@ #include #include -#define START_MASTERNODE_PAYMENTS_TESTNET 1403568776 //Tue, 24 Jun 2014 00:12:56 GMT -#define START_MASTERNODE_PAYMENTS 1403728576 //Wed, 25 Jun 2014 20:36:16 GMT - // Define difficulty retarget algorithms enum DiffMode { DIFF_DEFAULT = 0, // Default to invalid 0 @@ -201,13 +198,11 @@ void Misbehaving(NodeId nodeid, int howmuch); /** (try to) add transaction to memory pool **/ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fRejectInsaneFee=false); - - - - + bool* pfMissingInputs, bool fRejectInsaneFee=false, bool ignoreFees=false); +bool AcceptableInputs(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool ignoreFees=true); +int GetInputAge(CTxIn& vin); struct CNodeStateStats { @@ -608,6 +603,9 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex); * of problems. Note that in any case, coins may be modified. */ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool* pfClean = NULL); +/** Find a conflicting transcation in a block and disconnect all up to that point **/ +bool DisconnectBlockAndInputs(CValidationState &state, CTransaction txLock); + // Apply the effects of this block (with given index) on the UTXO set represented by coins bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck = false); diff --git a/src/masternode.cpp b/src/masternode.cpp new file mode 100644 index 0000000000..28418490dc --- /dev/null +++ b/src/masternode.cpp @@ -0,0 +1,828 @@ + + +#include "masternode.h" +#include "activemasternode.h" +#include "darksend.h" +#include "core.h" +#include "util.h" +#include "addrman.h" +#include + +/** The list of active masternodes */ +std::vector darkSendMasterNodes; +/** Object for who's going to get paid on which blocks */ +CMasternodePayments masternodePayments; +// keep track of masternode votes I've seen +map mapSeenMasternodeVotes; +// keep track of the scanning errors I've seen +map mapSeenMasternodeScanningErrors; +// who's asked for the masternode list and the last time +std::map askedForMasternodeList; +// which masternodes we've asked for +std::map askedForMasternodeListEntry; + +// manage the masternode connections +void ProcessMasternodeConnections(){ + LOCK(cs_vNodes); + + BOOST_FOREACH(CNode* pnode, vNodes) + { + //if it's our masternode, let it be + if(darkSendPool.submittedToMasternode == pnode->addr) continue; + + if(pnode->fDarkSendMaster){ + LogPrintf("Closing masternode connection %s \n", pnode->addr.ToString().c_str()); + pnode->CloseSocketDisconnect(); + } + } +} + +void ProcessMessageMasternode(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) +{ + if (strCommand == "dsee") { //DarkSend Election Entry + bool fIsInitialDownload = IsInitialBlockDownload(); + if(fIsInitialDownload) return; + + CTxIn vin; + CService addr; + CPubKey pubkey; + CPubKey pubkey2; + vector vchSig; + int64_t sigTime; + int count; + int current; + int64_t lastUpdated; + int protocolVersion; + std::string strMessage; + + // 70047 and greater + vRecv >> vin >> addr >> vchSig >> sigTime >> pubkey >> pubkey2 >> count >> current >> lastUpdated >> protocolVersion; + + // make sure signature isn't in the future (past is OK) + if (sigTime > GetAdjustedTime() + 60 * 60) { + LogPrintf("dsee - Signature rejected, too far into the future %s\n", vin.ToString().c_str()); + return; + } + + bool isLocal = false; // addr.IsRFC1918(); + std::string vchPubKey(pubkey.begin(), pubkey.end()); + std::string vchPubKey2(pubkey2.begin(), pubkey2.end()); + + strMessage = addr.ToString() + boost::lexical_cast(sigTime) + vchPubKey + vchPubKey2 + boost::lexical_cast(protocolVersion); + + if(protocolVersion < nMasternodeMinProtocol) { + LogPrintf("dsee - ignoring outdated masternode %s protocol version %d\n", vin.ToString().c_str(), protocolVersion); + return; + } + + CScript pubkeyScript; + pubkeyScript.SetDestination(pubkey.GetID()); + + if(pubkeyScript.size() != 25) { + LogPrintf("dsee - pubkey the wrong size\n"); + Misbehaving(pfrom->GetId(), 100); + return; + } + + CScript pubkeyScript2; + pubkeyScript2.SetDestination(pubkey2.GetID()); + + if(pubkeyScript2.size() != 25) { + LogPrintf("dsee - pubkey the wrong size\n"); + Misbehaving(pfrom->GetId(), 100); + return; + } + + std::string errorMessage = ""; + if(!darkSendSigner.VerifyMessage(pubkey, vchSig, strMessage, errorMessage)){ + LogPrintf("dsee - Got bad masternode address signature\n"); + Misbehaving(pfrom->GetId(), 100); + return; + } + + if((Params().NetworkID() == CChainParams::TESTNET && addr.GetPort() != 19999) || (!(Params().NetworkID() == CChainParams::TESTNET) && addr.GetPort() != 9999)) return; + + //search existing masternode list, this is where we update existing masternodes with new dsee broadcasts + + BOOST_FOREACH(CMasterNode& mn, darkSendMasterNodes) { + if(mn.vin.prevout == vin.prevout) { + // count == -1 when it's a new entry + // e.g. We don't want the entry relayed/time updated when we're syncing the list + // mn.pubkey = pubkey, IsVinAssociatedWithPubkey is validated once below, + // after that they just need to match + if(count == -1 && mn.pubkey == pubkey && !mn.UpdatedWithin(MASTERNODE_MIN_DSEE_SECONDS)){ + mn.UpdateLastSeen(); + + if(mn.now < sigTime){ //take the newest entry + LogPrintf("dsee - Got updated entry for %s\n", addr.ToString().c_str()); + mn.pubkey2 = pubkey2; + mn.now = sigTime; + mn.sig = vchSig; + mn.protocolVersion = protocolVersion; + mn.addr = addr; + + RelayDarkSendElectionEntry(vin, addr, vchSig, sigTime, pubkey, pubkey2, count, current, lastUpdated, protocolVersion); + } + } + + return; + } + } + + // make sure the vout that was signed is related to the transaction that spawned the masternode + // - this is expensive, so it's only done once per masternode +/* if(!darkSendSigner.IsVinAssociatedWithPubkey(vin, pubkey)) { + LogPrintf("dsee - Got mismatched pubkey and vin\n"); + Misbehaving(pfrom->GetId(), 100); + return; + } +*/ + if(fDebug) LogPrintf("dsee - Got NEW masternode entry %s\n", addr.ToString().c_str()); + + // make sure it's still unspent + // - this is checked later by .check() in many places and by ThreadCheckDarkSendPool() + + CValidationState state; + CTransaction tx = CTransaction(); + CTxOut vout = CTxOut(999.99*COIN, darkSendPool.collateralPubKey); + tx.vin.push_back(vin); + tx.vout.push_back(vout); + if(AcceptableInputs(mempool, state, tx)){ + if(fDebug) LogPrintf("dsee - Accepted masternode entry %i %i\n", count, current); + + if(GetInputAge(vin) < MASTERNODE_MIN_CONFIRMATIONS){ + LogPrintf("dsee - Input must have least %d confirmations\n", MASTERNODE_MIN_CONFIRMATIONS); + Misbehaving(pfrom->GetId(), 20); + return; + } + + // use this as a peer + addrman.Add(CAddress(addr), pfrom->addr, 2*60*60); + + // add our masternode + CMasterNode mn(addr, vin, pubkey, vchSig, sigTime, pubkey2, protocolVersion); + mn.UpdateLastSeen(lastUpdated); + darkSendMasterNodes.push_back(mn); + + /*// if it matches our masternodeprivkey, then we've been remotely activated + if(pubkey2 == activeMasternode.pubkeyMasterNode2 && protocolVersion == PROTOCOL_VERSION){ + activeMasternode.EnableHotColdMasterNode(vin, sigTime, addr); + }*/ + + if(count == -1 && !isLocal) + RelayDarkSendElectionEntry(vin, addr, vchSig, sigTime, pubkey, pubkey2, count, current, lastUpdated, protocolVersion); + + } else { + LogPrintf("dsee - Rejected masternode entry %s\n", addr.ToString().c_str()); + + int nDoS = 0; + if (state.IsInvalid(nDoS)) + { + LogPrintf("dsee - %s from %s %s was not accepted into the memory pool\n", tx.GetHash().ToString().c_str(), + pfrom->addr.ToString().c_str(), pfrom->cleanSubVer.c_str()); + if (nDoS > 0) + Misbehaving(pfrom->GetId(), nDoS); + } + } + } + + else if (strCommand == "dseep") { //DarkSend Election Entry Ping + bool fIsInitialDownload = IsInitialBlockDownload(); + if(fIsInitialDownload) return; + + CTxIn vin; + vector vchSig; + int64_t sigTime; + bool stop; + vRecv >> vin >> vchSig >> sigTime >> stop; + + if (sigTime > GetAdjustedTime() + 60 * 60) { + LogPrintf("dseep - Signature rejected, too far into the future %s\n", vin.ToString().c_str()); + return; + } + + if (sigTime <= GetAdjustedTime() - 60 * 60) { + LogPrintf("dseep - Signature rejected, too far into the past %s - %d %d \n", vin.ToString().c_str(), sigTime, GetAdjustedTime()); + return; + } + + // see if we have this masternode + + BOOST_FOREACH(CMasterNode& mn, darkSendMasterNodes) { + if(mn.vin.prevout == vin.prevout) { + // take this only if it's newer + if(mn.lastDseep < sigTime){ + std::string strMessage = mn.addr.ToString() + boost::lexical_cast(sigTime) + boost::lexical_cast(stop); + + std::string errorMessage = ""; + if(!darkSendSigner.VerifyMessage(mn.pubkey2, vchSig, strMessage, errorMessage)){ + LogPrintf("dseep - Got bad masternode address signature %s \n", vin.ToString().c_str()); + //Misbehaving(pfrom->GetId(), 100); + return; + } + + mn.lastDseep = sigTime; + + if(!mn.UpdatedWithin(MASTERNODE_MIN_DSEEP_SECONDS)){ + mn.UpdateLastSeen(); + if(stop) { + mn.Disable(); + mn.Check(); + } + RelayDarkSendElectionEntryPing(vin, vchSig, sigTime, stop); + } + } + return; + } + } + + if(fDebug) LogPrintf("dseep - Couldn't find masternode entry %s\n", vin.ToString().c_str()); + + std::map::iterator i = askedForMasternodeListEntry.find(vin.prevout); + if (i != askedForMasternodeListEntry.end()){ + int64_t t = (*i).second; + if (GetTime() < t) { + // we've asked recently + return; + } + } + + // ask for the dsee info once from the node that sent dseep + + LogPrintf("dseep - Asking source node for missing entry %s\n", vin.ToString().c_str()); + pfrom->PushMessage("dseg", vin); + int64_t askAgain = GetTime()+(60*60*24); + askedForMasternodeListEntry[vin.prevout] = askAgain; + + } else if (strCommand == "dseg") { //Get masternode list or specific entry + CTxIn vin; + vRecv >> vin; + + if(vin == CTxIn()) { //only should ask for this once + //local network + if(!pfrom->addr.IsRFC1918()) + { + std::map::iterator i = askedForMasternodeList.find(pfrom->addr); + if (i != askedForMasternodeList.end()) + { + int64_t t = (*i).second; + if (GetTime() < t) { + Misbehaving(pfrom->GetId(), 100); + LogPrintf("dseg - peer already asked me for the list\n"); + return; + } + } + + int64_t askAgain = GetTime()+(60*60*24); + askedForMasternodeList[pfrom->addr] = askAgain; + } + } //else, asking for a specific node which is ok + + int count = darkSendMasterNodes.size()-1; + int i = 0; + + BOOST_FOREACH(CMasterNode mn, darkSendMasterNodes) { + + if(mn.addr.IsRFC1918()) continue; //local network + + if(vin == CTxIn()){ + mn.Check(); + if(mn.IsEnabled()) { + if(fDebug) LogPrintf("dseg - Sending masternode entry - %s \n", mn.addr.ToString().c_str()); + pfrom->PushMessage("dsee", mn.vin, mn.addr, mn.sig, mn.now, mn.pubkey, mn.pubkey2, count, i, mn.lastTimeSeen, mn.protocolVersion); + } + } else if (vin == mn.vin) { + if(fDebug) LogPrintf("dseg - Sending masternode entry - %s \n", mn.addr.ToString().c_str()); + pfrom->PushMessage("dsee", mn.vin, mn.addr, mn.sig, mn.now, mn.pubkey, mn.pubkey2, count, i, mn.lastTimeSeen, mn.protocolVersion); + LogPrintf("dseg - Sent 1 masternode entries to %s\n", pfrom->addr.ToString().c_str()); + return; + } + i++; + } + + LogPrintf("dseg - Sent %d masternode entries to %s\n", count, pfrom->addr.ToString().c_str()); + } + + else if (strCommand == "mnget") { //Masternode Payments Request Sync + + if(pfrom->HasFulfilledRequest("mnget")) { + LogPrintf("mnget - peer already asked me for the list\n"); + Misbehaving(pfrom->GetId(), 20); + return; + } + + pfrom->FulfilledRequest("mnget"); + masternodePayments.Sync(pfrom); + LogPrintf("mnget - Sent masternode winners to %s\n", pfrom->addr.ToString().c_str()); + } + else if (strCommand == "mnw") { //Masternode Payments Declare Winner + CMasternodePaymentWinner winner; + vRecv >> winner; + + if(chainActive.Tip() == NULL) return; + + uint256 hash = winner.GetHash(); + if(mapSeenMasternodeVotes.count(hash)) { + if(fDebug) LogPrintf("mnw - seen vote %s Height %d bestHeight %d\n", hash.ToString().c_str(), winner.nBlockHeight, chainActive.Tip()->nHeight); + return; + } + + if(winner.nBlockHeight < chainActive.Tip()->nHeight - 10 || winner.nBlockHeight > chainActive.Tip()->nHeight+20){ + LogPrintf("mnw - winner out of range %s Height %d bestHeight %d\n", winner.vin.ToString().c_str(), winner.nBlockHeight, chainActive.Tip()->nHeight); + return; + } + + if(winner.vin.nSequence != std::numeric_limits::max()){ + LogPrintf("mnw - invalid nSequence\n"); + Misbehaving(pfrom->GetId(), 100); + return; + } + + LogPrintf("mnw - winning vote %s Height %d bestHeight %d\n", winner.vin.ToString().c_str(), winner.nBlockHeight, chainActive.Tip()->nHeight); + + if(!masternodePayments.CheckSignature(winner)){ + LogPrintf("mnw - invalid signature\n"); + Misbehaving(pfrom->GetId(), 100); + return; + } + + mapSeenMasternodeVotes.insert(make_pair(hash, 1)); + + if(masternodePayments.AddWinningMasternode(winner)){ + masternodePayments.Relay(winner); + } + } /*else if (strCommand == "mnse") { //Masternode Scanning Error + CMasternodeScanningError entry; + vRecv >> entry; + + if(chainActive.Tip() == NULL) return; + + uint256 hash = entry.GetHash(); + if(mapSeenMasternodeScanningErrors.count(hash)) { + if(fDebug) LogPrintf("mnse - seen entry addr %d error %d\n", entry.addr.ToString().c_str(), entry.error.c_str()); + return; + } + + LogPrintf("mnse - seen entry addr %d error %d\n", entry.addr.ToString().c_str(), entry.error.c_str()); + + if(!masternodeScanningError.CheckSignature(entry)){ + LogPrintf("mnse - invalid signature\n"); + Misbehaving(pfrom->GetId(), 100); + return; + } + + mapSeenMasternodeVotes.insert(make_pair(hash, 1)); + + if(masternodeScanningError.AddWinningMasternode(entry)){ + masternodeScanningError.Relay(entry); + } + }*/ +} + +struct CompareValueOnly +{ + bool operator()(const pair& t1, + const pair& t2) const + { + return t1.first < t2.first; + } +}; + +struct CompareValueOnly2 +{ + bool operator()(const pair& t1, + const pair& t2) const + { + return t1.first < t2.first; + } +}; + +int CountMasternodesAboveProtocol(int protocolVersion) +{ + int i = 0; + + BOOST_FOREACH(CMasterNode& mn, darkSendMasterNodes) { + if(mn.protocolVersion < protocolVersion) continue; + i++; + } + + return i; + +} + + +int GetMasternodeByVin(CTxIn& vin) +{ + int i = 0; + + BOOST_FOREACH(CMasterNode& mn, darkSendMasterNodes) { + if (mn.vin == vin) return i; + i++; + } + + return -1; +} + +int GetCurrentMasterNode(int mod, int64_t nBlockHeight, int minProtocol) +{ + int i = 0; + unsigned int score = 0; + int winner = -1; + + // scan for winner + BOOST_FOREACH(CMasterNode mn, darkSendMasterNodes) { + mn.Check(); + if(mn.protocolVersion < minProtocol) continue; + if(!mn.IsEnabled()) { + i++; + continue; + } + + // calculate the score for each masternode + uint256 n = mn.CalculateScore(mod, nBlockHeight); + unsigned int n2 = 0; + memcpy(&n2, &n, sizeof(n2)); + + // determine the winner + if(n2 > score){ + score = n2; + winner = i; + } + i++; + } + + return winner; +} + +int GetMasternodeByRank(int findRank, int64_t nBlockHeight, int minProtocol) +{ + int i = 0; + + std::vector > vecMasternodeScores; + + i = 0; + BOOST_FOREACH(CMasterNode mn, darkSendMasterNodes) { + mn.Check(); + if(mn.protocolVersion < minProtocol) continue; + if(!mn.IsEnabled()) { + i++; + continue; + } + + uint256 n = mn.CalculateScore(1, nBlockHeight); + unsigned int n2 = 0; + memcpy(&n2, &n, sizeof(n2)); + + vecMasternodeScores.push_back(make_pair(n2, i)); + i++; + } + + sort(vecMasternodeScores.rbegin(), vecMasternodeScores.rend(), CompareValueOnly2()); + + int rank = 0; + BOOST_FOREACH (PAIRTYPE(unsigned int, int)& s, vecMasternodeScores){ + rank++; + if(rank == findRank) return s.second; + } + + return -1; +} + +int GetMasternodeRank(CTxIn& vin, int64_t nBlockHeight, int minProtocol) +{ + std::vector > vecMasternodeScores; + + BOOST_FOREACH(CMasterNode mn, darkSendMasterNodes) { + mn.Check(); + if(mn.protocolVersion < minProtocol) continue; + if(!mn.IsEnabled()) { + continue; + } + + uint256 n = mn.CalculateScore(1, nBlockHeight); + unsigned int n2 = 0; + memcpy(&n2, &n, sizeof(n2)); + + vecMasternodeScores.push_back(make_pair(n2, mn.vin)); + } + + sort(vecMasternodeScores.rbegin(), vecMasternodeScores.rend(), CompareValueOnly()); + + unsigned int rank = 0; + BOOST_FOREACH (PAIRTYPE(unsigned int, CTxIn)& s, vecMasternodeScores){ + rank++; + if(s.second == vin) return rank; + } + + return -1; +} + +// +// Deterministically calculate a given "score" for a masternode depending on how close it's hash is to +// the proof of work for that block. The further away they are the better, the furthest will win the election +// and get paid this block +// +uint256 CMasterNode::CalculateScore(int mod, int64_t nBlockHeight) +{ + if(chainActive.Tip() == NULL) return 0; + + uint256 hash = 0; + if(!darkSendPool.GetLastValidBlockHash(hash, mod, nBlockHeight)) return 0; + uint256 hash2 = HashX11(BEGIN(hash), END(hash)); + + // we'll make a 4 dimensional point in space + // the closest masternode to that point wins + uint64_t a1 = hash2.Get64(0); + uint64_t a2 = hash2.Get64(1); + uint64_t a3 = hash2.Get64(2); + uint64_t a4 = hash2.Get64(3); + + //copy part of our source hash + int i1, i2, i3, i4; + i1=0;i2=0;i3=0;i4=0; + memcpy(&i1, &a1, 1); + memcpy(&i2, &a2, 1); + memcpy(&i3, &a3, 1); + memcpy(&i4, &a4, 1); + + //split up our mn hash + uint64_t b1 = vin.prevout.hash.Get64(0); + uint64_t b2 = vin.prevout.hash.Get64(1); + uint64_t b3 = vin.prevout.hash.Get64(2); + uint64_t b4 = vin.prevout.hash.Get64(3); + + //move mn hash around + b1 <<= (i1 % 64); + b2 <<= (i2 % 64); + b3 <<= (i3 % 64); + b4 <<= (i4 % 64); + + // calculate distance between target point and mn point + uint256 r = 0; + r += (a1 > b1 ? a1 - b1 : b1 - a1); + r += (a2 > b2 ? a2 - b2 : b2 - a2); + r += (a3 > b3 ? a3 - b3 : b3 - a3); + r += (a4 > b4 ? a4 - b4 : b4 - a4); + + /* + LogPrintf(" -- MasterNode CalculateScore() n2 = %s \n", n2.ToString().c_str()); + LogPrintf(" -- MasterNode CalculateScore() vin = %s \n", vin.prevout.hash.GetHex().c_str()); + LogPrintf(" -- MasterNode CalculateScore() n3 = %s \n", n3.ToString().c_str());*/ + + return r; +} + +void CMasterNode::Check() +{ + //once spent, stop doing the checks + if(enabled==3) return; + + + if(!UpdatedWithin(MASTERNODE_REMOVAL_SECONDS)){ + enabled = 4; + return; + } + + if(!UpdatedWithin(MASTERNODE_EXPIRATION_SECONDS)){ + enabled = 2; + return; + } + + if(!unitTest){ + CValidationState state; + CTransaction tx = CTransaction(); + CTxOut vout = CTxOut(999.99*COIN, darkSendPool.collateralPubKey); + tx.vin.push_back(vin); + tx.vout.push_back(vout); + + if(!AcceptableInputs(mempool, state, tx)){ + enabled = 3; + return; + } + } + + enabled = 1; // OK +} + +bool CMasternodePayments::CheckSignature(CMasternodePaymentWinner& winner) +{ + //note: need to investigate why this is failing +/* std::string strMessage = winner.vin.ToString().c_str() + boost::lexical_cast(winner.nBlockHeight); + std::string strPubKey = (Params().NetworkID() == CChainParams::TESTNET) ? strTestPubKey : strMainPubKey; + CPubKey pubkey(ParseHex(strPubKey)); + + std::string errorMessage = ""; + if(!darkSendSigner.VerifyMessage(pubkey, winner.vchSig, strMessage, errorMessage)){ + return false; + } +*/ + return true; +} + +bool CMasternodePayments::Sign(CMasternodePaymentWinner& winner) +{ + std::string strMessage = winner.vin.ToString().c_str() + boost::lexical_cast(winner.nBlockHeight); + + CKey key2; + CPubKey pubkey2; + std::string errorMessage = ""; + + if(!darkSendSigner.SetKey(strMasterPrivKey, errorMessage, key2, pubkey2)) + { + LogPrintf("CMasternodePayments::Sign - Invalid masternodeprivkey: '%s'\n", errorMessage.c_str()); + exit(0); + } + + if(!darkSendSigner.SignMessage(strMessage, errorMessage, winner.vchSig, key2)) { + LogPrintf("CMasternodePayments::Sign - Sign message failed"); + return false; + } + + if(!darkSendSigner.VerifyMessage(pubkey2, winner.vchSig, strMessage, errorMessage)) { + LogPrintf("CMasternodePayments::Sign - Verify message failed"); + return false; + } + + return true; +} + +uint64_t CMasternodePayments::CalculateScore(uint256 blockHash, CTxIn& vin) +{ + uint256 n1 = blockHash; + uint256 n2 = HashX11(BEGIN(n1), END(n1)); + uint256 n3 = HashX11(BEGIN(vin.prevout.hash), END(vin.prevout.hash)); + uint256 n4 = n3 > n2 ? (n3 - n2) : (n2 - n3); + + //printf(" -- CMasternodePayments CalculateScore() n2 = %d \n", n2.Get64()); + //printf(" -- CMasternodePayments CalculateScore() n3 = %d \n", n3.Get64()); + //printf(" -- CMasternodePayments CalculateScore() n4 = %d \n", n4.Get64()); + + return n4.Get64(); +} + +bool CMasternodePayments::GetBlockPayee(int nBlockHeight, CScript& payee) +{ + BOOST_FOREACH(CMasternodePaymentWinner& winner, vWinning){ + if(winner.nBlockHeight == nBlockHeight) { + + CTransaction tx; + uint256 hash; + if(GetTransaction(winner.vin.prevout.hash, tx, hash, true)){ + BOOST_FOREACH(CTxOut out, tx.vout){ + if(out.nValue == 1000*COIN){ + payee = out.scriptPubKey; + return true; + } + } + } + + return false; + } + } + + return false; +} + +bool CMasternodePayments::GetWinningMasternode(int nBlockHeight, CTxIn& vinOut) +{ + BOOST_FOREACH(CMasternodePaymentWinner& winner, vWinning){ + if(winner.nBlockHeight == nBlockHeight) { + vinOut = winner.vin; + return true; + } + } + + return false; +} + +bool CMasternodePayments::AddWinningMasternode(CMasternodePaymentWinner& winnerIn) +{ + uint256 blockHash = 0; + if(!darkSendPool.GetBlockHash(blockHash, winnerIn.nBlockHeight-576)) { + return false; + } + + winnerIn.score = CalculateScore(blockHash, winnerIn.vin); + + bool foundBlock = false; + BOOST_FOREACH(CMasternodePaymentWinner& winner, vWinning){ + if(winner.nBlockHeight == winnerIn.nBlockHeight) { + foundBlock = true; + if(winner.score < winnerIn.score){ + winner.score = winnerIn.score; + winner.vin = winnerIn.vin; + winner.vchSig = winnerIn.vchSig; + return true; + } + } + } + + // if it's not in the vector + if(!foundBlock){ + vWinning.push_back(winnerIn); + return true; + } + + return false; +} + +void CMasternodePayments::CleanPaymentList() +{ + if(chainActive.Tip() == NULL) return; + + vector::iterator it; + for(it=vWinning.begin();itnHeight - (*it).nBlockHeight > 1000){ + if(fDebug) LogPrintf("CMasternodePayments::CleanPaymentList - Removing old masternode payment - block %d\n", (*it).nBlockHeight); + vWinning.erase(it); + break; + } + } +} + +int CMasternodePayments::LastPayment(CMasterNode& mn) +{ + if(chainActive.Tip() == NULL) return 0; + + int ret = mn.GetMasternodeInputAge(); + + BOOST_FOREACH(CMasternodePaymentWinner& winner, vWinning){ + if(winner.vin == mn.vin && chainActive.Tip()->nHeight - winner.nBlockHeight < ret) + ret = chainActive.Tip()->nHeight - winner.nBlockHeight; + } + + return ret; +} + +bool CMasternodePayments::ProcessBlock(int nBlockHeight) +{ + if(strMasterPrivKey.empty()) return false; + CMasternodePaymentWinner winner; + + uint256 blockHash = 0; + if(!darkSendPool.GetBlockHash(blockHash, nBlockHeight-576)) return false; + + BOOST_FOREACH(CMasterNode& mn, darkSendMasterNodes) { + mn.Check(); + + if(!mn.IsEnabled()) { + continue; + } + + if(LastPayment(mn) < darkSendMasterNodes.size()*.9) continue; + + uint64_t score = CalculateScore(blockHash, mn.vin); + if(score > winner.score){ + winner.score = score; + winner.nBlockHeight = nBlockHeight; + winner.vin = mn.vin; + } + } + + if(winner.nBlockHeight == 0) return false; //no masternodes available + + if(Sign(winner)){ + if(AddWinningMasternode(winner)){ + Relay(winner); + return true; + } + } + + return false; +} + +void CMasternodePayments::Relay(CMasternodePaymentWinner& winner) +{ + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes){ + if(!pnode->fRelayTxes) + continue; + + pnode->PushMessage("mnw", winner); + } +} + +void CMasternodePayments::Sync(CNode* node) +{ + BOOST_FOREACH(CMasternodePaymentWinner& winner, vWinning) + if(winner.nBlockHeight >= chainActive.Tip()->nHeight-10 && winner.nBlockHeight <= chainActive.Tip()->nHeight + 20) + node->PushMessage("mnw", winner); +} + + +bool CMasternodePayments::SetPrivKey(std::string strPrivKey) +{ + CMasternodePaymentWinner winner; + + // Test signing successful, proceed + strMasterPrivKey = strPrivKey; + + Sign(winner); + + if(CheckSignature(winner)){ + LogPrintf("CMasternodePayments::SetPrivKey - Successfully initialized as masternode payments master\n"); + return true; + } else { + return false; + } +} diff --git a/src/masternode.h b/src/masternode.h new file mode 100644 index 0000000000..53c99f884e --- /dev/null +++ b/src/masternode.h @@ -0,0 +1,272 @@ + +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef MASTERNODE_H +#define MASTERNODE_H + +#include "bignum.h" +#include "sync.h" +#include "net.h" +#include "key.h" +#include "core.h" +#include "util.h" +#include "script.h" +#include "base58.h" +#include "main.h" + +class CMasterNode; +class CMasternodePayments; + +#define MASTERNODE_NOT_PROCESSED 0 // initial state +#define MASTERNODE_IS_CAPABLE 1 +#define MASTERNODE_NOT_CAPABLE 2 +#define MASTERNODE_STOPPED 3 +#define MASTERNODE_INPUT_TOO_NEW 4 +#define MASTERNODE_PORT_NOT_OPEN 6 +#define MASTERNODE_PORT_OPEN 7 +#define MASTERNODE_SYNC_IN_PROCESS 8 +#define MASTERNODE_REMOTELY_ENABLED 9 + +#define MASTERNODE_MIN_CONFIRMATIONS 15 +#define MASTERNODE_MIN_DSEEP_SECONDS (30*60) +#define MASTERNODE_MIN_DSEE_SECONDS (5*60) +#define MASTERNODE_PING_SECONDS (1*60) +#define MASTERNODE_EXPIRATION_SECONDS (65*60) +#define MASTERNODE_REMOVAL_SECONDS (70*60) + +using namespace std; + +extern std::vector darkSendMasterNodes; +extern CMasternodePayments masternodePayments; +extern std::vector vecMasternodeAskedFor; +extern map mapSeenMasternodeVotes; + + +// manage the masternode connections +void ProcessMasternodeConnections(); +int CountMasternodesAboveProtocol(int protocolVersion); + +// Get the current winner for this block +int GetCurrentMasterNode(int mod=1, int64_t nBlockHeight=0, int minProtocol=0); + +int GetMasternodeByVin(CTxIn& vin); +int GetMasternodeRank(CTxIn& vin, int64_t nBlockHeight=0, int minProtocol=0); +int GetMasternodeByRank(int findRank, int64_t nBlockHeight=0, int minProtocol=0); + +void ProcessMessageMasternode(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); + +// +// The Masternode Class. For managing the darksend process. It contains the input of the 1000DRK, signature to prove +// it's the one who own that ip address and code for calculating the payment election. +// +class CMasterNode +{ +public: + CService addr; + CTxIn vin; + int64_t lastTimeSeen; + CPubKey pubkey; + CPubKey pubkey2; + std::vector sig; + int64_t now; //dsee message times + int64_t lastDseep; + int cacheInputAge; + int cacheInputAgeBlock; + int enabled; + bool unitTest; + bool allowFreeTx; + int protocolVersion; + + //the dsq count from the last dsq broadcast of this node + int64_t nLastDsq; + + CMasterNode(CService newAddr, CTxIn newVin, CPubKey newPubkey, std::vector newSig, int64_t newNow, CPubKey newPubkey2, int protocolVersionIn) + { + addr = newAddr; + vin = newVin; + pubkey = newPubkey; + pubkey2 = newPubkey2; + sig = newSig; + now = newNow; + enabled = 1; + lastTimeSeen = 0; + unitTest = false; + cacheInputAge = 0; + cacheInputAgeBlock = 0; + nLastDsq = 0; + lastDseep = 0; + allowFreeTx = true; + protocolVersion = protocolVersionIn; + } + + uint256 CalculateScore(int mod=1, int64_t nBlockHeight=0); + + void UpdateLastSeen(int64_t override=0) + { + if(override == 0){ + lastTimeSeen = GetAdjustedTime(); + } else { + lastTimeSeen = override; + } + } + + inline uint64_t SliceHash(uint256& hash, int slice) + { + uint64_t n = 0; + memcpy(&n, &hash+slice*64, 64); + return n; + } + + void Check(); + + bool UpdatedWithin(int seconds) + { + // LogPrintf("UpdatedWithin %d, %d -- %d \n", GetAdjustedTime() , lastTimeSeen, (GetAdjustedTime() - lastTimeSeen) < seconds); + + return (GetAdjustedTime() - lastTimeSeen) < seconds; + } + + void Disable() + { + lastTimeSeen = 0; + } + + bool IsEnabled() + { + return enabled == 1; + } + + int GetMasternodeInputAge() + { + if(chainActive.Tip() == NULL) return 0; + + if(cacheInputAge == 0){ + cacheInputAge = GetInputAge(vin); + cacheInputAgeBlock = chainActive.Tip()->nHeight; + } + + return cacheInputAge+(chainActive.Tip()->nHeight-cacheInputAgeBlock); + } +}; + + +// for storing the winning payments +class CMasternodePaymentWinner +{ +public: + int nBlockHeight; + CTxIn vin; + std::vector vchSig; + uint64_t score; + + CMasternodePaymentWinner() { + nBlockHeight = 0; + score = 0; + vin = CTxIn(); + } + + uint256 GetHash(){ + uint256 n2 = HashX11(BEGIN(nBlockHeight), END(nBlockHeight)); + uint256 n3 = vin.prevout.hash > n2 ? (vin.prevout.hash - n2) : (n2 - vin.prevout.hash); + + return n3; + } + + IMPLEMENT_SERIALIZE( + READWRITE(nBlockHeight); + READWRITE(score); + READWRITE(vin); + READWRITE(vchSig); + ) +}; + +// +// Masternode Payments Class +// Keeps track of who should get paid for which blocks +// + +class CMasternodePayments +{ +private: + std::vector vWinning; + int nSyncedFromPeer; + std::string strMasterPrivKey; + std::string strTestPubKey; + std::string strMainPubKey; + +public: + + CMasternodePayments() { + strMainPubKey = "04549ac134f694c0243f503e8c8a9a986f5de6610049c40b07816809b0d1d06a21b07be27b9bb555931773f62ba6cf35a25fd52f694d4e1106ccd237a7bb899fdd"; + strTestPubKey = "046f78dcf911fbd61910136f7f0f8d90578f68d0b3ac973b5040fb7afb501b5939f39b108b0569dca71488f5bbf498d92e4d1194f6f941307ffd95f75e76869f0e"; + } + + bool SetPrivKey(std::string strPrivKey); + bool CheckSignature(CMasternodePaymentWinner& winner); + bool Sign(CMasternodePaymentWinner& winner); + + // Deterministically calculate a given "score" for a masternode depending on how close it's hash is + // to the blockHeight. The further away they are the better, the furthest will win the election + // and get paid this block + // + + uint64_t CalculateScore(uint256 blockHash, CTxIn& vin); + bool GetWinningMasternode(int nBlockHeight, CTxIn& vinOut); + bool AddWinningMasternode(CMasternodePaymentWinner& winner); + bool ProcessBlock(int nBlockHeight); + void Relay(CMasternodePaymentWinner& winner); + void Sync(CNode* node); + void CleanPaymentList(); + int LastPayment(CMasterNode& mn); + + //slow + bool GetBlockPayee(int nBlockHeight, CScript& payee); +}; + +/*// +// Masternode Scanning Error +// Enforces proof-of-service by scanning the masternode network +// + +class CMasternodePayments +{ +private: + std::vector vWinning; + int nSyncedFromPeer; + std::string strMasterPrivKey; + std::string strTestPubKey; + std::string strMainPubKey; + +public: + + CMasternodePayments() { + strMainPubKey = "04549ac134f694c0243f503e8c8a9a986f5de6610049c40b07816809b0d1d06a21b07be27b9bb555931773f62ba6cf35a25fd52f694d4e1106ccd237a7bb899fdd"; + strTestPubKey = "046f78dcf911fbd61910136f7f0f8d90578f68d0b3ac973b5040fb7afb501b5939f39b108b0569dca71488f5bbf498d92e4d1194f6f941307ffd95f75e76869f0e"; + } + + bool SetPrivKey(std::string strPrivKey); + bool CheckSignature(CMasternodePaymentWinner& winner); + bool Sign(CMasternodePaymentWinner& winner); + + // Deterministically calculate a given "score" for a masternode depending on how close it's hash is + // to the blockHeight. The further away they are the better, the furthest will win the election + // and get paid this block + // + + uint64_t CalculateScore(uint256 blockHash, CTxIn& vin); + bool GetWinningMasternode(int nBlockHeight, CTxIn& vinOut); + bool AddWinningMasternode(CMasternodePaymentWinner& winner); + bool ProcessBlock(int nBlockHeight); + void Relay(CMasternodePaymentWinner& winner); + void Sync(CNode* node); + void CleanPaymentList(); + int LastPayment(CMasterNode& mn); + + //slow + bool GetBlockPayee(int nBlockHeight, CScript& payee); +};*/ + + +#endif diff --git a/src/miner.cpp b/src/miner.cpp index 42acb35b70..da093a5ef1 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -113,6 +113,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) return NULL; CBlock *pblock = &pblocktemplate->block; // pointer for convenience + int payments = 1; // Create coinbase tx CTransaction txNew; txNew.vin.resize(1); @@ -140,6 +141,19 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) unsigned int nBlockMinSize = GetArg("-blockminsize", DEFAULT_BLOCK_MIN_SIZE); nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize); + // start masternode payments + bool bMasterNodePayment = false; + + if ( Params().NetworkID() == CChainParams::TESTNET ){ + if (GetTimeMicros() > START_MASTERNODE_PAYMENTS_TESTNET ){ + bMasterNodePayment = true; + } + }else{ + if (GetTimeMicros() > START_MASTERNODE_PAYMENTS){ + bMasterNodePayment = true; + } + } + // Collect memory pool transactions into the block int64_t nFees = 0; { @@ -147,6 +161,35 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) CBlockIndex* pindexPrev = chainActive.Tip(); CCoinsViewCache view(*pcoinsTip, true); + if(bMasterNodePayment) { + bool hasPayment = true; + //spork + if(!masternodePayments.GetBlockPayee(pindexPrev->nHeight+1, pblock->payee)){ + //no masternode detected + int winningNode = GetCurrentMasterNode(1); + if(winningNode >= 0){ + pblock->payee.SetDestination(darkSendMasterNodes[winningNode].pubkey.GetID()); + } else { + LogPrintf("CreateNewBlock: Failed to detect masternode to pay\n"); + hasPayment = false; + } + } + + if(hasPayment){ + payments++; + txNew.vout.resize(payments); + + txNew.vout[payments-1].scriptPubKey = pblock->payee; + txNew.vout[payments-1].nValue = 0; + + CTxDestination address1; + ExtractDestination(pblock->payee, address1); + CBitcoinAddress address2(address1); + + LogPrintf("Masternode payment to %s\n", address2.ToString().c_str()); + } + } + // Priority order to process transactions list vOrphan; // list memory doesn't move map > mapDependers; @@ -322,7 +365,16 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) nLastBlockSize = nBlockSize; LogPrintf("CreateNewBlock(): total size %u\n", nBlockSize); - pblock->vtx[0].vout[0].nValue = GetBlockValue(GetNextWorkRequired(pindexPrev, pblock), pindexPrev->nHeight+1, nFees); + int64_t blockValue = GetBlockValue(GetNextWorkRequired(pindexPrev, pblock), pindexPrev->nHeight+1, nFees); + int64_t masternodePayment = GetMasternodePayment(pindexPrev->nHeight+1, blockValue); + + //create masternode payment + if(payments > 1){ + pblock->vtx[0].vout[payments-1].nValue = masternodePayment; + blockValue -= masternodePayment; + } + pblock->vtx[0].vout[0].nValue = blockValue; + pblocktemplate->vTxFees[0] = -nFees; // Fill in header diff --git a/src/net.cpp b/src/net.cpp index 3db2ecd699..dedd495dfd 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -13,6 +13,8 @@ #include "chainparams.h" #include "core.h" #include "ui_interface.h" +#include "darksend.h" +#include "wallet.h" #ifdef WIN32 #include @@ -437,7 +439,7 @@ CNode* FindNode(const CService& addr) return NULL; } -CNode* ConnectNode(CAddress addrConnect, const char *pszDest) +CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool darkSendMaster) { if (pszDest == NULL) { if (IsLocal(addrConnect)) @@ -447,6 +449,9 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest) CNode* pnode = FindNode((CService)addrConnect); if (pnode) { + if(darkSendMaster) + pnode->fDarkSendMaster = true; + pnode->AddRef(); return pnode; } @@ -486,6 +491,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest) } pnode->nTimeConnected = GetTime(); + if(darkSendMaster) pnode->fDarkSendMaster = true; return pnode; } else @@ -1843,6 +1849,88 @@ void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataSt } } + +void RelayTransactionLockReq(const CTransaction& tx, const uint256& hash, bool relayToAll) +{ + CInv inv(MSG_TXLOCK_REQUEST, tx.GetHash()); + + //broadcast the new lock + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + if(!relayToAll && !pnode->fRelayTxes) + continue; + + //there's no requests for transactions locks, so we should show it was propagated correctly + //pwalletMain->mapRequestCount[tx.GetHash()]++; + + pnode->PushMessage("txlreq", tx); + } + +} + +void RelayDarkSendFinalTransaction(const int sessionID, const CTransaction& txNew) +{ + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + pnode->PushMessage("dsf", sessionID, txNew); + } +} + +void RelayDarkSendIn(const std::vector& in, const int64_t& nAmount, const CTransaction& txCollateral, const std::vector& out) +{ + LOCK(cs_vNodes); + + BOOST_FOREACH(CNode* pnode, vNodes) + { + if(darkSendPool.submittedToMasternode != pnode->addr) continue; + LogPrintf("RelayDarkSendIn - found master, relaying message - %s \n", pnode->addr.ToString().c_str()); + pnode->PushMessage("dsi", in, nAmount, txCollateral, out); + } +} + +void RelayDarkSendStatus(const int sessionID, const int newState, const int newEntriesCount, const int newAccepted, const std::string error) +{ + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + pnode->PushMessage("dssu", sessionID, newState, newEntriesCount, newAccepted, error); + } +} + +void RelayDarkSendElectionEntry(const CTxIn vin, const CService addr, const std::vector vchSig, const int64_t nNow, const CPubKey pubkey, const CPubKey pubkey2, const int count, const int current, const int64_t lastUpdated, const int protocolVersion) +{ + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + if(!pnode->fRelayTxes) continue; + + pnode->PushMessage("dsee", vin, addr, vchSig, nNow, pubkey, pubkey2, count, current, lastUpdated, protocolVersion); + } +} + +void RelayDarkSendElectionEntryPing(const CTxIn vin, const std::vector vchSig, const int64_t nNow, const bool stop) +{ + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + if(!pnode->fRelayTxes) continue; + + pnode->PushMessage("dseep", vin, vchSig, nNow, stop); + } +} + +void RelayDarkSendCompletedTransaction(const int sessionID, const bool error, const std::string errorMessage) +{ + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + pnode->PushMessage("dsc", sessionID, error, errorMessage); + } +} + + void CNode::RecordBytesRecv(uint64_t bytes) { LOCK(cs_totalBytesRecv); diff --git a/src/net.h b/src/net.h index cb07156bdf..390c374b68 100644 --- a/src/net.h +++ b/src/net.h @@ -16,6 +16,7 @@ #include "sync.h" #include "uint256.h" #include "util.h" +#include "core.h" #include #include @@ -50,7 +51,7 @@ bool GetMyExternalIP(CNetAddr& ipRet); void AddressCurrentlyConnected(const CService& addr); CNode* FindNode(const CNetAddr& ip); CNode* FindNode(const CService& ip); -CNode* ConnectNode(CAddress addrConnect, const char *strDest = NULL); +CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool darkSendMaster=false); void MapPort(bool fUseUPnP); unsigned short GetListenPort(); bool BindListenPort(const CService &bindAddr, std::string& strError=REF(std::string())); @@ -233,6 +234,7 @@ public: // b) the peer may tell us in their version message that we should not relay tx invs // until they have initialized their bloom filter. bool fRelayTxes; + bool fDarkSendMaster; CSemaphoreGrant grantOutbound; CCriticalSection cs_filter; CBloomFilter* pfilter; @@ -245,6 +247,8 @@ protected: static std::map setBanned; static CCriticalSection cs_setBanned; + std::vector vecRequestsFulfilled; //keep track of what client has asked for + // Basic fuzz-testing void Fuzz(int nChance); // modifies ssSend @@ -676,6 +680,38 @@ public: throw; } } + + template + void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8, const T9& a9, const T10& a10) + { + try + { + BeginMessage(pszCommand); + ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8 << a9 << a10; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + + + bool HasFulfilledRequest(std::string strRequest) + { + BOOST_FOREACH(std::string& type, vecRequestsFulfilled) + { + if(type == strRequest) return true; + } + return false; + } + + void FulfilledRequest(std::string strRequest) + { + if(HasFulfilledRequest(strRequest)) return; + vecRequestsFulfilled.push_back(strRequest); + } bool IsSubscribed(unsigned int nChannel); void Subscribe(unsigned int nChannel, unsigned int nHops=0); @@ -716,6 +752,15 @@ public: class CTransaction; void RelayTransaction(const CTransaction& tx, const uint256& hash); void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataStream& ss); +void RelayTransactionLockReq(const CTransaction& tx, const uint256& hash, bool relayToAll=false); +void RelayDarkSendFinalTransaction(const int sessionID, const CTransaction& txNew); +void RelayDarkSendIn(const std::vector& in, const int64_t& nAmount, const CTransaction& txCollateral, const std::vector& out); +void RelayDarkSendStatus(const int sessionID, const int newState, const int newEntriesCount, const int newAccepted, const std::string error=""); +void RelayDarkSendElectionEntry(const CTxIn vin, const CService addr, const std::vector vchSig, const int64_t nNow, const CPubKey pubkey, const CPubKey pubkey2, const int count, const int current, const int64_t lastUpdated, const int protocolVersion); +void RelayDarkSendElectionEntryPing(const CTxIn vin, const std::vector vchSig, const int64_t nNow, const bool stop); +void RelayDarkSendCompletedTransaction(const int sessionID, const bool error, const std::string errorMessage); +void RelayDarkSendMasterNodeContestant(); + /** Access to the (IP) address database (peers.dat) */ class CAddrDB diff --git a/src/protocol.h b/src/protocol.h index 86e08ddcfa..f3ca6ac0ec 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -135,6 +135,8 @@ enum // Nodes may always request a MSG_FILTERED_BLOCK in a getdata, however, // MSG_FILTERED_BLOCK should not appear in any invs except as a part of getdata. MSG_FILTERED_BLOCK, + MSG_TXLOCK_REQUEST, + MSG_TXLOCK, }; #endif // __INCLUDED_PROTOCOL_H__ diff --git a/src/qt/Makefile.am b/src/qt/Makefile.am index 648971bd8f..24d7ef7071 100644 --- a/src/qt/Makefile.am +++ b/src/qt/Makefile.am @@ -5,12 +5,12 @@ AM_CPPFLAGS += -I$(top_srcdir)/src \ -I$(top_builddir)/src/qt/forms \ $(PROTOBUF_CFLAGS) \ $(QR_CFLAGS) -bin_PROGRAMS = bitcoin-qt -noinst_LIBRARIES = libbitcoinqt.a +bin_PROGRAMS = darkcoin-qt +noinst_LIBRARIES = libdarkcoinqt.a SUBDIRS = . $(BUILD_TEST_QT) DIST_SUBDIRS = . test -# bitcoin qt core # +# darkcoin qt core # QT_TS = \ locale/bitcoin_ach.ts \ locale/bitcoin_af_ZA.ts \ @@ -261,7 +261,7 @@ RES_ICONS = \ res/icons/tx_mined.png BITCOIN_QT_CPP = \ - bitcoin.cpp \ + darkcoin.cpp \ bitcoinaddressvalidator.cpp \ bitcoinamountfield.cpp \ bitcoingui.cpp \ @@ -321,38 +321,38 @@ RES_MOVIES = $(wildcard res/movies/spinner-*.png) BITCOIN_RC = res/bitcoin-qt-res.rc -libbitcoinqt_a_CPPFLAGS = $(AM_CPPFLAGS) $(QT_INCLUDES) \ +libdarkcoinqt_a_CPPFLAGS = $(AM_CPPFLAGS) $(QT_INCLUDES) \ -I$(top_srcdir)/src/qt/forms $(QT_DBUS_INCLUDES) -libbitcoinqt_a_SOURCES = $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(QT_FORMS_UI) \ +libdarkcoinqt_a_SOURCES = $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(QT_FORMS_UI) \ $(QT_QRC) $(QT_TS) $(PROTOBUF_PROTO) $(RES_ICONS) $(RES_IMAGES) $(RES_MOVIES) -nodist_libbitcoinqt_a_SOURCES = $(QT_MOC_CPP) $(QT_MOC) $(PROTOBUF_CC) \ +nodist_libdarkcoinqt_a_SOURCES = $(QT_MOC_CPP) $(QT_MOC) $(PROTOBUF_CC) \ $(PROTOBUF_H) $(QT_QRC_CPP) -BUILT_SOURCES = $(nodist_libbitcoinqt_a_SOURCES) +BUILT_SOURCES = $(nodist_libdarkcoinqt_a_SOURCES) #Generating these with a half-written protobuf header leads to wacky results. #This makes sure it's done. $(QT_MOC): $(PROTOBUF_H) $(QT_MOC_CPP): $(PROTOBUF_H) -# bitcoin-qt binary # -bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(QT_INCLUDES) \ +# darkcoin-qt binary # +darkcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(QT_INCLUDES) \ -I$(top_srcdir)/src/qt/forms -bitcoin_qt_SOURCES = bitcoin.cpp +darkcoin_qt_SOURCES = darkcoin.cpp if TARGET_DARWIN - bitcoin_qt_SOURCES += $(BITCOIN_MM) + darkcoin_qt_SOURCES += $(BITCOIN_MM) endif if TARGET_WINDOWS - bitcoin_qt_SOURCES += $(BITCOIN_RC) + darkcoin_qt_SOURCES += $(BITCOIN_RC) endif -bitcoin_qt_LDADD = libbitcoinqt.a $(LIBBITCOIN_SERVER) +darkcoin_qt_LDADD = libdarkcoinqt.a $(LIBBITCOIN_SERVER) if ENABLE_WALLET -bitcoin_qt_LDADD += $(LIBBITCOIN_WALLET) +darkcoin_qt_LDADD += $(LIBBITCOIN_WALLET) endif -bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBLEVELDB) $(LIBMEMENV) \ +darkcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBLEVELDB) $(LIBMEMENV) \ $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) -bitcoin_qt_LDFLAGS = $(QT_LDFLAGS) +darkcoin_qt_LDFLAGS = $(QT_LDFLAGS) # forms/foo.h -> forms/ui_foo.h QT_FORMS_H=$(join $(dir $(QT_FORMS_UI)),$(addprefix ui_, $(notdir $(QT_FORMS_UI:.ui=.h)))) @@ -371,7 +371,7 @@ translate: bitcoinstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_CPP) $( @QT_SELECT=$(QT_SELECT) $(LUPDATE) $^ -locations relative -no-obsolete -ts locale/bitcoin_en.ts $(QT_QRC_CPP): $(QT_QRC) $(QT_QM) $(QT_FORMS_H) $(RES_ICONS) $(RES_IMAGES) $(RES_MOVIES) $(PROTOBUF_H) - @cd $(abs_srcdir); test -f $(RCC) && QT_SELECT=$(QT_SELECT) $(RCC) -name bitcoin -o $(abs_builddir)/$@ $< || \ + @cd $(abs_srcdir); test -f $(RCC) && QT_SELECT=$(QT_SELECT) $(RCC) -name darkcoin -o $(abs_builddir)/$@ $< || \ echo error: could not build $@ $(SED) -e '/^\*\*.*Created:/d' $@ > $@.n && mv $@{.n,} $(SED) -e '/^\*\*.*by:/d' $@ > $@.n && mv $@{.n,} diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index dfbd445ce3..7b05d224bd 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -370,7 +370,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con CPubKey newKey; if(!wallet->GetKeyFromPool(newKey)) { - WalletModel::UnlockContext ctx(walletModel->requestUnlock()); + WalletModel::UnlockContext ctx(walletModel->requestUnlock(true)); if(!ctx.isValid()) { // Unlock wallet failed or was cancelled diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index 2a6d6abc35..871b4ffe38 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -40,6 +40,9 @@ AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget *parent) : ui->warningLabel->setText(tr("Enter the new passphrase to the wallet.
Please use a passphrase of 10 or more random characters, or eight or more words.")); setWindowTitle(tr("Encrypt wallet")); break; + case UnlockAnonymize: + ui->anonymizationCheckBox->setChecked(true); + ui->anonymizationCheckBox->show(); case Unlock: // Ask passphrase ui->warningLabel->setText(tr("This operation needs your wallet passphrase to unlock the wallet.")); ui->passLabel2->hide(); @@ -80,6 +83,7 @@ AskPassphraseDialog::~AskPassphraseDialog() void AskPassphraseDialog::setModel(WalletModel *model) { this->model = model; + ui->anonymizationCheckBox->setChecked(model->isAnonymizeOnlyUnlocked()); } void AskPassphraseDialog::accept() @@ -105,7 +109,7 @@ void AskPassphraseDialog::accept() break; } QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm wallet encryption"), - tr("Warning: If you encrypt your wallet and lose your passphrase, you will LOSE ALL OF YOUR BITCOINS!") + "

" + tr("Are you sure you wish to encrypt your wallet?"), + tr("Warning: If you encrypt your wallet and lose your passphrase, you will LOSE ALL OF YOUR DARKCOINS!") + "

" + tr("Are you sure you wish to encrypt your wallet?"), QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Cancel); if(retval == QMessageBox::Yes) @@ -116,9 +120,9 @@ void AskPassphraseDialog::accept() { QMessageBox::warning(this, tr("Wallet encrypted"), "" + - tr("Bitcoin will close now to finish the encryption process. " + tr("DarkCoin will close now to finish the encryption process. " "Remember that encrypting your wallet cannot fully protect " - "your bitcoins from being stolen by malware infecting your computer.") + + "your darkcoins from being stolen by malware infecting your computer.") + "

" + tr("IMPORTANT: Any previous backups you have made of your wallet file " "should be replaced with the newly generated, encrypted wallet file. " @@ -145,8 +149,9 @@ void AskPassphraseDialog::accept() QDialog::reject(); // Cancelled } } break; + case UnlockAnonymize: case Unlock: - if(!model->setWalletLocked(false, oldpass)) + if(!model->setWalletLocked(false, oldpass, ui->anonymizationCheckBox->isChecked())) { QMessageBox::critical(this, tr("Wallet unlock failed"), tr("The passphrase entered for the wallet decryption was incorrect.")); @@ -200,6 +205,7 @@ void AskPassphraseDialog::textChanged() case Encrypt: // New passphrase x2 acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty(); break; + case UnlockAnonymize: // Old passphrase x1 case Unlock: // Old passphrase x1 case Decrypt: acceptable = !ui->passEdit1->text().isEmpty(); diff --git a/src/qt/askpassphrasedialog.h b/src/qt/askpassphrasedialog.h index 1119e0861f..f3ad4db3a7 100644 --- a/src/qt/askpassphrasedialog.h +++ b/src/qt/askpassphrasedialog.h @@ -22,6 +22,7 @@ class AskPassphraseDialog : public QDialog public: enum Mode { Encrypt, /**< Ask passphrase twice and encrypt */ + UnlockAnonymize, /**< Ask passphrase and unlock only for anonymization */ Unlock, /**< Ask passphrase and unlock */ ChangePass, /**< Ask old passphrase + new passphrase twice */ Decrypt /**< Ask passphrase and decrypt wallet */ diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index cac12658e7..4021350f1d 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -292,6 +292,9 @@ void BitcoinGUI::createActions(bool fIsTestnet) backupWalletAction->setStatusTip(tr("Backup wallet to another location")); changePassphraseAction = new QAction(QIcon(":/icons/key"), tr("&Change Passphrase..."), this); changePassphraseAction->setStatusTip(tr("Change the passphrase used for wallet encryption")); + unlockWalletAction = new QAction(tr("&Unlock Wallet..."), this); + unlockWalletAction->setToolTip(tr("Unlock wallet")); + lockWalletAction = new QAction(tr("&Lock Wallet"), this); signMessageAction = new QAction(QIcon(":/icons/edit"), tr("Sign &message..."), this); signMessageAction->setStatusTip(tr("Sign messages with your Darkcoin addresses to prove you own them")); verifyMessageAction = new QAction(QIcon(":/icons/transaction_0"), tr("&Verify message..."), this); @@ -323,6 +326,8 @@ void BitcoinGUI::createActions(bool fIsTestnet) connect(encryptWalletAction, SIGNAL(triggered(bool)), walletFrame, SLOT(encryptWallet(bool))); connect(backupWalletAction, SIGNAL(triggered()), walletFrame, SLOT(backupWallet())); connect(changePassphraseAction, SIGNAL(triggered()), walletFrame, SLOT(changePassphrase())); + connect(unlockWalletAction, SIGNAL(triggered()), walletFrame, SLOT(unlockWallet())); + connect(lockWalletAction, SIGNAL(triggered()), walletFrame, SLOT(lockWallet())); connect(signMessageAction, SIGNAL(triggered()), this, SLOT(gotoSignMessageTab())); connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(gotoVerifyMessageTab())); connect(usedSendingAddressesAction, SIGNAL(triggered()), walletFrame, SLOT(usedSendingAddresses())); @@ -362,6 +367,8 @@ void BitcoinGUI::createMenuBar() { settings->addAction(encryptWalletAction); settings->addAction(changePassphraseAction); + settings->addAction(unlockWalletAction); + settings->addAction(lockWalletAction); settings->addSeparator(); } settings->addAction(optionsAction); @@ -891,6 +898,8 @@ void BitcoinGUI::setEncryptionStatus(int status) labelEncryptionIcon->hide(); encryptWalletAction->setChecked(false); changePassphraseAction->setEnabled(false); + unlockWalletAction->setVisible(false); + lockWalletAction->setVisible(false); encryptWalletAction->setEnabled(true); break; case WalletModel::Unlocked: @@ -899,6 +908,18 @@ void BitcoinGUI::setEncryptionStatus(int status) labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently unlocked")); encryptWalletAction->setChecked(true); changePassphraseAction->setEnabled(true); + unlockWalletAction->setVisible(false); + lockWalletAction->setVisible(true); + encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported + break; + case WalletModel::UnlockedForAnonymizationOnly: + labelEncryptionIcon->show(); + labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_open").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently unlocked for anonimization only")); + encryptWalletAction->setChecked(true); + changePassphraseAction->setEnabled(true); + unlockWalletAction->setVisible(true); + lockWalletAction->setVisible(true); encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported break; case WalletModel::Locked: @@ -907,6 +928,8 @@ void BitcoinGUI::setEncryptionStatus(int status) labelEncryptionIcon->setToolTip(tr("Wallet is encrypted and currently locked")); encryptWalletAction->setChecked(true); changePassphraseAction->setEnabled(true); + unlockWalletAction->setVisible(true); + lockWalletAction->setVisible(false); encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported break; } diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index b4675b95ac..720fbff445 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -90,6 +90,8 @@ private: QAction *encryptWalletAction; QAction *backupWalletAction; QAction *changePassphraseAction; + QAction *unlockWalletAction; + QAction *lockWalletAction; QAction *aboutQtAction; QAction *openRPCConsoleAction; QAction *openAction; diff --git a/src/qt/bitcoin.cpp b/src/qt/darkcoin.cpp similarity index 99% rename from src/qt/bitcoin.cpp rename to src/qt/darkcoin.cpp index d179dd565a..f0e675a16c 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/darkcoin.cpp @@ -475,7 +475,7 @@ int main(int argc, char *argv[]) QTextCodec::setCodecForCStrings(QTextCodec::codecForTr()); #endif - Q_INIT_RESOURCE(bitcoin); + Q_INIT_RESOURCE(darkcoin); GUIUtil::SubstituteFonts(); diff --git a/src/qt/darksendconfig.cpp b/src/qt/darksendconfig.cpp new file mode 100644 index 0000000000..af6b3afa12 --- /dev/null +++ b/src/qt/darksendconfig.cpp @@ -0,0 +1,74 @@ +#include "darksendconfig.h" +#include "ui_darksendconfig.h" + +#include "guiconstants.h" +#include "walletmodel.h" +#include "init.h" + +#include +#include +#include +#include + +DarksendConfig::DarksendConfig(QWidget *parent) : + QDialog(parent), + ui(new Ui::DarksendConfig), + model(0) +{ + ui->setupUi(this); + + connect(ui->buttonBasic, SIGNAL(clicked()), this, SLOT(clickBasic())); + connect(ui->buttonHigh, SIGNAL(clicked()), this, SLOT(clickHigh())); + connect(ui->buttonMax, SIGNAL(clicked()), this, SLOT(clickMax())); +} + +DarksendConfig::~DarksendConfig() +{ + delete ui; +} + +void DarksendConfig::setModel(WalletModel *model) +{ + this->model = model; +} + +void DarksendConfig::clickBasic() +{ + configure(true, 1000, 2); + + QMessageBox::information(this, tr("Darksend Configuration"), + tr("Darksend was successfully set to basic (1000 DRK and 2 rounds). You can change this at any time by opening Darkcoin's configuration screen.")); + + close(); +} + +void DarksendConfig::clickHigh() +{ + configure(true, 1000, 4); + + QMessageBox::information(this, tr("Darksend Configuration"), + tr("Darksend was successfully set to high (1000 DRK and 4 rounds). You can change this at any time by opening Darkcoin's configuration screen.")); + + close(); +} + +void DarksendConfig::clickMax() +{ + configure(true, 1000, 8); + + QMessageBox::information(this, tr("Darksend Configuration"), + tr("Darksend was successfully set to maximum (1000 DRK and 8 rounds). You can change this at any time by opening Darkcoin's configuration screen.")); + + close(); +} + +void DarksendConfig::configure(bool enabled, int coins, int rounds) { + + QSettings settings; + + settings.setValue("nDarksendRounds", rounds); + settings.setValue("nAnonymizeDarkcoinAmount", coins); + + nDarksendRounds = rounds; + nAnonymizeDarkcoinAmount = coins; +} \ No newline at end of file diff --git a/src/qt/darksendconfig.h b/src/qt/darksendconfig.h new file mode 100644 index 0000000000..788602fb9e --- /dev/null +++ b/src/qt/darksendconfig.h @@ -0,0 +1,37 @@ +#ifndef DARKSENDCONFIG_H +#define DARKSENDCONFIG_H + +#include + +namespace Ui { + class DarksendConfig; +} +class WalletModel; + +/** Multifunctional dialog to ask for passphrases. Used for encryption, unlocking, and changing the passphrase. + */ +class DarksendConfig : public QDialog +{ + Q_OBJECT + +public: + + DarksendConfig(QWidget *parent = 0); + ~DarksendConfig(); + + void setModel(WalletModel *model); + + +private: + Ui::DarksendConfig *ui; + WalletModel *model; + void configure(bool enabled, int coins, int rounds); + +private slots: + + void clickBasic(); + void clickHigh(); + void clickMax(); +}; + +#endif // DARKSENDCONFIG_H diff --git a/src/qt/forms/askpassphrasedialog.ui b/src/qt/forms/askpassphrasedialog.ui index bc4921455f..b2c6be0fa2 100644 --- a/src/qt/forms/askpassphrasedialog.ui +++ b/src/qt/forms/askpassphrasedialog.ui @@ -99,6 +99,22 @@ + + + + true + + + Serves to disable the trivial sendmoney when OS account compromised. Provides no real security. + + + For anonymization only + + + false + + + diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 0103842e02..dc4e78b9e7 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -130,6 +130,34 @@ + + + + + + + <html><head/><body><p>This setting determines the amount of individual masternodes that an input will be anonymized through. More rounds of anonymization gives a higher degree of privacy, but also costs more in fees.</p></body></html> + + + Darksend Rounds To Use + + + + + + + 1 + + + 8 + + + 2 + + + + + diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 4cb1670c79..ffed07d075 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -741,13 +741,40 @@ 3 - - - - 0 - 0 - + + + + 75 + true + + + Qt::Vertical + + + + + + + Darksend + + + true + + + + + + + false + + + InstantX + + + + + Balance: diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 711878800a..e7e5eb92e4 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -134,6 +134,11 @@ void OptionsModel::Init() if (!SoftSetArg("-lang", settings.value("language").toString().toStdString())) addOverriddenOption("-lang"); + if (settings.contains("nDarksendRounds")) + SoftSetArg("-darksendrounds", settings.value("nDarksendRounds").toString().toStdString()); + if (settings.contains("nAnonymizeDarkcoinAmount")) + SoftSetArg("-anonymizedarkcoinamount", settings.value("nAnonymizeDarkcoinAmount").toString().toStdString()); + language = settings.value("language").toString(); } @@ -218,6 +223,10 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return settings.value("nDatabaseCache"); case ThreadsScriptVerif: return settings.value("nThreadsScriptVerif"); + case DarksendRounds: + return QVariant(nDarksendRounds); + case AnonymizeDarkcoinAmount: + return QVariant(nAnonymizeDarkcoinAmount); default: return QVariant(); } @@ -324,6 +333,16 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in setRestartRequired(true); } break; + case DarksendRounds: + nDarksendRounds = value.toInt(); + settings.setValue("nDarksendRounds", nDarksendRounds); + emit darksendRoundsChanged(nDarksendRounds); + break; + case AnonymizeDarkcoinAmount: + nAnonymizeDarkcoinAmount = value.toInt(); + settings.setValue("nAnonymizeDarkcoinAmount", nAnonymizeDarkcoinAmount); + emit anonymizeDarkcoinAmountChanged(nAnonymizeDarkcoinAmount); + break; case CoinControlFeatures: fCoinControlFeatures = value.toBool(); settings.setValue("fCoinControlFeatures", fCoinControlFeatures); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index f05e3e92de..37f1d0b7c0 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -43,6 +43,8 @@ public: DatabaseCache, // int SpendZeroConfChange, // bool OptionIDRowCount, + DarksendRounds, // int + AnonymizeDarkcoinAmount, //int }; void Init(); @@ -84,6 +86,8 @@ private: signals: void displayUnitChanged(int unit); void transactionFeeChanged(qint64); + void darksendRoundsChanged(int); + void anonymizeDarkcoinAmountChanged(int); void coinControlFeaturesChanged(bool); }; diff --git a/src/qt/res/bitcoin-qt-res.rc b/src/qt/res/bitcoin-qt-res.rc index ee23ae9b78..cbbfac1a2e 100644 --- a/src/qt/res/bitcoin-qt-res.rc +++ b/src/qt/res/bitcoin-qt-res.rc @@ -20,14 +20,14 @@ BEGIN BEGIN BLOCK "040904E4" // U.S. English - multilingual (hex) BEGIN - VALUE "CompanyName", "Bitcoin" - VALUE "FileDescription", "Bitcoin Core (OSS GUI client for Bitcoin)" + VALUE "CompanyName", "Darkcoin" + VALUE "FileDescription", "Darkcoin Core (OSS GUI client for Darkcoin)" VALUE "FileVersion", VER_FILEVERSION_STR - VALUE "InternalName", "bitcoin-qt" + VALUE "InternalName", "darkcoin-qt" VALUE "LegalCopyright", COPYRIGHT_STR VALUE "LegalTrademarks1", "Distributed under the MIT/X11 software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php." - VALUE "OriginalFilename", "bitcoin-qt.exe" - VALUE "ProductName", "Bitcoin Core" + VALUE "OriginalFilename", "darkcoin-qt.exe" + VALUE "ProductName", "Darkcoin Core" VALUE "ProductVersion", VER_PRODUCTVERSION_STR END END diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 33621e54b0..09bfafe93e 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -45,6 +45,7 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent) : connect(ui->pushButtonCoinControl, SIGNAL(clicked()), this, SLOT(coinControlButtonClicked())); connect(ui->checkBoxCoinControlChange, SIGNAL(stateChanged(int)), this, SLOT(coinControlChangeChecked(int))); connect(ui->lineEditCoinControlChange, SIGNAL(textEdited(const QString &)), this, SLOT(coinControlChangeEdited(const QString &))); + connect(ui->checkUseDarksend, SIGNAL(stateChanged ( int )), this, SLOT(updateDisplayUnit())); // Coin Control: clipboard actions QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this); @@ -89,9 +90,11 @@ void SendCoinsDialog::setModel(WalletModel *model) entry->setModel(model); } } - - setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance()); - connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64))); + } + if(model && model->getOptionsModel()) + { + setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance(), model->getAnonymizedBalance()); + connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64, qint64))); connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); // Coin Control @@ -137,6 +140,27 @@ void SendCoinsDialog::on_sendButton_clicked() return; } + QString strFunds = " Using Anonymous Funds"; + QString strFee = ""; + recipients[0].inputType = "ONLY_DENOMINATED"; + + if(ui->checkUseDarksend->isChecked()) { + recipients[0].inputType = "ONLY_DENOMINATED"; + strFunds = "Using Anonymous Funds"; + strFee = "(Darksend requires this amount to be rounded up to the nearest 0.1DRK)"; + } else { + recipients[0].inputType = "ALL_COINS"; + strFunds = "Using ANY AVAILABLE Funds"; + } + + if(ui->checkInstantX->isChecked()) { + recipients[0].useInstantX = true; + strFunds += " and InstantX"; + } else { + recipients[0].useInstantX = false; + } + + // Format confirmation message QStringList formatted; foreach(const SendCoinsRecipient &rcp, recipients) @@ -144,6 +168,8 @@ void SendCoinsDialog::on_sendButton_clicked() // generate bold amount string QString amount = "" + BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount); amount.append(""); + amount.append(strFunds); + // generate monospace address string QString address = "" + rcp.address; address.append(""); @@ -177,7 +203,7 @@ void SendCoinsDialog::on_sendButton_clicked() fNewRecipientAllowed = false; - WalletModel::UnlockContext ctx(model->requestUnlock()); + WalletModel::UnlockContext ctx(model->requestUnlock(true)); if(!ctx.isValid()) { // Unlock wallet was cancelled @@ -211,6 +237,7 @@ void SendCoinsDialog::on_sendButton_clicked() // append fee string if a fee is required questionString.append("
"); questionString.append(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee)); + questionString.append(strFee); questionString.append(" "); questionString.append(tr("added as transaction fee")); } @@ -401,20 +428,29 @@ bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv) return true; } -void SendCoinsDialog::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance) +void SendCoinsDialog::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance, qint64 anonymizedBalance) { Q_UNUSED(unconfirmedBalance); Q_UNUSED(immatureBalance); + Q_UNUSED(anonymizedBalance); if(model && model->getOptionsModel()) { - ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balance)); + uint64_t bal = 0; + + if(ui->checkUseDarksend->isChecked()) { + bal = anonymizedBalance; + } else { + bal = balance; + } + + ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), bal)); } } void SendCoinsDialog::updateDisplayUnit() { - setBalance(model->getBalance(), 0, 0); + setBalance(model->getBalance(), 0, 0, 0); } void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg) @@ -451,6 +487,11 @@ void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn msgParams.first = tr("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."); msgParams.second = CClientUIInterface::MSG_ERROR; break; + case WalletModel::AnonymizeOnlyUnlocked: + QMessageBox::warning(this, tr("Send Coins"), + tr("Error: The wallet was unlocked only to anonymize coins."), + QMessageBox::Ok, QMessageBox::Ok); + break; // included to prevent a compiler warning. case WalletModel::OK: default: diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index fcae26c720..486f25592d 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -47,7 +47,7 @@ public slots: void accept(); SendCoinsEntry *addEntry(); void updateTabsAndLabels(); - void setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance); + void setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance, qint64 anonymizedBalance); private: Ui::SendCoinsDialog *ui; diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index b3268faf8b..109944ab9a 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -123,7 +123,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked() return; } - WalletModel::UnlockContext ctx(model->requestUnlock()); + WalletModel::UnlockContext ctx(model->requestUnlock(true)); if (!ctx.isValid()) { ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); diff --git a/src/qt/test/Makefile.am b/src/qt/test/Makefile.am index 2461b5ff4d..6e7a6321fe 100644 --- a/src/qt/test/Makefile.am +++ b/src/qt/test/Makefile.am @@ -5,8 +5,8 @@ AM_CPPFLAGS += -I$(top_srcdir)/src \ -I$(top_builddir)/src/qt \ $(PROTOBUF_CFLAGS) \ $(QR_CFLAGS) -bin_PROGRAMS = test_bitcoin-qt -TESTS = test_bitcoin-qt +bin_PROGRAMS = test_darkcoin-qt +TESTS = test_darkcoin-qt TEST_QT_MOC_CPP = moc_uritests.cpp @@ -21,26 +21,26 @@ TEST_QT_H = \ BUILT_SOURCES = $(TEST_QT_MOC_CPP) -test_bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(QT_INCLUDES) $(QT_TEST_INCLUDES) +test_darkcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(QT_INCLUDES) $(QT_TEST_INCLUDES) -test_bitcoin_qt_SOURCES = \ +test_darkcoin_qt_SOURCES = \ test_main.cpp \ uritests.cpp \ $(TEST_QT_H) if ENABLE_WALLET -test_bitcoin_qt_SOURCES += \ +test_darkcoin_qt_SOURCES += \ paymentservertests.cpp endif -nodist_test_bitcoin_qt_SOURCES = $(TEST_QT_MOC_CPP) +nodist_test_darkcoin_qt_SOURCES = $(TEST_QT_MOC_CPP) -test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN_SERVER) +test_darkcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN_SERVER) if ENABLE_WALLET -test_bitcoin_qt_LDADD += $(LIBBITCOIN_WALLET) +test_darkcoin_qt_LDADD += $(LIBBITCOIN_WALLET) endif -test_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBLEVELDB) \ +test_darkcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBLEVELDB) \ $(LIBMEMENV) $(BOOST_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) $(QT_LIBS) \ $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) -test_bitcoin_qt_LDFLAGS = $(QT_LDFLAGS) +test_darkcoin_qt_LDFLAGS = $(QT_LDFLAGS) CLEANFILES = $(BUILT_SOURCES) *.gcda *.gcno diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index fead022928..e38e25de7d 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -175,6 +175,13 @@ void WalletFrame::unlockWallet() walletView->unlockWallet(); } +void WalletFrame::lockWallet() +{ + WalletView *walletView = currentWalletView(); + if (walletView) + walletView->lockWallet(); +} + void WalletFrame::usedSendingAddresses() { WalletView *walletView = currentWalletView(); diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h index f1830a0d6b..b2c077943b 100644 --- a/src/qt/walletframe.h +++ b/src/qt/walletframe.h @@ -70,6 +70,8 @@ public slots: void changePassphrase(); /** Ask for passphrase to unlock wallet temporarily */ void unlockWallet(); + /** Lock wallet */ + void lockWallet(); /** Show used sending addresses */ void usedSendingAddresses(); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 37d82ec063..ec23622c8c 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -66,6 +66,12 @@ qint64 WalletModel::getBalance(const CCoinControl *coinControl) const return wallet->GetBalance(); } + +qint64 WalletModel::getAnonymizedBalance() const +{ + return wallet->GetAnonymizedBalance(); +} + qint64 WalletModel::getUnconfirmedBalance() const { return wallet->GetUnconfirmedBalance(); @@ -108,10 +114,12 @@ void WalletModel::pollBalanceChanged() if(!lockWallet) return; - if(chainActive.Height() != cachedNumBlocks) + if(chainActive.Height() != cachedNumBlocks || nDarksendRounds != cachedDarksendRounds) { // Balance and number of transactions might have changed cachedNumBlocks = chainActive.Height(); + cachedDarksendRounds = nDarksendRounds; + cachedTxLocks = 0; checkBalanceChanged(); if(transactionTableModel) @@ -124,13 +132,15 @@ void WalletModel::checkBalanceChanged() qint64 newBalance = getBalance(); qint64 newUnconfirmedBalance = getUnconfirmedBalance(); qint64 newImmatureBalance = getImmatureBalance(); + qint64 newAnonymizedBalance = getAnonymizedBalance(); - if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance) + if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance|| cachedAnonymizedBalance != newAnonymizedBalance) { cachedBalance = newBalance; cachedUnconfirmedBalance = newUnconfirmedBalance; cachedImmatureBalance = newImmatureBalance; - emit balanceChanged(newBalance, newUnconfirmedBalance, newImmatureBalance); + cachedAnonymizedBalance = newAnonymizedBalance; + emit balanceChanged(newBalance, newUnconfirmedBalance, newImmatureBalance, newAnonymizedBalance); } } @@ -174,6 +184,11 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact return OK; } + if(isAnonymizeOnlyUnlocked()) + { + return AnonymizeOnlyUnlocked; + } + QSet setAddress; // Used to detect duplicates int nAddresses = 0; @@ -246,7 +261,18 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact CWalletTx *newTx = transaction.getTransaction(); CReserveKey *keyChange = transaction.getPossibleKeyChange(); - bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, strFailReason, coinControl); + + + AvailableCoinsType act = ONLY_DENOMINATED; + if(recipients[0].inputType == "ONLY_NONDENOMINATED"){ + act = ONLY_NONDENOMINATED; + } else if(recipients[0].inputType == "ONLY_DENOMINATED"){ + act = ONLY_DENOMINATED; + } else if(recipients[0].inputType == "ALL_COINS"){ + act = ALL_COINS; + } + + bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, strFailReason, coinControl, act); transaction.setTransactionFee(nFeeRequired); if(!fCreated) @@ -268,11 +294,17 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran { QByteArray transaction_array; /* store serialized transaction */ + if(isAnonymizeOnlyUnlocked()) + { + return AnonymizeOnlyUnlocked; + } + { LOCK2(cs_main, wallet->cs_wallet); CWalletTx *newTx = transaction.getTransaction(); // Store PaymentRequests in wtx.vOrderForm in wallet. + std::string strCommand = "tx"; foreach(const SendCoinsRecipient &rcp, transaction.getRecipients()) { if (rcp.paymentRequest.IsInitialized()) @@ -283,11 +315,20 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran newTx->vOrderForm.push_back(make_pair(key, value)); } else if (!rcp.message.isEmpty()) // Message from normal bitcoin:URI (bitcoin:123...?message=example) + { newTx->vOrderForm.push_back(make_pair("Message", rcp.message.toStdString())); + } + + if(rcp.useInstantX) { + strCommand = "txlreq"; + } } CReserveKey *keyChange = transaction.getPossibleKeyChange(); - if(!wallet->CommitTransaction(*newTx, *keyChange)) + + transaction.getRecipients(); + + if(!wallet->CommitTransaction(*newTx, *keyChange, strCommand)) return TransactionCommitFailed; CTransaction* t = (CTransaction*)newTx; @@ -358,6 +399,10 @@ WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const { return Locked; } + else if (wallet->fWalletUnlockAnonymizeOnly) + { + return UnlockedForAnonymizationOnly; + } else { return Unlocked; @@ -378,7 +423,7 @@ bool WalletModel::setWalletEncrypted(bool encrypted, const SecureString &passphr } } -bool WalletModel::setWalletLocked(bool locked, const SecureString &passPhrase) +bool WalletModel::setWalletLocked(bool locked, const SecureString &passPhrase, bool anonymizeOnly) { if(locked) { @@ -388,10 +433,15 @@ bool WalletModel::setWalletLocked(bool locked, const SecureString &passPhrase) else { // Unlock - return wallet->Unlock(passPhrase); + return wallet->Unlock(passPhrase, anonymizeOnly); } } +bool WalletModel::isAnonymizeOnlyUnlocked() +{ + return wallet->fWalletUnlockAnonymizeOnly; +} + bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureString &newPass) { bool retval; @@ -489,9 +539,16 @@ void WalletModel::unsubscribeFromCoreSignals() } // WalletModel::UnlockContext implementation -WalletModel::UnlockContext WalletModel::requestUnlock() +WalletModel::UnlockContext WalletModel::requestUnlock(bool relock) { bool was_locked = getEncryptionStatus() == Locked; + + if (!was_locked && isAnonymizeOnlyUnlocked()) + { + setWalletLocked(true); + was_locked = getEncryptionStatus() == Locked; + } + if(was_locked) { // Request UI to unlock wallet @@ -500,7 +557,8 @@ WalletModel::UnlockContext WalletModel::requestUnlock() // If wallet is still locked, unlock was failed or cancelled, mark context as invalid bool valid = getEncryptionStatus() != Locked; - return UnlockContext(this, valid, was_locked); + return UnlockContext(this, valid, relock); +// return UnlockContext(this, valid, was_locked && !isAnonymizeOnlyUnlocked()); } WalletModel::UnlockContext::UnlockContext(WalletModel *wallet, bool valid, bool relock): diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index ccf590aaed..356769a714 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -48,6 +48,8 @@ public: QString address; QString label; qint64 amount; + std::string inputType; + bool useInstantX; // If from a payment request, this is used for storing the memo QString message; @@ -110,14 +112,16 @@ public: AmountWithFeeExceedsBalance, DuplicateAddress, TransactionCreationFailed, // Error returned when wallet is still locked - TransactionCommitFailed + TransactionCommitFailed, + AnonymizeOnlyUnlocked }; enum EncryptionStatus { Unencrypted, // !wallet->IsCrypted() Locked, // wallet->IsCrypted() && wallet->IsLocked() - Unlocked // wallet->IsCrypted() && !wallet->IsLocked() + Unlocked, // wallet->IsCrypted() && !wallet->IsLocked() + UnlockedForAnonymizationOnly // wallet->IsCrypted() && !wallet->IsLocked() && wallet->fWalletUnlockAnonymizeOnly }; OptionsModel *getOptionsModel(); @@ -126,6 +130,7 @@ public: RecentRequestsTableModel *getRecentRequestsTableModel(); qint64 getBalance(const CCoinControl *coinControl = NULL) const; + qint64 getAnonymizedBalance() const; qint64 getUnconfirmedBalance() const; qint64 getImmatureBalance() const; int getNumTransactions() const; @@ -151,8 +156,10 @@ public: // Wallet encryption bool setWalletEncrypted(bool encrypted, const SecureString &passphrase); // Passphrase only needed when unlocking - bool setWalletLocked(bool locked, const SecureString &passPhrase=SecureString()); + bool setWalletLocked(bool locked, const SecureString &passPhrase=SecureString(), bool anonymizeOnly=false); bool changePassphrase(const SecureString &oldPass, const SecureString &newPass); + // Is wallet unlocked for anonymization only? + bool isAnonymizeOnlyUnlocked(); // Wallet backup bool backupWallet(const QString &filename); @@ -176,7 +183,7 @@ public: void CopyFrom(const UnlockContext& rhs); }; - UnlockContext requestUnlock(); + UnlockContext requestUnlock(bool relock); bool getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; void getOutputs(const std::vector& vOutpoints, std::vector& vOutputs); @@ -206,7 +213,10 @@ private: qint64 cachedBalance; qint64 cachedUnconfirmedBalance; qint64 cachedImmatureBalance; + qint64 cachedAnonymizedBalance; qint64 cachedNumTransactions; + int cachedTxLocks; + int cachedDarksendRounds; EncryptionStatus cachedEncryptionStatus; int cachedNumBlocks; @@ -218,7 +228,7 @@ private: signals: // Signal that balance in wallet changed - void balanceChanged(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance); + void balanceChanged(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance, qint64 anonymizedBalance); // Number of transactions in wallet changed void numTransactionsChanged(int count); diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 1cef48344f..04c9000c60 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -254,14 +254,23 @@ void WalletView::unlockWallet() if(!walletModel) return; // Unlock wallet when requested by wallet model - if (walletModel->getEncryptionStatus() == WalletModel::Locked) + + if (walletModel->getEncryptionStatus() == WalletModel::Locked || walletModel->getEncryptionStatus() == WalletModel::UnlockedForAnonymizationOnly) { - AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, this); + AskPassphraseDialog dlg(AskPassphraseDialog::UnlockAnonymize, this); dlg.setModel(walletModel); dlg.exec(); } } +void WalletView::lockWallet() +{ + if(!walletModel) + return; + + walletModel->setWalletLocked(true); +} + void WalletView::usedSendingAddresses() { if(!walletModel) diff --git a/src/qt/walletview.h b/src/qt/walletview.h index 9cfa8d6760..87c583e9c9 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -91,6 +91,8 @@ public slots: void changePassphrase(); /** Ask for passphrase to unlock wallet temporarily */ void unlockWallet(); + /** Lock wallet */ + void lockWallet(); /** Show used sending addresses */ void usedSendingAddresses(); diff --git a/src/rpcdarksend.cpp b/src/rpcdarksend.cpp new file mode 100644 index 0000000000..c3e3857e8f --- /dev/null +++ b/src/rpcdarksend.cpp @@ -0,0 +1,323 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "main.h" +#include "core.h" +#include "db.h" +#include "init.h" +#include "masternode.h" +#include "activemasternode.h" +#include "rpcserver.h" +#include + +#include +using namespace json_spirit; +using namespace std; + + + +Value darksend(const Array& params, bool fHelp) +{ + if (fHelp || params.size() == 0) + throw runtime_error( + "darksend \n" + "darkcoinaddress, reset, or auto (AutoDenominate)" + " is a real and is rounded to the nearest 0.00000001" + + HelpRequiringPassphrase()); + + if(fMasterNode) + return "DarkSend is not supported from masternodes"; + + if (pwalletMain->IsLocked()) + throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); + + if(params[0].get_str() == "auto"){ + darkSendPool.DoAutomaticDenominating(); + return "DoAutomaticDenominating"; + } + + if(params[0].get_str() == "reset"){ + darkSendPool.SetNull(true); + darkSendPool.UnlockCoins(); + return "successfully reset darksend"; + } + + if (params.size() != 2) + throw runtime_error( + "darksend \n" + "darkcoinaddress, denominate, or auto (AutoDenominate)" + " is a real and is rounded to the nearest 0.00000001" + + HelpRequiringPassphrase()); + + CBitcoinAddress address(params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid DarkCoin address"); + + // Amount + int64_t nAmount = AmountFromValue(params[1]); + + // Wallet comments + CWalletTx wtx; + string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx, ONLY_DENOMINATED); + if (strError != "") + throw JSONRPCError(RPC_WALLET_ERROR, strError); + + return wtx.GetHash().GetHex(); +} + + +Value getpoolinfo(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getpoolinfo\n" + "Returns an object containing anonymous pool-related information."); + + Object obj; + obj.push_back(Pair("current_masternode", GetCurrentMasterNode())); + obj.push_back(Pair("state", darkSendPool.GetState())); + obj.push_back(Pair("entries", darkSendPool.GetEntriesCount())); + obj.push_back(Pair("entries_accepted", darkSendPool.GetCountEntriesAccepted())); + return obj; +} + +Value masternode(const Array& params, bool fHelp) +{ + string strCommand; + if (params.size() >= 1) + strCommand = params[0].get_str(); + + if (fHelp || + (strCommand != "start" && strCommand != "start-many" && strCommand != "stop" && strCommand != "list" && strCommand != "count" && strCommand != "enforce" + && strCommand != "debug" && strCommand != "current" && strCommand != "winners" && strCommand != "genkey" && strCommand != "connect")) + throw runtime_error( + "masternode passphrase\n"); + + if (strCommand == "stop") + { + if(!fMasterNode) return "you must set masternode=1 in the configuration"; + + if(pwalletMain->IsLocked()) { + SecureString strWalletPass; + strWalletPass.reserve(100); + + if (params.size() == 2){ + strWalletPass = params[1].get_str().c_str(); + } else { + throw runtime_error( + "Your wallet is locked, passphrase is required\n"); + } + + if(!pwalletMain->Unlock(strWalletPass)){ + return "incorrect passphrase"; + } + } + + activeMasternode.RegisterAsMasterNode(true); + pwalletMain->Lock(); + + if(activeMasternode.isCapableMasterNode == MASTERNODE_STOPPED) return "successfully stopped masternode"; + if(activeMasternode.isCapableMasterNode == MASTERNODE_NOT_CAPABLE) return "not capable masternode"; + + return "unknown"; + } + + if (strCommand == "list") + { + std::string strCommand = "active"; + + if (params.size() == 2){ + strCommand = params[1].get_str().c_str(); + } + + if (strCommand != "active" && strCommand != "vin" && strCommand != "pubkey" && strCommand != "lastseen" && strCommand != "activeseconds" && strCommand != "rank" && strCommand != "protocol"){ + throw runtime_error( + "list supports 'active', 'vin', 'pubkey', 'lastseen', 'activeseconds', 'rank', 'protocol'\n"); + } + + Object obj; + BOOST_FOREACH(CMasterNode mn, darkSendMasterNodes) { + mn.Check(); + + if(strCommand == "active"){ + obj.push_back(Pair(mn.addr.ToString().c_str(), (int)mn.IsEnabled())); + } else if (strCommand == "vin") { + obj.push_back(Pair(mn.addr.ToString().c_str(), mn.vin.prevout.hash.ToString().c_str())); + } else if (strCommand == "pubkey") { + CScript pubkey; + pubkey.SetDestination(mn.pubkey.GetID()); + CTxDestination address1; + ExtractDestination(pubkey, address1); + CBitcoinAddress address2(address1); + + obj.push_back(Pair(mn.addr.ToString().c_str(), address2.ToString().c_str())); + } else if (strCommand == "protocol") { + obj.push_back(Pair(mn.addr.ToString().c_str(), (int64_t)mn.protocolVersion)); + } else if (strCommand == "lastseen") { + obj.push_back(Pair(mn.addr.ToString().c_str(), (int64_t)mn.lastTimeSeen)); + } else if (strCommand == "activeseconds") { + obj.push_back(Pair(mn.addr.ToString().c_str(), (int64_t)(mn.lastTimeSeen - mn.now))); + } else if (strCommand == "rank") { + obj.push_back(Pair(mn.addr.ToString().c_str(), (int)(GetMasternodeRank(mn.vin, chainActive.Tip()->nHeight)))); + } + } + return obj; + } + if (strCommand == "count") return (int)darkSendMasterNodes.size(); + + if (strCommand == "start") + { + if(!fMasterNode) return "you must set masternode=1 in the configuration"; + + if(pwalletMain->IsLocked()) { + SecureString strWalletPass; + strWalletPass.reserve(100); + + if (params.size() == 2){ + strWalletPass = params[1].get_str().c_str(); + } else { + throw runtime_error( + "Your wallet is locked, passphrase is required\n"); + } + + if(!pwalletMain->Unlock(strWalletPass)){ + return "incorrect passphrase"; + } + } + + activeMasternode.RegisterAsMasterNode(false); + pwalletMain->Lock(); + + if(activeMasternode.isCapableMasterNode == MASTERNODE_REMOTELY_ENABLED) return "masternode started remotely"; + if(activeMasternode.isCapableMasterNode == MASTERNODE_INPUT_TOO_NEW) return "masternode input must have at least 15 confirmations"; + if(activeMasternode.isCapableMasterNode == MASTERNODE_STOPPED) return "masternode is stopped"; + if(activeMasternode.isCapableMasterNode == MASTERNODE_IS_CAPABLE) return "successfully started masternode"; + if(activeMasternode.masternodePortOpen == MASTERNODE_PORT_NOT_OPEN) return "inbound port is not open. Please open it and try again. (19999 for testnet and 9999 for mainnet)"; + if(activeMasternode.isCapableMasterNode == MASTERNODE_NOT_CAPABLE) return "not capable masternode"; + if(activeMasternode.isCapableMasterNode == MASTERNODE_SYNC_IN_PROCESS) return "sync in process. Must wait until client is synced to start."; + + return "unknown"; + } + + if (strCommand == "start-many") + { + boost::filesystem::path pathDebug = GetDataDir() / "masternode.conf"; + std::ifstream infile(pathDebug.string().c_str()); + + std::string line; + int total = 0; + int successful = 0; + int fail = 0; + while (std::getline(infile, line)) + { + std::istringstream iss(line); + std::string a, b; + if (!(iss >> a >> b)) { break; } // error + + total++; + if(activeMasternode.RegisterAsMasterNodeRemoteOnly(a, b)){ + successful++; + } else { + fail++; + } + } + + printf(" Successfully started %d masternodes, failed to start %d, total %d\n", successful, fail, total); + return ""; + + } + + if (strCommand == "debug") + { + if(activeMasternode.isCapableMasterNode == MASTERNODE_REMOTELY_ENABLED) return "masternode started remotely"; + if(activeMasternode.isCapableMasterNode == MASTERNODE_INPUT_TOO_NEW) return "masternode input must have at least 15 confirmations"; + if(activeMasternode.isCapableMasterNode == MASTERNODE_IS_CAPABLE) return "successfully started masternode"; + if(activeMasternode.isCapableMasterNode == MASTERNODE_STOPPED) return "masternode is stopped"; + if(activeMasternode.masternodePortOpen == MASTERNODE_PORT_NOT_OPEN) return "inbound port is not open. Please open it and try again. (19999 for testnet and 9999 for mainnet)"; + if(activeMasternode.isCapableMasterNode == MASTERNODE_NOT_CAPABLE) return "not capable masternode"; + if(activeMasternode.isCapableMasterNode == MASTERNODE_SYNC_IN_PROCESS) return "sync in process. Must wait until client is synced to start."; + + CTxIn vin = CTxIn(); + CPubKey pubkey = CScript(); + CKey key; + bool found = activeMasternode.GetMasterNodeVin(vin, pubkey, key); + if(!found){ + return "Missing masternode input, please look at the documentation for instructions on masternode creation"; + } else { + return "No problems were found"; + } + } + + if (strCommand == "create") + { + + return "Not implemented yet, please look at the documentation for instructions on masternode creation"; + } + + if (strCommand == "current") + { + int winner = GetCurrentMasterNode(1); + if(winner >= 0) { + return darkSendMasterNodes[winner].addr.ToString().c_str(); + } + + return "unknown"; + } + + if (strCommand == "genkey") + { + CKey secret; + secret.MakeNewKey(false); + + return CBitcoinSecret(secret).ToString(); + } + + if (strCommand == "winners") + { + Object obj; + + for(int nHeight = chainActive.Tip()->nHeight-10; nHeight < chainActive.Tip()->nHeight+20; nHeight++) + { + CScript payee; + if(masternodePayments.GetBlockPayee(nHeight, payee)){ + CTxDestination address1; + ExtractDestination(payee, address1); + CBitcoinAddress address2(address1); + obj.push_back(Pair(boost::lexical_cast(nHeight), address2.ToString().c_str())); + } else { + obj.push_back(Pair(boost::lexical_cast(nHeight), "")); + } + } + + return obj; + } + + if(strCommand == "enforce") + { + return (uint64_t)enforceMasternodePaymentsTime; + } + + if(strCommand == "connect") + { + std::string strAddress = ""; + if (params.size() == 2){ + strAddress = params[1].get_str().c_str(); + } else { + throw runtime_error( + "Masternode address required\n"); + } + + CService addr = CService(strAddress); + + if(ConnectNode((CAddress)addr, NULL, true)){ + return "successfully connected"; + } else { + return "error connecting"; + } + } + + return Value::null; +} + diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 3534253adc..556bed4c3c 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -331,7 +331,7 @@ Value getwork(const Array& params, bool fHelp) // Clear pindexPrev so future getworks make a new block, despite any failures from here on pindexPrev = NULL; - // Store the pindexBest used before CreateNewBlock, to avoid races + // Store the chainActive.Tip() used before CreateNewBlock, to avoid races nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); CBlockIndex* pindexPrevNew = chainActive.Tip(); nStart = GetTime(); @@ -498,7 +498,7 @@ Value getblocktemplate(const Array& params, bool fHelp) // Clear pindexPrev so future calls make a new block, despite any failures from here on pindexPrev = NULL; - // Store the pindexBest used before CreateNewBlock, to avoid races + // Store the chainActive.Tip() used before CreateNewBlock, to avoid races nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); CBlockIndex* pindexPrevNew = chainActive.Tip(); nStart = GetTime(); diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 53e8fcfc4e..319b385df5 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -269,6 +269,10 @@ static const CRPCCommand vRPCCommands[] = { "validateaddress", &validateaddress, true, false, false }, /* uses wallet if enabled */ { "verifymessage", &verifymessage, false, false, false }, + /* Darkcoin features */ + { "darksend", &darksend, false, false, true }, + { "masternode", &masternode, false, false, true }, + #ifdef ENABLE_WALLET /* Wallet */ { "addmultisigaddress", &addmultisigaddress, false, false, true }, @@ -315,6 +319,7 @@ static const CRPCCommand vRPCCommands[] = { "gethashespersec", &gethashespersec, true, false, false }, { "getwork", &getwork, true, false, true }, { "setgenerate", &setgenerate, true, true, false }, + #endif // ENABLE_WALLET }; @@ -508,7 +513,7 @@ void StartRPCThreads() { unsigned char rand_pwd[32]; RAND_bytes(rand_pwd, 32); - string strWhatAmI = "To use bitcoind"; + string strWhatAmI = "To use darkcoind"; if (mapArgs.count("-server")) strWhatAmI = strprintf(_("To use the %s option"), "\"-server\""); else if (mapArgs.count("-daemon")) @@ -517,13 +522,13 @@ void StartRPCThreads() _("%s, you must set a rpcpassword in the configuration file:\n" "%s\n" "It is recommended you use the following random password:\n" - "rpcuser=bitcoinrpc\n" + "rpcuser=darkcoinrpc\n" "rpcpassword=%s\n" "(you do not need to remember this password)\n" "The username and password MUST NOT be the same.\n" "If the file does not exist, create it with owner-readable-only file permissions.\n" "It is also recommended to set alertnotify so you are notified of problems;\n" - "for example: alertnotify=echo %%s | mail -s \"Bitcoin Alert\" admin@foo.com\n"), + "for example: alertnotify=echo %%s | mail -s \"Darkcoin Alert\" admin@foo.com\n"), strWhatAmI, GetConfigFile().string(), EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32)), @@ -889,7 +894,7 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s } std::string HelpExampleCli(string methodname, string args){ - return "> bitcoin-cli " + methodname + " " + args + "\n"; + return "> darkcoin-cli " + methodname + " " + args + "\n"; } std::string HelpExampleRpc(string methodname, string args){ diff --git a/src/rpcserver.h b/src/rpcserver.h index 1092c691be..aa2f0037a0 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -63,7 +63,7 @@ public: }; /** - * Bitcoin RPC command dispatcher. + * Darkcoin RPC command dispatcher. */ class CRPCTable { @@ -188,4 +188,9 @@ extern json_spirit::Value gettxoutsetinfo(const json_spirit::Array& params, bool extern json_spirit::Value gettxout(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value verifychain(const json_spirit::Array& params, bool fHelp); + +extern json_spirit::Value darksend(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value masternode(const json_spirit::Array& params, bool fHelp); + + #endif diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index e3b35dbb04..7879f94d77 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -1,5 +1,6 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers +// Copyright (c) 2014 The Darkcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -76,13 +77,13 @@ Value getnewaddress(const Array& params, bool fHelp) if (fHelp || params.size() > 1) throw runtime_error( "getnewaddress ( \"account\" )\n" - "\nReturns a new Bitcoin address for receiving payments.\n" + "\nReturns a new Darkcoin address for receiving payments.\n" "If 'account' is specified (recommended), it is added to the address book \n" "so payments received with the address will be credited to 'account'.\n" "\nArguments:\n" "1. \"account\" (string, optional) The account name for the address to be linked to. if not provided, the default account \"\" is used. It can also be set to the empty string \"\" to represent the default account. The account does not need to exist, it will be created if there is no account by the given name.\n" "\nResult:\n" - "\"bitcoinaddress\" (string) The new bitcoin address\n" + "\"darkcoinaddress\" (string) The new darkcoin address\n" "\nExamples:\n" + HelpExampleCli("getnewaddress", "") + HelpExampleCli("getnewaddress", "\"\"") @@ -153,11 +154,11 @@ Value getaccountaddress(const Array& params, bool fHelp) if (fHelp || params.size() != 1) throw runtime_error( "getaccountaddress \"account\"\n" - "\nReturns the current Bitcoin address for receiving payments to this account.\n" + "\nReturns the current Darkcoin address for receiving payments to this account.\n" "\nArguments:\n" "1. \"account\" (string, required) The account name for the address. It can also be set to the empty string \"\" to represent the default account. The account does not need to exist, it will be created and a new address created if there is no account by the given name.\n" "\nResult:\n" - "\"bitcoinaddress\" (string) The account bitcoin address\n" + "\"darkcoinaddress\" (string) The account darkcoin address\n" "\nExamples:\n" + HelpExampleCli("getaccountaddress", "") + HelpExampleCli("getaccountaddress", "\"\"") @@ -181,7 +182,7 @@ Value getrawchangeaddress(const Array& params, bool fHelp) if (fHelp || params.size() > 1) throw runtime_error( "getrawchangeaddress\n" - "\nReturns a new Bitcoin address, for receiving change.\n" + "\nReturns a new Darkcoin address, for receiving change.\n" "This is for use with raw transactions, NOT normal use.\n" "\nResult:\n" "\"address\" (string) The address\n" @@ -210,19 +211,19 @@ Value setaccount(const Array& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( - "setaccount \"bitcoinaddress\" \"account\"\n" + "setaccount \"darkcoinaddress\" \"account\"\n" "\nSets the account associated with the given address.\n" "\nArguments:\n" - "1. \"bitcoinaddress\" (string, required) The bitcoin address to be associated with an account.\n" + "1. \"darkcoinaddress\" (string, required) The darkcoin address to be associated with an account.\n" "2. \"account\" (string, required) The account to assign the address to.\n" "\nExamples:\n" - + HelpExampleCli("setaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"tabby\"") - + HelpExampleRpc("setaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", \"tabby\"") + + HelpExampleCli("setaccount", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\" \"tabby\"") + + HelpExampleRpc("setaccount", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\", \"tabby\"") ); CBitcoinAddress address(params[0].get_str()); if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Darkcoin address"); string strAccount; @@ -247,20 +248,20 @@ Value getaccount(const Array& params, bool fHelp) { if (fHelp || params.size() != 1) throw runtime_error( - "getaccount \"bitcoinaddress\"\n" + "getaccount \"darkcoinaddress\"\n" "\nReturns the account associated with the given address.\n" "\nArguments:\n" - "1. \"bitcoinaddress\" (string, required) The bitcoin address for account lookup.\n" + "1. \"darkcoinaddress\" (string, required) The darkcoin address for account lookup.\n" "\nResult:\n" "\"accountname\" (string) the account address\n" "\nExamples:\n" - + HelpExampleCli("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\"") - + HelpExampleRpc("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\"") + + HelpExampleCli("getaccount", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\"") + + HelpExampleRpc("getaccount", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\"") ); CBitcoinAddress address(params[0].get_str()); if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Darkcoin address"); string strAccount; map::iterator mi = pwalletMain->mapAddressBook.find(address.Get()); @@ -280,7 +281,7 @@ Value getaddressesbyaccount(const Array& params, bool fHelp) "1. \"account\" (string, required) The account name.\n" "\nResult:\n" "[ (json array of string)\n" - " \"bitcoinaddress\" (string) a bitcoin address associated with the given account\n" + " \"darkcoinaddress\" (string) a darkcoin address associated with the given account\n" " ,...\n" "]\n" "\nExamples:\n" @@ -306,11 +307,11 @@ Value sendtoaddress(const Array& params, bool fHelp) { if (fHelp || params.size() < 2 || params.size() > 4) throw runtime_error( - "sendtoaddress \"bitcoinaddress\" amount ( \"comment\" \"comment-to\" )\n" + "sendtoaddress \"darkcoinaddress\" amount ( \"comment\" \"comment-to\" )\n" "\nSent an amount to a given address. The amount is a real and is rounded to the nearest 0.00000001\n" + HelpRequiringPassphrase() + "\nArguments:\n" - "1. \"bitcoinaddress\" (string, required) The bitcoin address to send to.\n" + "1. \"darkcoinaddress\" (string, required) The darkcoin address to send to.\n" "2. \"amount\" (numeric, required) The amount in btc to send. eg 0.1\n" "3. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" " This is not part of the transaction, just kept in your wallet.\n" @@ -327,7 +328,7 @@ Value sendtoaddress(const Array& params, bool fHelp) CBitcoinAddress address(params[0].get_str()); if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Darkcoin address"); // Amount int64_t nAmount = AmountFromValue(params[1]); @@ -360,7 +361,7 @@ Value listaddressgroupings(const Array& params, bool fHelp) "[\n" " [\n" " [\n" - " \"bitcoinaddress\", (string) The bitcoin address\n" + " \"darkcoinaddress\", (string) The darkcoin address\n" " amount, (numeric) The amount in btc\n" " \"account\" (string, optional) The account\n" " ]\n" @@ -399,11 +400,11 @@ Value signmessage(const Array& params, bool fHelp) { if (fHelp || params.size() != 2) throw runtime_error( - "signmessage \"bitcoinaddress\" \"message\"\n" + "signmessage \"darkcoinaddress\" \"message\"\n" "\nSign a message with the private key of an address" + HelpRequiringPassphrase() + "\n" "\nArguments:\n" - "1. \"bitcoinaddress\" (string, required) The bitcoin address to use for the private key.\n" + "1. \"darkcoinaddress\" (string, required) The darkcoin address to use for the private key.\n" "2. \"message\" (string, required) The message to create a signature of.\n" "\nResult:\n" "\"signature\" (string) The signature of the message encoded in base 64\n" @@ -411,11 +412,11 @@ Value signmessage(const Array& params, bool fHelp) "\nUnlock the wallet for 30 seconds\n" + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + "\nCreate the signature\n" - + HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"my message\"") + + + HelpExampleCli("signmessage", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\" \"my message\"") + "\nVerify the signature\n" - + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"signature\" \"my message\"") + + + HelpExampleCli("verifymessage", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\" \"signature\" \"my message\"") + "\nAs json rpc\n" - + HelpExampleRpc("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", \"my message\"") + + HelpExampleRpc("signmessage", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\", \"my message\"") ); EnsureWalletIsUnlocked(); @@ -450,29 +451,29 @@ Value getreceivedbyaddress(const Array& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( - "getreceivedbyaddress \"bitcoinaddress\" ( minconf )\n" - "\nReturns the total amount received by the given bitcoinaddress in transactions with at least minconf confirmations.\n" + "getreceivedbyaddress \"darkcoinaddress\" ( minconf )\n" + "\nReturns the total amount received by the given darkcoinaddress in transactions with at least minconf confirmations.\n" "\nArguments:\n" - "1. \"bitcoinaddress\" (string, required) The bitcoin address for transactions.\n" + "1. \"darkcoinaddress\" (string, required) The darkcoin address for transactions.\n" "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" "\nResult:\n" "amount (numeric) The total amount in btc received at this address.\n" "\nExamples:\n" "\nThe amount from transactions with at least 1 confirmation\n" - + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\"") + + + HelpExampleCli("getreceivedbyaddress", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\"") + "\nThe amount including unconfirmed transactions, zero confirmations\n" - + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" 0") + + + HelpExampleCli("getreceivedbyaddress", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\" 0") + "\nThe amount with at least 6 confirmation, very safe\n" - + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" 6") + + + HelpExampleCli("getreceivedbyaddress", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\" 6") + "\nAs a json rpc call\n" - + HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", 6") + + HelpExampleRpc("getreceivedbyaddress", "\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\", 6") ); - // Bitcoin address + // Darkcoin address CBitcoinAddress address = CBitcoinAddress(params[0].get_str()); CScript scriptPubKey; if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Darkcoin address"); scriptPubKey.SetDestination(address.Get()); if (!IsMine(*pwalletMain,scriptPubKey)) return (double)0.0; @@ -732,13 +733,13 @@ Value sendfrom(const Array& params, bool fHelp) { if (fHelp || params.size() < 3 || params.size() > 6) throw runtime_error( - "sendfrom \"fromaccount\" \"tobitcoinaddress\" amount ( minconf \"comment\" \"comment-to\" )\n" - "\nSent an amount from an account to a bitcoin address.\n" + "sendfrom \"fromaccount\" \"todarkcoinaddress\" amount ( minconf \"comment\" \"comment-to\" )\n" + "\nSent an amount from an account to a darkcoin address.\n" "The amount is a real and is rounded to the nearest 0.00000001." + HelpRequiringPassphrase() + "\n" "\nArguments:\n" "1. \"fromaccount\" (string, required) The name of the account to send funds from. May be the default account using \"\".\n" - "2. \"tobitcoinaddress\" (string, required) The bitcoin address to send funds to.\n" + "2. \"todarkcoinaddress\" (string, required) The darkcoin address to send funds to.\n" "3. amount (numeric, required) The amount in btc. (transaction fee is added on top).\n" "4. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n" "5. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" @@ -760,7 +761,7 @@ Value sendfrom(const Array& params, bool fHelp) string strAccount = AccountFromValue(params[0]); CBitcoinAddress address(params[1].get_str()); if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Darkcoin address"); int64_t nAmount = AmountFromValue(params[2]); int nMinDepth = 1; if (params.size() > 3) @@ -800,7 +801,7 @@ Value sendmany(const Array& params, bool fHelp) "1. \"fromaccount\" (string, required) The account to send the funds from, can be \"\" for the default account\n" "2. \"amounts\" (string, required) A json object with addresses and amounts\n" " {\n" - " \"address\":amount (numeric) The bitcoin address is the key, the numeric amount in btc is the value\n" + " \"address\":amount (numeric) The darkcoin address is the key, the numeric amount in btc is the value\n" " ,...\n" " }\n" "3. minconf (numeric, optional, default=1) Only use the balance confirmed at least this many times.\n" @@ -810,11 +811,11 @@ Value sendmany(const Array& params, bool fHelp) " the number of addresses.\n" "\nExamples:\n" "\nSend two amounts to two different addresses:\n" - + HelpExampleCli("sendmany", "\"tabby\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"") + + + HelpExampleCli("sendmany", "\"tabby\" \"{\\\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"") + "\nSend two amounts to two different addresses setting the confirmation and comment:\n" - + HelpExampleCli("sendmany", "\"tabby\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 6 \"testing\"") + + + HelpExampleCli("sendmany", "\"tabby\" \"{\\\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 6 \"testing\"") + "\nAs a json rpc call\n" - + HelpExampleRpc("sendmany", "\"tabby\", \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\", 6, \"testing\"") + + HelpExampleRpc("sendmany", "\"tabby\", \"{\\\"XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\", 6, \"testing\"") ); string strAccount = AccountFromValue(params[0]); @@ -836,7 +837,7 @@ Value sendmany(const Array& params, bool fHelp) { CBitcoinAddress address(s.name_); if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+s.name_); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Darkcoin address: ")+s.name_); if (setAddress.count(address)) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_); @@ -879,26 +880,26 @@ Value addmultisigaddress(const Array& params, bool fHelp) { string msg = "addmultisigaddress nrequired [\"key\",...] ( \"account\" )\n" "\nAdd a nrequired-to-sign multisignature address to the wallet.\n" - "Each key is a Bitcoin address or hex-encoded public key.\n" + "Each key is a Darkcoin address or hex-encoded public key.\n" "If 'account' is specified, assign address to that account.\n" "\nArguments:\n" "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n" - "2. \"keysobject\" (string, required) A json array of bitcoin addresses or hex-encoded public keys\n" + "2. \"keysobject\" (string, required) A json array of darkcoin addresses or hex-encoded public keys\n" " [\n" - " \"address\" (string) bitcoin address or hex-encoded public key\n" + " \"address\" (string) darkcoin address or hex-encoded public key\n" " ...,\n" " ]\n" "3. \"account\" (string, optional) An account to assign the addresses to.\n" "\nResult:\n" - "\"bitcoinaddress\" (string) A bitcoin address associated with the keys.\n" + "\"darkcoinaddress\" (string) A darkcoin address associated with the keys.\n" "\nExamples:\n" "\nAdd a multisig address from 2 addresses\n" - + HelpExampleCli("addmultisigaddress", "2 \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + + + HelpExampleCli("addmultisigaddress", "2 \"[\\\"Xt4qk9uKvQYAonVGSZNXqxeDmtjaEWgfrs\\\",\\\"XoSoWQkpgLpppPoyyzbUFh1fq2RBvW6UK1\\\"]\"") + "\nAs json rpc call\n" - + HelpExampleRpc("addmultisigaddress", "2, \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + + HelpExampleRpc("addmultisigaddress", "2, \"[\\\"Xt4qk9uKvQYAonVGSZNXqxeDmtjaEWgfrs\\\",\\\"XoSoWQkpgLpppPoyyzbUFh1fq2RBvW6UK1\\\"]\"") ; throw runtime_error(msg); } @@ -1192,7 +1193,7 @@ Value listtransactions(const Array& params, bool fHelp) " {\n" " \"account\":\"accountname\", (string) The account name associated with the transaction. \n" " It will be \"\" for the default account.\n" - " \"address\":\"bitcoinaddress\", (string) The bitcoin address of the transaction. Not present for \n" + " \"address\":\"darkcoinaddress\", (string) The darkcoin address of the transaction. Not present for \n" " move transactions (category = move).\n" " \"category\":\"send|receive|move\", (string) The transaction category. 'move' is a local (off blockchain)\n" " transaction between accounts, and not associated with an address,\n" @@ -1365,7 +1366,7 @@ Value listsinceblock(const Array& params, bool fHelp) "{\n" " \"transactions\": [\n" " \"account\":\"accountname\", (string) The account name associated with the transaction. Will be \"\" for the default account.\n" - " \"address\":\"bitcoinaddress\", (string) The bitcoin address of the transaction. Not present for move transactions (category = move).\n" + " \"address\":\"darkcoinaddress\", (string) The darkcoin address of the transaction. Not present for move transactions (category = move).\n" " \"category\":\"send|receive\", (string) The transaction category. 'send' has negative amounts, 'receive' has positive amounts.\n" " \"amount\": x.xxx, (numeric) The amount in btc. This is negative for the 'send' category, and for the 'move' category for moves \n" " outbound. It is positive for the 'receive' category, and for the 'move' category for inbound funds.\n" @@ -1452,7 +1453,7 @@ Value gettransaction(const Array& params, bool fHelp) " \"details\" : [\n" " {\n" " \"account\" : \"accountname\", (string) The account name involved in the transaction, can be \"\" for the default account.\n" - " \"address\" : \"bitcoinaddress\", (string) The bitcoin address involved in the transaction\n" + " \"address\" : \"darkcoinaddress\", (string) The darkcoin address involved in the transaction\n" " \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n" " \"amount\" : x.xxx (numeric) The amount in btc\n" " }\n" @@ -1560,11 +1561,11 @@ static void LockWallet(CWallet* pWallet) Value walletpassphrase(const Array& params, bool fHelp) { - if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) + if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3)) throw runtime_error( - "walletpassphrase \"passphrase\" timeout\n" + "walletpassphrase \"passphrase\" \n" "\nStores the wallet decryption key in memory for 'timeout' seconds.\n" - "This is needed prior to performing transactions related to private keys such as sending bitcoins\n" + "This is needed prior to performing transactions related to private keys such as sending darkcoins\n" "\nArguments:\n" "1. \"passphrase\" (string, required) The wallet passphrase\n" "2. timeout (numeric, required) The time to keep the decryption key in seconds.\n" @@ -1577,7 +1578,8 @@ Value walletpassphrase(const Array& params, bool fHelp) "\nLock the wallet again (before 60 seconds)\n" + HelpExampleCli("walletlock", "") + "\nAs json rpc call\n" - + HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60") + + HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60") + + "if [anonymizeonly] is true sending functions are disabled." ); if (fHelp) @@ -1594,12 +1596,21 @@ Value walletpassphrase(const Array& params, bool fHelp) if (strWalletPass.length() > 0) { - if (!pwalletMain->Unlock(strWalletPass)) + bool anonymizeOnly; + if (params.size() == 3) + anonymizeOnly = params[2].get_bool(); + else + anonymizeOnly = false; + + if (!pwalletMain->IsLocked() && pwalletMain->fWalletUnlockAnonymizeOnly && anonymizeOnly) + throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked."); + + if (!pwalletMain->Unlock(strWalletPass, anonymizeOnly)) throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); } else throw runtime_error( - "walletpassphrase \n" + "walletpassphrase \n" "Stores the wallet decryption key in memory for seconds."); pwalletMain->TopUpKeyPool(); @@ -1704,10 +1715,10 @@ Value encryptwallet(const Array& params, bool fHelp) "\nExamples:\n" "\nEncrypt you wallet\n" + HelpExampleCli("encryptwallet", "\"my pass phrase\"") + - "\nNow set the passphrase to use the wallet, such as for signing or sending bitcoin\n" + "\nNow set the passphrase to use the wallet, such as for signing or sending darkcoin\n" + HelpExampleCli("walletpassphrase", "\"my pass phrase\"") + "\nNow we can so something like sign\n" - + HelpExampleCli("signmessage", "\"bitcoinaddress\" \"test message\"") + + + HelpExampleCli("signmessage", "\"darkcoinaddress\" \"test message\"") + "\nNow lock the wallet again by removing the passphrase\n" + HelpExampleCli("walletlock", "") + "\nAs a json rpc call\n" @@ -1737,7 +1748,7 @@ Value encryptwallet(const Array& params, bool fHelp) // slack space in .dat files; that is bad if the old data is // unencrypted private keys. So: StartShutdown(); - return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup."; + return "wallet encrypted; darkcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup."; } Value lockunspent(const Array& params, bool fHelp) @@ -1747,7 +1758,7 @@ Value lockunspent(const Array& params, bool fHelp) "lockunspent unlock [{\"txid\":\"txid\",\"vout\":n},...]\n" "\nUpdates list of temporarily unspendable outputs.\n" "Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n" - "A locked transaction output will not be chosen by automatic coin selection, when spending bitcoins.\n" + "A locked transaction output will not be chosen by automatic coin selection, when spending darkcoins.\n" "Locks are stored in memory only. Nodes start with zero locked outputs, and the locked output list\n" "is always cleared (by virtue of process exit) when a node stops or fails.\n" "Also see the listunspent call\n" @@ -1896,7 +1907,7 @@ Value getwalletinfo(const Array& params, bool fHelp) "\nResult:\n" "{\n" " \"walletversion\": xxxxx, (numeric) the wallet version\n" - " \"balance\": xxxxxxx, (numeric) the total bitcoin balance of the wallet\n" + " \"balance\": xxxxxxx, (numeric) the total darkcoin balance of the wallet\n" " \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n" " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n" " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" diff --git a/src/script.cpp b/src/script.cpp index 81d2754453..4a2af4cd2d 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -2057,3 +2057,28 @@ bool CScriptCompressor::Decompress(unsigned int nSize, const std::vectorsize() != 25) return false; + + std::string str; + opcodetype opcode; + const_iterator pc = begin(); + int i = 0; + while (pc < end()) + { + GetOp(pc, opcode); + + if( i == 0 && opcode != OP_DUP) return false; + else if(i == 1 && opcode != OP_HASH160) return false; + else if(i == 3 && opcode != OP_EQUALVERIFY) return false; + else if(i == 4 && opcode != OP_CHECKSIG) return false; + else if(i == 5) return false; + + i++; + } + + return true; +} \ No newline at end of file diff --git a/src/script.h b/src/script.h index 1742ce81f8..9e96cb4c1c 100644 --- a/src/script.h +++ b/src/script.h @@ -641,7 +641,7 @@ public: return nFound; } - // Pre-version-0.6, Bitcoin always counted CHECKMULTISIGs + // Pre-version-0.6, Darkcoin always counted CHECKMULTISIGs // as 20 sigops. With pay-to-script-hash, that changed: // CHECKMULTISIGs serialized in scriptSigs are // counted more accurately, assuming they are of the form @@ -652,6 +652,7 @@ public: // pay-to-script-hash transactions: unsigned int GetSigOpCount(const CScript& scriptSig) const; + bool IsNormalPaymentScript() const; bool IsPayToScriptHash() const; // Called by IsStandardTx and P2SH VerifyScript (which makes it consensus-critical). diff --git a/src/test/Makefile.am b/src/test/Makefile.am index e12f4904f1..39ef47a31e 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -2,9 +2,9 @@ include $(top_srcdir)/src/Makefile.include AM_CPPFLAGS += -I$(top_srcdir)/src -bin_PROGRAMS = test_bitcoin +bin_PROGRAMS = test_darkcoin -TESTS = test_bitcoin +TESTS = test_darkcoin JSON_TEST_FILES = \ data/script_valid.json \ @@ -22,16 +22,16 @@ RAW_TEST_FILES = data/alertTests.raw BUILT_SOURCES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h) -# test_bitcoin binary # -test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(TESTDEFS) -test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBLEVELDB) $(LIBMEMENV) \ +# test_darkcoin binary # +test_darkcoin_CPPFLAGS = $(AM_CPPFLAGS) $(TESTDEFS) +test_darkcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBLEVELDB) $(LIBMEMENV) \ $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) if ENABLE_WALLET -test_bitcoin_LDADD += $(LIBBITCOIN_WALLET) +test_darkcoin_LDADD += $(LIBBITCOIN_WALLET) endif -test_bitcoin_LDADD += $(BDB_LIBS) +test_darkcoin_LDADD += $(BDB_LIBS) -test_bitcoin_SOURCES = \ +test_darkcoin_SOURCES = \ alert_tests.cpp \ allocator_tests.cpp \ base32_tests.cpp \ @@ -57,7 +57,7 @@ test_bitcoin_SOURCES = \ script_tests.cpp \ serialize_tests.cpp \ sigopcount_tests.cpp \ - test_bitcoin.cpp \ + test_darkcoin.cpp \ transaction_tests.cpp \ uint256_tests.cpp \ util_tests.cpp \ @@ -66,12 +66,12 @@ test_bitcoin_SOURCES = \ $(JSON_TEST_FILES) $(RAW_TEST_FILES) if ENABLE_WALLET -test_bitcoin_SOURCES += \ +test_darkcoin_SOURCES += \ accounting_tests.cpp \ wallet_tests.cpp \ rpc_wallet_tests.cpp endif -nodist_test_bitcoin_SOURCES = $(BUILT_SOURCES) +nodist_test_darkcoin_SOURCES = $(BUILT_SOURCES) CLEANFILES = *.gcda *.gcno $(BUILT_SOURCES) diff --git a/src/test/test_bitcoin.cpp b/src/test/test_darkcoin.cpp similarity index 98% rename from src/test/test_bitcoin.cpp rename to src/test/test_darkcoin.cpp index 2d993e24db..a0137aac9f 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_darkcoin.cpp @@ -10,6 +10,7 @@ #include "txdb.h" #include "ui_interface.h" #include "util.h" +#include "activemasternode.h" #ifdef ENABLE_WALLET #include "db.h" #include "wallet.h" diff --git a/src/uint256.h b/src/uint256.h index a3beb8b5e8..32f7f2eccd 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -368,6 +368,11 @@ public: return sizeof(pn); } + uint64_t Get64(int n=0) const + { + return pn[2*n] | (uint64_t)pn[2*n+1] << 32; + } + uint64_t GetLow64() const { assert(WIDTH >= 2); diff --git a/src/util.cpp b/src/util.cpp index 599b533d45..676348f56b 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -87,6 +87,22 @@ namespace boost { using namespace std; +//Darkcoin only features +bool fMasterNode = false; +string strMasterNodePrivKey = ""; +string strMasterNodeAddr = ""; +int nInstantXDepth = 1; +int nDarksendRounds = 2; +int nAnonymizeDarkcoinAmount = 1000; +int nLiquidityProvider = 0; +/** Spork enforcement enabled time */ +int64_t enforceMasternodePaymentsTime = 4085657524; +int nMasternodeMinProtocol = 0; +bool fSucessfullyLoaded = false; +bool fEnableDarksend = false; +/** All denominations used by darksend */ +std::vector darkSendDenominations; + map mapArgs; map > mapMultiArgs; bool fDebug = false; diff --git a/src/util.h b/src/util.h index 2b9bb44816..47aefddb79 100644 --- a/src/util.h +++ b/src/util.h @@ -92,7 +92,20 @@ inline void MilliSleep(int64_t n) #endif } +//Darkcoin only features +extern bool fMasterNode; +extern int nInstantXDepth; +extern int nDarksendRounds; +extern int nAnonymizeDarkcoinAmount; +extern int nLiquidityProvider; +extern bool fEnableDarksend; +extern int64_t enforceMasternodePaymentsTime; +extern std::string strMasterNodeAddr; +extern int nMasternodeMinProtocol; +extern int keysLoaded; +extern bool fSucessfullyLoaded; +extern std::vector darkSendDenominations; extern std::map mapArgs; extern std::map > mapMultiArgs; diff --git a/src/version.h b/src/version.h index 08712a20af..1f7dbf9e68 100644 --- a/src/version.h +++ b/src/version.h @@ -27,13 +27,13 @@ extern const std::string CLIENT_DATE; // network protocol versioning // -static const int PROTOCOL_VERSION = 71000; +static const int PROTOCOL_VERSION = 70052; // intial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; // disconnect from peers older than this proto version -static const int MIN_PEER_PROTO_VERSION = 70043; +static const int MIN_PEER_PROTO_VERSION = 70046; // nTime field added to CAddress, starting with this version; // if possible, avoid requesting addresses nodes older than this diff --git a/src/wallet.cpp b/src/wallet.cpp index 30637731e8..df4819f992 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -145,8 +145,14 @@ bool CWallet::LoadCScript(const CScript& redeemScript) return CCryptoKeyStore::AddCScript(redeemScript); } -bool CWallet::Unlock(const SecureString& strWalletPassphrase) +bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool anonymizeOnly) { + if (!IsLocked()) + { + fWalletUnlockAnonymizeOnly = anonymizeOnly; + return true; + } + CCrypter crypter; CKeyingMaterial vMasterKey; @@ -159,7 +165,10 @@ bool CWallet::Unlock(const SecureString& strWalletPassphrase) if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) continue; // try another master key if (CCryptoKeyStore::Unlock(vMasterKey)) + { + fWalletUnlockAnonymizeOnly = anonymizeOnly; return true; + } } } return false; @@ -683,6 +692,26 @@ int64_t CWallet::GetDebit(const CTxIn &txin) const return 0; } +int64_t CWallet::IsDenominated(const CTxIn &txin) const +{ + { + LOCK(cs_wallet); + map::const_iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (txin.prevout.n < prev.vout.size()){ + BOOST_FOREACH(int64_t d, darkSendDenominations){ + if(prev.vout[txin.prevout.n].nValue == d) { + return true; + } + } + } + } + } + return 0; +} + bool CWallet::IsChange(const CTxOut& txout) const { CTxDestination address; @@ -905,14 +934,19 @@ void CWallet::ReacceptWalletTransactions() } } -void CWalletTx::RelayWalletTransaction() +void CWalletTx::RelayWalletTransaction(std::string strCommand) { if (!IsCoinBase()) { if (GetDepthInMainChain() == 0) { uint256 hash = GetHash(); LogPrintf("Relaying wtx %s\n", hash.ToString()); - RelayTransaction((CTransaction)*this, hash); + + if(strCommand == "txlreq"){ + RelayTransactionLockReq((CTransaction)*this, hash, true); + } else { + RelayTransaction((CTransaction)*this, hash); + } } } } @@ -994,6 +1028,95 @@ int64_t CWallet::GetBalance() const return nTotal; } +int64_t CWallet::GetAnonymizedBalance() const +{ + int64_t nTotal = 0; + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsTrusted()){ + for (unsigned int i = 0; i < pcoin->vout.size(); i++) { + + COutput out = COutput(pcoin, i, pcoin->GetDepthInMainChain()); + CTxIn vin = CTxIn(out.tx->GetHash(), out.i); + + if(IsSpent(out.tx->GetHash(), i) || !IsMine(pcoin->vout[i]) || !IsDenominated(vin)) continue; + + int rounds = GetInputDarksendRounds(vin); + if(rounds >= nDarksendRounds){ + nTotal += pcoin->vout[i].nValue; + } + } + } + } + } + + return nTotal; +} + +double CWallet::GetAverageAnonymizedRounds() const +{ + double fTotal = 0; + double fCount = 0; + + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + for (unsigned int i = 0; i < pcoin->vout.size(); i++) { + + COutput out = COutput(pcoin, i, pcoin->GetDepthInMainChain()); + CTxIn vin = CTxIn(out.tx->GetHash(), out.i); + + if(IsSpent(out.tx->GetHash(), i) || !IsMine(pcoin->vout[i]) || !IsDenominated(vin)) continue; + + int rounds = GetInputDarksendRounds(vin); + fTotal += (float)rounds; + fCount += 1; + } + } + } + + if(fCount == 0) return 0; + + return fTotal/fCount; +} + +int64_t CWallet::GetDenominatedBalance(bool onlyDenom, bool onlyUnconfirmed) const +{ + int64_t nTotal = 0; + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + + bool isDenom = false; + for (unsigned int i = 0; i < pcoin->vout.size(); i++) + BOOST_FOREACH(int64_t d, darkSendDenominations) + if(pcoin->vout[i].nValue == d) + isDenom = true; + + if(onlyUnconfirmed){ + if (!pcoin->IsTrusted()){ + if(onlyDenom == isDenom){ + nTotal += pcoin->GetAvailableCredit(); + } + } + } else if (pcoin->IsTrusted()) { + if(onlyDenom == isDenom) { + nTotal += pcoin->GetAvailableCredit(); + } + } + } + } + + return nTotal; +} + int64_t CWallet::GetUnconfirmedBalance() const { int64_t nTotal = 0; @@ -1024,7 +1147,7 @@ int64_t CWallet::GetImmatureBalance() const } // populate vCoins with vector of spendable COutputs -void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl) const +void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, AvailableCoinsType coin_type) const { vCoins.clear(); @@ -1049,6 +1172,25 @@ void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const continue; for (unsigned int i = 0; i < pcoin->vout.size(); i++) { + bool found = false; + if(coin_type == ONLY_DENOMINATED) { + //should make this a vector + + COutput out = COutput(pcoin, i, pcoin->GetDepthInMainChain()); + CTxIn vin = CTxIn(out.tx->GetHash(), out.i); + int rounds = GetInputDarksendRounds(vin); + if(rounds >= nDarksendRounds) found = true; + } else if(coin_type == ONLY_NONDENOMINATED) { + found = true; + BOOST_FOREACH(int64_t d, darkSendDenominations) + if(pcoin->vout[i].nValue == d) + found = false; + + } else { + found = true; + } + if(!found) continue; + if (!(IsSpent(wtxid, i)) && IsMine(pcoin->vout[i]) && !IsLockedCoin((*it).first, i) && pcoin->vout[i].nValue > 0 && (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i))) @@ -1104,6 +1246,26 @@ static void ApproximateBestSubset(vector vCoins; + AvailableCoins(vCoins, true, coinControl, ALL_COINS); + + BOOST_FOREACH(const COutput& out, vCoins) + { + if(out.tx->vout[out.i].nValue == 1000*COIN){ //exactly + vin = CTxIn(out.tx->GetHash(),out.i); + pubScript = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey + nValueRet = out.tx->vout[out.i].nValue; + return true; + } + } + + return false; +} + bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, int nConfMine, int nConfTheirs, vector vCoins, set >& setCoinsRet, int64_t& nValueRet) const { @@ -1202,11 +1364,34 @@ bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, int nConfMine, int nConfT return true; } -bool CWallet::SelectCoins(int64_t nTargetValue, set >& setCoinsRet, int64_t& nValueRet, const CCoinControl* coinControl) const +bool CWallet::SelectCoins(int64_t nTargetValue, set >& setCoinsRet, int64_t& nValueRet, const CCoinControl* coinControl, AvailableCoinsType coin_type) const { vector vCoins; AvailableCoins(vCoins, true, coinControl); + //if we're doing only denominated, we need to round up to the nearest .1DRK + if(coin_type == ONLY_DENOMINATED){ + // denominate our funds + std::vector vOut; + + // Make outputs by looping through denominations, from large to small + BOOST_FOREACH(int64_t v, darkSendDenominations) + { + int added = 0; + BOOST_FOREACH(const COutput& out, vCoins) + { + if(out.tx->vout[out.i].nValue == v //make sure it's the denom we're looking for + && nValueRet + out.tx->vout[out.i].nValue < nTargetValue + (0.1*COIN)+1 //round the amount up to .1DRK over + && added <= 50){ //don't add more than 50 of one denom type + nValueRet += out.tx->vout[out.i].nValue; + setCoinsRet.insert(make_pair(out.tx, out.i)); + added++; + } + } + } + return (nValueRet >= nTargetValue); + } + // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) if (coinControl && coinControl->HasSelected()) { @@ -1223,11 +1408,227 @@ bool CWallet::SelectCoins(int64_t nTargetValue, set t2.Priority(); + } +}; +bool CWallet::SelectCoinsDark(int64_t nValueMin, int64_t nValueMax, std::vector& setCoinsRet, int64_t& nValueRet, int nDarksendRoundsMin, int nDarksendRoundsMax, bool& hasFeeInput) const +{ + CCoinControl *coinControl=NULL; + vector vCoins; + AvailableCoins(vCoins, false, coinControl, ALL_COINS); + + set > setCoinsRet2; + + //order the array so fees are first, then denominated money, then the rest. + sort(vCoins.rbegin(), vCoins.rend(), CompareByPriority()); + + //the first thing we get is a fee input, then we'll use as many denominated as possible. then the rest + BOOST_FOREACH(const COutput& out, vCoins) + { + //there's no reason to allow inputs less than 1 COIN into DS (other than denominations smaller than that amount) + if(out.tx->vout[out.i].nValue <= 1*COIN && out.tx->vout[out.i].nValue != (.1*COIN)+1) continue; + if(fMasterNode && out.tx->vout[out.i].nValue == 1000*COIN) continue; //masternode input + + if(nValueRet + out.tx->vout[out.i].nValue <= nValueMax){ + CTxIn vin = CTxIn(out.tx->GetHash(),out.i); + + int rounds = GetInputDarksendRounds(vin); + if(rounds >= nDarksendRoundsMax) continue; + if(rounds < nDarksendRoundsMin) continue; + + vin.prevPubKey = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey + nValueRet += out.tx->vout[out.i].nValue; + setCoinsRet.push_back(vin); + setCoinsRet2.insert(make_pair(out.tx, out.i)); + } + } + + // if it's more than min, we're good to return + if(nValueRet >= nValueMin) return true; + + return false; +} + +bool CWallet::SelectCoinsCollateral(std::vector& setCoinsRet, int64_t& nValueRet) const +{ + CCoinControl *coinControl=NULL; + + vector vCoins; + + //printf(" selecting coins for collateral\n"); + AvailableCoins(vCoins, false, coinControl, ALL_COINS); + + //printf("found coins %d\n", (int)vCoins.size()); + + set > setCoinsRet2; + + BOOST_FOREACH(const COutput& out, vCoins) + { + // collateral inputs will always be a multiple of DARSEND_COLLATERAL, up to five + if( + out.tx->vout[out.i].nValue == (DARKSEND_COLLATERAL * 5)+DARKSEND_FEE || + out.tx->vout[out.i].nValue == (DARKSEND_COLLATERAL * 4)+DARKSEND_FEE || + out.tx->vout[out.i].nValue == (DARKSEND_COLLATERAL * 3)+DARKSEND_FEE || + out.tx->vout[out.i].nValue == (DARKSEND_COLLATERAL * 2)+DARKSEND_FEE || + out.tx->vout[out.i].nValue == (DARKSEND_COLLATERAL * 1)+DARKSEND_FEE + ){ + CTxIn vin = CTxIn(out.tx->GetHash(),out.i); + + vin.prevPubKey = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey + nValueRet += out.tx->vout[out.i].nValue; + setCoinsRet.push_back(vin); + setCoinsRet2.insert(make_pair(out.tx, out.i)); + return true; + } + } + + return false; +} + +int CWallet::CountInputsWithAmount(int64_t nInputAmount) +{ + int64_t nTotal = 0; + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsTrusted()){ + for (unsigned int i = 0; i < pcoin->vout.size(); i++) { + COutput out = COutput(pcoin, i, pcoin->GetDepthInMainChain()); + CTxIn vin = CTxIn(out.tx->GetHash(), out.i); + + if(out.tx->vout[out.i].nValue != nInputAmount) continue; + + if(IsSpent(out.tx->GetHash(), i) || !IsMine(pcoin->vout[i]) || !IsDenominated(vin)) continue; + + int rounds = GetInputDarksendRounds(vin); + if(rounds >= nDarksendRounds){ + nTotal++; + } + } + } + } + } + + return nTotal; +} + +bool CWallet::HasDarksendFeeInputs() const +{ + CCoinControl *coinControl=NULL; + + vector vCoins; + AvailableCoins(vCoins, false, coinControl, ALL_COINS); + + bool found_collateral = false; + BOOST_FOREACH(const COutput& out, vCoins) + { + if( + out.tx->vout[out.i].nValue == (DARKSEND_COLLATERAL * 5)+DARKSEND_FEE || + out.tx->vout[out.i].nValue == (DARKSEND_COLLATERAL * 4)+DARKSEND_FEE || + out.tx->vout[out.i].nValue == (DARKSEND_COLLATERAL * 3)+DARKSEND_FEE || + out.tx->vout[out.i].nValue == (DARKSEND_COLLATERAL * 2)+DARKSEND_FEE || + out.tx->vout[out.i].nValue == (DARKSEND_COLLATERAL * 1)+DARKSEND_FEE + ) found_collateral = true; + + } + + return found_collateral; +} + +bool CWallet::SelectCoinsWithoutDenomination(int64_t nTargetValue, set >& setCoinsRet, int64_t& nValueRet) const +{ + CCoinControl *coinControl=NULL; + + vector vCoins; + AvailableCoins(vCoins, true, coinControl, ONLY_NONDENOMINATED); + + BOOST_FOREACH(const COutput& out, vCoins) + { + nValueRet += out.tx->vout[out.i].nValue; + setCoinsRet.insert(make_pair(out.tx, out.i)); + } + return (nValueRet >= nTargetValue); +} + +bool CWallet::CreateCollateralTransaction(CTransaction& txCollateral, std::string strReason) +{ + /* + To doublespend a collateral transaction, it will require a fee higher than this. So there's + still a significant cost. + */ + int64_t nFeeRet = 0.001*COIN; + + txCollateral.vin.clear(); + txCollateral.vout.clear(); + + CReserveKey reservekey(this); + int64_t nValueIn2 = 0; + std::vector vCoinsCollateral; + + if (!SelectCoinsCollateral(vCoinsCollateral, nValueIn2)) + { + strReason = "Error: Darksend requires a collateral transaction and could not locate an acceptable input!"; + return false; + } + + // make our change address + CScript scriptChange; + CPubKey vchPubKey; + assert(reservekey.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked + scriptChange.SetDestination(vchPubKey.GetID()); + reservekey.KeepKey(); + + BOOST_FOREACH(CTxIn v, vCoinsCollateral) + txCollateral.vin.push_back(v); + + if(nValueIn2 - DARKSEND_COLLATERAL - nFeeRet > 0) { + //pay collateral charge in fees + CTxOut vout3 = CTxOut(nValueIn2 - DARKSEND_COLLATERAL, scriptChange); + txCollateral.vout.push_back(vout3); + } + + int vinNumber = 0; + BOOST_FOREACH(CTxIn v, txCollateral.vin) { + if(!SignSignature(*this, v.prevPubKey, txCollateral, vinNumber, int(SIGHASH_ALL|SIGHASH_ANYONECANPAY))) { + BOOST_FOREACH(CTxIn v, vCoinsCollateral) + UnlockCoin(v.prevout); + + strReason = "CDarkSendPool::Sign - Unable to sign collateral transaction! \n"; + return false; + } + vinNumber++; + } + + return true; +} + +bool CWallet::ConvertList(std::vector vCoins, std::vector& vecAmounts) +{ + BOOST_FOREACH(CTxIn i, vCoins){ + if (mapWallet.count(i.prevout.hash)) + { + CWalletTx& wtx = mapWallet[i.prevout.hash]; + if(i.prevout.n < wtx.vout.size()){ + vecAmounts.push_back(wtx.vout[i.prevout.n].nValue); + } + } else { + LogPrintf("ConvertList -- Couldn't find transaction\n"); + } + } + return true; +} bool CWallet::CreateTransaction(const vector >& vecSend, - CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl) + CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, std::string& strFailReason, const CCoinControl* coinControl, AvailableCoinsType coin_type) { int64_t nValue = 0; BOOST_FOREACH (const PAIRTYPE(CScript, int64_t)& s, vecSend) @@ -1274,11 +1675,19 @@ bool CWallet::CreateTransaction(const vector >& vecSend, // Choose coins to use set > setCoins; int64_t nValueIn = 0; - if (!SelectCoins(nTotalValue, setCoins, nValueIn, coinControl)) + if (!SelectCoins(nTotalValue, setCoins, nValueIn, coinControl, coin_type)) { - strFailReason = _("Insufficient funds"); + if(coin_type == ALL_COINS) + strFailReason = _("Insufficient funds"); + else if (coin_type == ONLY_NONDENOMINATED) + strFailReason = _("Unable to locate enough Darksend non-denominated funds for this transaction"); + else + strFailReason = _("Unable to locate enough Darksend denominated funds for this transaction"); + return false; } + + BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { int64_t nCredit = pcoin.first->vout[pcoin.second].nValue; @@ -1399,7 +1808,7 @@ bool CWallet::CreateTransaction(CScript scriptPubKey, int64_t nValue, } // Call after CreateTransaction unless you want to abort -bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) +bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, std::string strCommand) { { LOCK2(cs_main, cs_wallet); @@ -1440,7 +1849,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) LogPrintf("CommitTransaction() : Error: Transaction not valid"); return false; } - wtxNew.RelayWalletTransaction(); + wtxNew.RelayWalletTransaction(strCommand); } return true; } @@ -1448,19 +1857,31 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) -string CWallet::SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew) +string CWallet::SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, AvailableCoinsType coin_type) { CReserveKey reservekey(this); int64_t nFeeRequired; + CCoinControl *coinControl=NULL; if (IsLocked()) { string strError = _("Error: Wallet locked, unable to create transaction!"); - LogPrintf("SendMoney() : %s", strError); + LogPrintf("SendMoney() : %s\n", strError.c_str()); return strError; } - string strError; - if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired, strError)) + if (fWalletUnlockAnonymizeOnly) + { + string strError = _("Error: Wallet unlocked for anonymization only, unable to create transaction."); + LogPrintf("SendMoney() : %s\n", strError.c_str()); + return strError; + } + + CWalletTx wtx; + std::vector > vecSend; + vecSend.push_back(make_pair(scriptPubKey, nValue)); + string strError = ""; + + if (!CreateTransaction(vecSend, wtxNew, reservekey, nFeeRequired, strError, coinControl, coin_type)) { if (nValue + nFeeRequired > 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)); @@ -1476,7 +1897,7 @@ string CWallet::SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNe -string CWallet::SendMoneyToDestination(const CTxDestination& address, int64_t nValue, CWalletTx& wtxNew) +string CWallet::SendMoneyToDestination(const CTxDestination& address, int64_t nValue, CWalletTx& wtxNew, AvailableCoinsType coin_type) { // Check amount if (nValue <= 0) @@ -1488,11 +1909,141 @@ string CWallet::SendMoneyToDestination(const CTxDestination& address, int64_t nV CScript scriptPubKey; scriptPubKey.SetDestination(address); - return SendMoney(scriptPubKey, nValue, wtxNew); + return SendMoney(scriptPubKey, nValue, wtxNew, coin_type); } +int64_t CWallet::GetTotalValue(std::vector vCoins) { + int64_t nTotalValue = 0; + CWalletTx wtx; + BOOST_FOREACH(CTxIn i, vCoins){ + if (mapWallet.count(i.prevout.hash)) + { + CWalletTx& wtx = mapWallet[i.prevout.hash]; + if(i.prevout.n < wtx.vout.size()){ + nTotalValue += wtx.vout[i.prevout.n].nValue; + } + } else { + LogPrintf("GetTotalValue -- Couldn't find transaction\n"); + } + } + return nTotalValue; +} +string CWallet::PrepareDarksendDenominate(int minRounds, int maxRounds, int64_t maxAmount) +{ + if (IsLocked()) + return _("Error: Wallet locked, unable to create transaction!"); + if(darkSendPool.GetState() != POOL_STATUS_ERROR && darkSendPool.GetState() != POOL_STATUS_SUCCESS){ + if(darkSendPool.GetMyTransactionCount() > 0){ + return _("Error: You already have pending entries in the Darksend pool"); + } + } + + // ** find the coins we'll use + std::vector vCoins; + int64_t nValueIn = 0; + CReserveKey reservekey(this); + + //make sure we + bool hasFeeInput = false; + + //select coins we'll use + if (!SelectCoinsDark(1*COIN, maxAmount, vCoins, nValueIn, minRounds, maxRounds, hasFeeInput)) + { + vCoins.clear(); + + return _("Insufficient funds"); + } + + // calculate total value out + int64_t nTotalValue = GetTotalValue(vCoins); + + LogPrintf("PrepareDarksendDenominate - preparing darksend denominate . Asked for: %d, Got: %d \n", maxAmount, nTotalValue); + + //-------------- + + BOOST_FOREACH(CTxIn v, vCoins) + LockCoin(v.prevout); + + // denominate our funds + int64_t nValueLeft = nTotalValue; + std::vector vOut; + + //if minRounds >= 0, we want to use our exact denominations. + if(minRounds >= 0){ + CWalletTx wtx; + BOOST_FOREACH(CTxIn i, vCoins){ + if (mapWallet.count(i.prevout.hash)) + { + CWalletTx& wtx = mapWallet[i.prevout.hash]; + if(i.prevout.n < wtx.vout.size()){ + CScript scriptChange; + CPubKey vchPubKey; + //use a unique change address + assert(reservekey.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked + scriptChange.SetDestination(vchPubKey.GetID()); + reservekey.KeepKey(); + + CTxOut o(wtx.vout[i.prevout.n].nValue, scriptChange); + vOut.push_back(o); + + //increment outputs and subtract denomination amount + nValueLeft -= wtx.vout[i.prevout.n].nValue; + } + } else { + LogPrintf("GetTotalValue -- Couldn't find transaction\n"); + } + } + + } else { + // Make outputs by looping through denominations, from small to large + BOOST_REVERSE_FOREACH(int64_t v, darkSendDenominations){ + int nOutputs = 0; + + if(std::find(darkSendPool.vecDisabledDenominations.begin(), + darkSendPool.vecDisabledDenominations.end(), v) != + darkSendPool.vecDisabledDenominations.end()){ + continue; + } + + // add each output up to 10 times until it can't be added again + while(nValueLeft - v >= 0 && nOutputs <= 10) { + CScript scriptChange; + CPubKey vchPubKey; + //use a unique change address + assert(reservekey.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked + scriptChange.SetDestination(vchPubKey.GetID()); + reservekey.KeepKey(); + + CTxOut o(v, scriptChange); + vOut.push_back(o); + + //increment outputs and subtract denomination amount + nOutputs++; + nValueLeft -= v; + } + + if(nValueLeft == 0) break; + } + } + + // if we have anything left over, send it back as change + if(nValueLeft > 0){ + CScript scriptChange; + CPubKey vchPubKey; + assert(reservekey.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked + scriptChange.SetDestination(vchPubKey.GetID()); + reservekey.KeepKey(); + + CTxOut o(nValueLeft, scriptChange); + vOut.push_back(o); + } + + darkSendPool.SendDarksendDenominate(vCoins, vOut, nValueIn); + + return ""; +} DBErrors CWallet::LoadWallet(bool& fFirstRunRet) { @@ -1591,6 +2142,20 @@ bool CWallet::DelAddressBook(const CTxDestination& address) return CWalletDB(strWalletFile).EraseName(CBitcoinAddress(address).ToString()); } +bool CWallet::GetTransaction(const uint256 &hashTx, CWalletTx& wtx) +{ + { + LOCK(cs_wallet); + map::iterator mi = mapWallet.find(hashTx); + if (mi != mapWallet.end()) + { + wtx = (*mi).second; + return true; + } + } + return false; +} + bool CWallet::SetDefaultKey(const CPubKey &vchPubKey) { if (fFileBacked) diff --git a/src/wallet.h b/src/wallet.h index 1ec4b3c0b6..3391a9e8e3 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -7,6 +7,7 @@ #define BITCOIN_WALLET_H #include "core.h" +#include "darksend.h" #include "crypter.h" #include "key.h" #include "keystore.h" @@ -51,6 +52,13 @@ enum WalletFeature FEATURE_LATEST = 61000 }; +enum AvailableCoinsType +{ + ALL_COINS = 1, + ONLY_DENOMINATED = 2, + ONLY_NONDENOMINATED = 3 +}; + /** A key pool entry */ class CKeyPool @@ -101,7 +109,6 @@ public: class CWallet : public CCryptoKeyStore, public CWalletInterface { private: - bool SelectCoins(int64_t nTargetValue, std::set >& setCoinsRet, int64_t& nValueRet, const CCoinControl *coinControl = NULL) const; CWalletDB *pwalletdbEncryption; @@ -125,6 +132,17 @@ private: void SyncMetaData(std::pair); public: + bool SelectCoins(int64_t nTargetValue, std::set >& setCoinsRet, int64_t& nValueRet, const CCoinControl *coinControl = NULL, AvailableCoinsType coin_type=ALL_COINS) const; + bool SelectCoinsDark(int64_t nValueMin, int64_t nValueMax, std::vector& setCoinsRet, int64_t& nValueRet, int nDarksendRoundsMin, int nDarksendRoundsMax, bool& hasFeeInput) const; + bool SelectCoinsDarkDenominated(int64_t nTargetValue, std::vector& setCoinsRet, int64_t& nValueRet) const; + bool SelectCoinsMasternode(CTxIn& vin, int64_t& nValueRet, CScript& pubScript) const; + bool HasDarksendFeeInputs() const; + int CountInputsWithAmount(int64_t nInputAmount); + + bool SelectCoinsCollateral(std::vector& setCoinsRet, int64_t& nValueRet) const ; + bool SelectCoinsWithoutDenomination(int64_t nTargetValue, std::set >& setCoinsRet, int64_t& nValueRet) const; + bool GetTransaction(const uint256 &hashTx, CWalletTx& wtx); + /// Main wallet lock. /// This lock protects all the fields added by CWallet /// except for: @@ -133,6 +151,7 @@ public: mutable CCriticalSection cs_wallet; bool fFileBacked; + bool fWalletUnlockAnonymizeOnly; std::string strWalletFile; std::set setKeyPool; @@ -164,6 +183,7 @@ public: nNextResend = 0; nLastResend = 0; nTimeFirstKey = 0; + fWalletUnlockAnonymizeOnly = false; } std::map mapWallet; @@ -184,7 +204,7 @@ public: // check whether we are allowed to upgrade (or already support) to the named feature bool CanSupportFeature(enum WalletFeature wf) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; } - void AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl = NULL) const; + void AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl = NULL, AvailableCoinsType coin_type=ALL_COINS) const; bool SelectCoinsMinConf(int64_t nTargetValue, int nConfMine, int nConfTheirs, std::vector vCoins, std::set >& setCoinsRet, int64_t& nValueRet) const; bool IsSpent(const uint256& hash, unsigned int n) const; @@ -194,6 +214,7 @@ public: void UnlockCoin(COutPoint& output); void UnlockAllCoins(); void ListLockedCoins(std::vector& vOutpts); + int64_t GetTotalValue(std::vector vCoins); // keystore implementation // Generate a new key @@ -223,7 +244,7 @@ public: /// Look up a destination data tuple in the store, return true if found false otherwise bool GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const; - bool Unlock(const SecureString& strWalletPassphrase); + bool Unlock(const SecureString& strWalletPassphrase, bool anonimizeOnly = false); bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); bool EncryptWallet(const SecureString& strWalletPassphrase); @@ -254,13 +275,21 @@ public: int64_t GetBalance() const; int64_t GetUnconfirmedBalance() const; int64_t GetImmatureBalance() const; + int64_t GetAnonymizedBalance() const; + double GetAverageAnonymizedRounds() const; + int64_t GetDenominatedBalance(bool onlyDenom=true, bool onlyUnconfirmed=false) const; + bool CreateTransaction(const std::vector >& vecSend, - CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL); + CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL, AvailableCoinsType coin_type=ALL_COINS); + bool CreateTransaction(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL); - bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); - std::string SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew); - std::string SendMoneyToDestination(const CTxDestination &address, int64_t nValue, CWalletTx& wtxNew); + bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, std::string strCommand="tx"); + std::string SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, AvailableCoinsType coin_type=ALL_COINS); + std::string SendMoneyToDestination(const CTxDestination &address, int64_t nValue, CWalletTx& wtxNew, AvailableCoinsType coin_type=ALL_COINS); + std::string PrepareDarksendDenominate(int minRounds, int maxRounds, int64_t maxAmount); + bool CreateCollateralTransaction(CTransaction& txCollateral, std::string strReason); + bool ConvertList(std::vector vCoins, std::vector& vecAmounts); bool NewKeyPool(); bool TopUpKeyPool(unsigned int kpSize = 0); @@ -277,6 +306,23 @@ public: std::set GetAccountAddresses(std::string strAccount) const; + int64_t IsDenominated(const CTxIn &txin) const; + + int64_t IsDenominated(const CTransaction& tx) const + { + /* + Return false if ANY inputs are non-denom + */ + bool ret = true; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + if(!IsDenominated(txin)) { + ret = false; + } + } + return ret; + } + bool IsMine(const CTxIn& txin) const; int64_t GetDebit(const CTxIn& txin) const; bool IsMine(const CTxOut& txout) const @@ -588,6 +634,13 @@ public: return nDebitCached; } + int64_t IsDenominated() const + { + if (vin.empty()) + return 0; + return pwallet->IsDenominated(*this); + } + int64_t GetCredit(bool fUseCache=true) const { // Must wait until coinbase is safely deep enough in the chain before valuing it @@ -699,7 +752,7 @@ public: int64_t GetTxTime() const; int GetRequestCount() const; - void RelayWalletTransaction(); + void RelayWalletTransaction(std::string strCommand="tx"); std::set GetConflicts() const; }; @@ -724,6 +777,18 @@ public: return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString().c_str(), i, nDepth, FormatMoney(tx->vout[i].nValue).c_str()); } + //Used with Darksend. Will return fees, then denominations, everything else, then very small inputs that aren't fees + int Priority() const + { + if(tx->vout[i].nValue == DARKSEND_FEE) return -20000; + BOOST_FOREACH(int64_t d, darkSendDenominations) + if(tx->vout[i].nValue == d) return 10000; + if(tx->vout[i].nValue < 1*COIN) return 20000; + + //nondenom return largest first + return -(tx->vout[i].nValue/COIN); + } + void print() const { LogPrintf("%s\n", ToString().c_str());