diff --git a/.gitignore b/.gitignore index 1ad7b35d4d8b0..5864de739a950 100644 --- a/.gitignore +++ b/.gitignore @@ -118,3 +118,4 @@ qa/pull-tester/test.*/* .cproject .project /doc/doxygen/ +/nbproject/ diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index 65bb1feea77a9..f122c256d7480 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -34,12 +34,6 @@ remotes: files: [] script: | - #unlock sudo - echo "ubuntu" | sudo -S true - - sudo mkdir -p /usr/include/i386-linux-gnu/ - sudo ln -s /usr/include/x86_64-linux-gnu/asm /usr/include/i386-linux-gnu/asm - WRAP_DIR=$HOME/wrapped HOSTS="i686-pc-linux-gnu x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu" CONFIGFLAGS="--enable-glibc-back-compat --enable-reduce-exports --disable-bench --disable-gui-tests" @@ -91,11 +85,45 @@ script: | create_per-host_faketime_wrappers "2000-01-01 12:00:00" export PATH=${WRAP_DIR}:${PATH} + EXTRA_INCLUDES_BASE=$WRAP_DIR/extra_includes + mkdir -p $EXTRA_INCLUDES_BASE + + # x86 needs /usr/include/i386-linux-gnu/asm pointed to /usr/include/x86_64-linux-gnu/asm, + # but we can't write there. Instead, create a link here and force it to be included in the + # search paths by wrapping gcc/g++. + + mkdir -p $EXTRA_INCLUDES_BASE/i686-pc-linux-gnu + rm -f $WRAP_DIR/extra_includes/i686-pc-linux-gnu/asm + ln -s /usr/include/x86_64-linux-gnu/asm $EXTRA_INCLUDES_BASE/i686-pc-linux-gnu/asm + + for prog in gcc g++; do + rm -f ${WRAP_DIR}/${prog} + cat << EOF > ${WRAP_DIR}/${prog} + #!/bin/bash + REAL="`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1`" + for var in "\$@" + do + if [ "\$var" = "-m32" ]; then + export C_INCLUDE_PATH="$EXTRA_INCLUDES_BASE/i686-pc-linux-gnu" + export CPLUS_INCLUDE_PATH="$EXTRA_INCLUDES_BASE/i686-pc-linux-gnu" + break + fi + done + \$REAL \$@ + EOF + chmod +x ${WRAP_DIR}/${prog} + done + cd darknet BASEPREFIX=`pwd`/depends # Build dependencies for each host for i in $HOSTS; do + EXTRA_INCLUDES="$EXTRA_INCLUDES_BASE/$i" + if [ -d "$EXTRA_INCLUDES" ]; then + export HOST_ID_SALT="$EXTRA_INCLUDES" + fi make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}" + unset HOST_ID_SALT done # Faketime for binaries diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index f70c3dc22df8e..50ef6011ef534 100755 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -28,6 +28,7 @@ QT_FORMS_UI = \ qt/forms/editaddressdialog.ui \ qt/forms/helpmessagedialog.ui \ qt/forms/intro.ui \ + qt/forms/masternodelist.ui \ qt/forms/openuridialog.ui \ qt/forms/optionsdialog.ui \ qt/forms/overviewpage.ui \ @@ -57,6 +58,7 @@ QT_MOC_CPP = \ qt/moc_intro.cpp \ qt/moc_macdockiconhandler.cpp \ qt/moc_macnotificationhandler.cpp \ + qt/moc_masternodelist.cpp \ qt/moc_notificator.cpp \ qt/moc_openuridialog.cpp \ qt/moc_optionsdialog.cpp \ @@ -124,6 +126,7 @@ BITCOIN_QT_H = \ qt/intro.h \ qt/macdockiconhandler.h \ qt/macnotificationhandler.h \ + qt/masternodelist.h \ qt/networkstyle.h \ qt/notificator.h \ qt/openuridialog.h \ @@ -133,6 +136,7 @@ BITCOIN_QT_H = \ qt/paymentrequestplus.h \ qt/paymentserver.h \ qt/peertablemodel.h \ + qt/platformstyle.h \ qt/qvalidatedlineedit.h \ qt/qvaluecombobox.h \ qt/receivecoinsdialog.h \ @@ -201,6 +205,7 @@ RES_ICONS = \ qt/res/icons/key.png \ qt/res/icons/lock_closed.png \ qt/res/icons/lock_open.png \ + qt/res/icons/masternodes.png \ qt/res/icons/overview.png \ qt/res/icons/qrcode.png \ qt/res/icons/quit.png \ @@ -236,6 +241,7 @@ BITCOIN_QT_CPP = \ qt/optionsdialog.cpp \ qt/optionsmodel.cpp \ qt/peertablemodel.cpp \ + qt/platformstyle.cpp \ qt/qvalidatedlineedit.cpp \ qt/qvaluecombobox.cpp \ qt/rpcconsole.cpp \ @@ -253,6 +259,7 @@ BITCOIN_QT_CPP += \ qt/coincontroltreewidget.cpp \ qt/obfuscationconfig.cpp \ qt/editaddressdialog.cpp \ + qt/masternodelist.cpp \ qt/openuridialog.cpp \ qt/overviewpage.cpp \ qt/paymentrequestplus.cpp \ diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h index 97a0f6d703090..71a3ba775ca90 100644 --- a/src/chainparamsbase.h +++ b/src/chainparamsbase.h @@ -15,6 +15,7 @@ class CBaseChainParams { public: + enum Network { MAIN, TESTNET, diff --git a/src/coincontrol.h b/src/coincontrol.h index b0e6c339df99a..5e8413e7fa86d 100644 --- a/src/coincontrol.h +++ b/src/coincontrol.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_COINCONTROL_H #define BITCOIN_COINCONTROL_H +#include "script/standard.h" #include "primitives/transaction.h" /** Coin Control Features. */ @@ -14,6 +15,12 @@ class CCoinControl CTxDestination destChange; bool useObfuScation; bool useSwiftTX; + //! If false, allows unselected inputs, but requires all selected inputs be used + bool fAllowOtherInputs; + //! Includes watch only addresses which match the ISMINE_WATCH_SOLVABLE criteria + bool fAllowWatchOnly; + //! Minimum absolute fee (not per kilobyte) + CAmount nMinimumTotalFee; CCoinControl() { @@ -26,6 +33,9 @@ class CCoinControl setSelected.clear(); useSwiftTX = false; useObfuScation = true; + fAllowOtherInputs = false; + fAllowWatchOnly = false; + nMinimumTotalFee = 0; } bool HasSelected() const diff --git a/src/coins.h b/src/coins.h index 4c5bdb1d87eaf..a3d4adbc1df89 100644 --- a/src/coins.h +++ b/src/coins.h @@ -10,6 +10,7 @@ #include "serialize.h" #include "uint256.h" #include "undo.h" +#include "script/standard.h" #include #include @@ -358,9 +359,21 @@ class CCoinsViewBacked : public CCoinsView bool GetStats(CCoinsStats &stats) const; }; - class CCoinsViewCache; +/** Flags for nSequence and nLockTime locks */ +enum { + /* Interpret sequence numbers as relative lock-time constraints. */ + LOCKTIME_VERIFY_SEQUENCE = (1 << 0), + + /* Use GetMedianTimePast() instead of nTime for end point timestamp. */ + LOCKTIME_MEDIAN_TIME_PAST = (1 << 1), +}; + +/** Used as the flags parameter to sequence and nLocktime checks in non-consensus code. */ +static const unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS = LOCKTIME_VERIFY_SEQUENCE | + LOCKTIME_MEDIAN_TIME_PAST; + /** * A reference to a mutable cache entry. Encapsulating it allows us to run * cleanup code after the modification is finished, and keeping track of diff --git a/src/main.cpp b/src/main.cpp index c1c5d98994491..dc6760d46898f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1025,6 +1025,38 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) return true; } +bool CheckFinalTx(const CTransaction &tx, int flags) +{ + AssertLockHeld(cs_main); + + // By convention a negative value for flags indicates that the + // current network-enforced consensus rules should be used. In + // a future soft-fork scenario that would mean checking which + // rules would be enforced for the next block and setting the + // appropriate flags. At the present time no soft-forks are + // scheduled, so no flags are set. + flags = std::max(flags, 0); + + // CheckFinalTx() uses chainActive.Height()+1 to evaluate + // nLockTime because when IsFinalTx() is called within + // CBlock::AcceptBlock(), the height of the block *being* + // evaluated is what is used. Thus if we want to know if a + // transaction can be part of the *next* block, we need to call + // IsFinalTx() with one more than chainActive.Height(). + const int nBlockHeight = chainActive.Height() + 1; + + // BIP113 will require that time-locked transactions have nLockTime set to + // less than the median time of the previous block they're contained in. + // When the next block is created its previous block will be the current + // chain tip, so we use that to calculate the median time passed to + // IsFinalTx() if LOCKTIME_MEDIAN_TIME_PAST is set. + const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST) + ? chainActive.Tip()->GetMedianTimePast() + : GetAdjustedTime(); + + return IsFinalTx(tx, nBlockHeight, nBlockTime); +} + CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree) { { diff --git a/src/main.h b/src/main.h index 905d077d19dbe..7432696cc63dd 100644 --- a/src/main.h +++ b/src/main.h @@ -338,6 +338,15 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach /** Context-independent validity checks */ bool CheckTransaction(const CTransaction& tx, CValidationState& state); +/** + * Check if transaction will be final in the next block to be created. + * + * Calls IsFinalTx() with current block height and appropriate block time. + * + * See consensus/consensus.h for flag definitions. + */ +bool CheckFinalTx(const CTransaction &tx, int flags = -1); + /** Check for standard transaction types * @return True if all outputs (scriptPubKeys) use only standard transaction forms */ diff --git a/src/masternode-sync.h b/src/masternode-sync.h index fbe65800e0e85..f7229eccd44ca 100644 --- a/src/masternode-sync.h +++ b/src/masternode-sync.h @@ -72,6 +72,7 @@ class CMasternodeSync void Process(); bool IsSynced(); bool IsBlockchainSynced(); + bool IsMasternodeListSynced() { return RequestedMasternodeAssets > MASTERNODE_SYNC_LIST; } void ClearFulfilledRequest(); }; diff --git a/src/masternode.cpp b/src/masternode.cpp index d5f1817734d68..632275293f1ef 100644 --- a/src/masternode.cpp +++ b/src/masternode.cpp @@ -69,6 +69,7 @@ CMasternode::CMasternode() cacheInputAgeBlock = 0; unitTest = false; allowFreeTx = true; + nActiveState = MASTERNODE_ENABLED, protocolVersion = PROTOCOL_VERSION; nLastDsq = 0; nScanningErrorCount = 0; @@ -93,6 +94,7 @@ CMasternode::CMasternode(const CMasternode& other) cacheInputAgeBlock = other.cacheInputAgeBlock; unitTest = other.unitTest; allowFreeTx = other.allowFreeTx; + nActiveState = MASTERNODE_ENABLED, protocolVersion = other.protocolVersion; nLastDsq = other.nLastDsq; nScanningErrorCount = other.nScanningErrorCount; @@ -117,6 +119,7 @@ CMasternode::CMasternode(const CMasternodeBroadcast& mnb) cacheInputAgeBlock = 0; unitTest = false; allowFreeTx = true; + nActiveState = MASTERNODE_ENABLED, protocolVersion = mnb.protocolVersion; nLastDsq = mnb.nLastDsq; nScanningErrorCount = 0; @@ -284,6 +287,28 @@ int64_t CMasternode::GetLastPaid() { return 0; } +std::string CMasternode::GetStatus() +{ + switch(nActiveState) { + case CMasternode::MASTERNODE_PRE_ENABLED: return "PRE_ENABLED"; + case CMasternode::MASTERNODE_ENABLED: return "ENABLED"; + case CMasternode::MASTERNODE_EXPIRED: return "EXPIRED"; + case CMasternode::MASTERNODE_OUTPOINT_SPENT: return "OUTPOINT_SPENT"; + case CMasternode::MASTERNODE_REMOVE: return "REMOVE"; + case CMasternode::MASTERNODE_WATCHDOG_EXPIRED: return "WATCHDOG_EXPIRED"; + case CMasternode::MASTERNODE_POSE_BAN: return "POSE_BAN"; + default: return "UNKNOWN"; + } +} + +bool CMasternode::IsValidNetAddr() +{ + // TODO: regtest is fine with any addresses for now, + // should probably be a bit smarter if one day we start to implement tests for this + return Params().NetworkID() == CBaseChainParams::REGTEST || + (IsReachable(addr) && addr.IsRoutable()); +} + CMasternodeBroadcast::CMasternodeBroadcast() { vin = CTxIn(); @@ -344,6 +369,88 @@ CMasternodeBroadcast::CMasternodeBroadcast(const CMasternode& mn) nLastScanningErrorBlockHeight = mn.nLastScanningErrorBlockHeight; } +bool CMasternodeBroadcast::Create(std::string strService, std::string strKeyMasternode, std::string strTxHash, std::string strOutputIndex, std::string& strErrorRet, CMasternodeBroadcast &mnbRet, bool fOffline) +{ + CTxIn txin; + CPubKey pubKeyCollateralAddressNew; + CKey keyCollateralAddressNew; + CPubKey pubKeyMasternodeNew; + CKey keyMasternodeNew; + + //need correct blocks to send ping + if(!fOffline && !masternodeSync.IsBlockchainSynced()) { + strErrorRet = "Sync in progress. Must wait until sync is complete to start Masternode"; + LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet); + return false; + } + + if(!obfuScationSigner.GetKeysFromSecret(strKeyMasternode, keyMasternodeNew, pubKeyMasternodeNew)) { + strErrorRet = strprintf("Invalid masternode key %s", strKeyMasternode); + LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet); + return false; + } + + if(!pwalletMain->GetMasternodeVinAndKeys(txin, pubKeyCollateralAddressNew, keyCollateralAddressNew, strTxHash, strOutputIndex)) { + strErrorRet = strprintf("Could not allocate txin %s:%s for masternode %s", strTxHash, strOutputIndex, strService); + LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet); + return false; + } + + CService service = CService(strService); + int mainnetDefaultPort = Params(CBaseChainParams::MAIN).GetDefaultPort(); + if(Params().NetworkID() == CBaseChainParams::MAIN) { + if(service.GetPort() != mainnetDefaultPort) { + strErrorRet = strprintf("Invalid port %u for masternode %s, only %d is supported on mainnet.", service.GetPort(), strService, mainnetDefaultPort); + LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet); + return false; + } + } else if (service.GetPort() == mainnetDefaultPort) { + strErrorRet = strprintf("Invalid port %u for masternode %s, %d is the only supported on mainnet.", service.GetPort(), strService, mainnetDefaultPort); + LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet); + return false; + } + + return Create(txin, CService(strService), keyCollateralAddressNew, pubKeyCollateralAddressNew, keyMasternodeNew, pubKeyMasternodeNew, strErrorRet, mnbRet); +} + +bool CMasternodeBroadcast::Create(CTxIn txin, CService service, CKey keyCollateralAddressNew, CPubKey pubKeyCollateralAddressNew, CKey keyMasternodeNew, CPubKey pubKeyMasternodeNew, std::string &strErrorRet, CMasternodeBroadcast &mnbRet) +{ + // wait for reindex and/or import to finish + if (fImporting || fReindex) return false; + + LogPrint("masternode", "CMasternodeBroadcast::Create -- pubKeyCollateralAddressNew = %s, pubKeyMasternodeNew.GetID() = %s\n", + CBitcoinAddress(pubKeyCollateralAddressNew.GetID()).ToString(), + pubKeyMasternodeNew.GetID().ToString()); + + + CMasternodePing mnp(txin); + if(!mnp.Sign(keyMasternodeNew, pubKeyMasternodeNew)) { + strErrorRet = strprintf("Failed to sign ping, masternode=%s", txin.prevout.ToStringShort()); + LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet); + mnbRet = CMasternodeBroadcast(); + return false; + } + + mnbRet = CMasternodeBroadcast(service, txin, pubKeyCollateralAddressNew, pubKeyMasternodeNew, PROTOCOL_VERSION); + + if(!mnbRet.IsValidNetAddr()) { + strErrorRet = strprintf("Invalid IP address, masternode=%s", txin.prevout.ToStringShort()); + LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet); + mnbRet = CMasternodeBroadcast(); + return false; + } + + mnbRet.lastPing = mnp; + if(!mnbRet.Sign(keyCollateralAddressNew)) { + strErrorRet = strprintf("Failed to sign broadcast, masternode=%s", txin.prevout.ToStringShort()); + LogPrintf("CMasternodeBroadcast::Create -- %s\n", strErrorRet); + mnbRet = CMasternodeBroadcast(); + return false; + } + + return true; +} + bool CMasternodeBroadcast::CheckAndUpdate(int& nDos) { // make sure signature isn't in the future (past is OK) diff --git a/src/masternode.h b/src/masternode.h index 039f3793d18a7..3a5337d8a498f 100644 --- a/src/masternode.h +++ b/src/masternode.h @@ -99,7 +99,6 @@ class CMasternodePing }; - // // The Masternode Class. For managing the Obfuscation process. It contains the input of the 10000DNET, signature to prove // it's the one who own that ip address and code for calculating the payment election. @@ -112,17 +111,23 @@ class CMasternode int64_t lastTimeChecked; public: enum state { - MASTERNODE_ENABLED = 1, - MASTERNODE_EXPIRED = 2, - MASTERNODE_VIN_SPENT = 3, - MASTERNODE_REMOVE = 4, - MASTERNODE_POS_ERROR = 5 + MASTERNODE_PRE_ENABLED, + MASTERNODE_ENABLED, + MASTERNODE_EXPIRED, + MASTERNODE_OUTPOINT_SPENT, + MASTERNODE_REMOVE, + MASTERNODE_WATCHDOG_EXPIRED, + MASTERNODE_POSE_BAN, + MASTERNODE_VIN_SPENT, + MASTERNODE_POS_ERROR }; CTxIn vin; CService addr; CPubKey pubkey; CPubKey pubkey2; + CPubKey pubKeyCollateralAddress; + CPubKey pubKeyMasternode; std::vector sig; int activeState; int64_t sigTime; //mnb message time @@ -131,6 +136,7 @@ class CMasternode bool unitTest; bool allowFreeTx; int protocolVersion; + int nActiveState; int64_t nLastDsq; //the dsq count from the last dsq broadcast of this node int nScanningErrorCount; int nLastScanningErrorBlockHeight; @@ -259,6 +265,8 @@ class CMasternode return cacheInputAge+(chainActive.Tip()->nHeight-cacheInputAgeBlock); } + std::string GetStatus(); + std::string Status() { std::string strStatus = "ACTIVE"; @@ -272,6 +280,7 @@ class CMasternode } int64_t GetLastPaid(); + bool IsValidNetAddr(); }; @@ -314,6 +323,9 @@ class CMasternodeBroadcast : public CMasternode return ss.GetHash(); } +/// Create Masternode broadcast, needs to be relayed manually after that + static bool Create(CTxIn vin, CService service, CKey keyCollateralAddressNew, CPubKey pubKeyCollateralAddressNew, CKey keyMasternodeNew, CPubKey pubKeyMasternodeNew, std::string &strErrorRet, CMasternodeBroadcast &mnbRet); + static bool Create(std::string strService, std::string strKey, std::string strTxHash, std::string strOutputIndex, std::string& strErrorRet, CMasternodeBroadcast &mnbRet, bool fOffline = false); }; #endif diff --git a/src/masternodeman.cpp b/src/masternodeman.cpp index 46128176ed1a3..274a5e4ccff56 100644 --- a/src/masternodeman.cpp +++ b/src/masternodeman.cpp @@ -1108,6 +1108,25 @@ void CMasternodeMan::Remove(CTxIn vin) } } +void CMasternodeMan::UpdateMasternodeList(CMasternodeBroadcast mnb) +{ + LOCK(cs); + mapSeenMasternodePing.insert(std::make_pair(mnb.lastPing.GetHash(), mnb.lastPing)); + mapSeenMasternodeBroadcast.insert(std::make_pair(mnb.GetHash(), mnb)); + + LogPrintf("CMasternodeMan::UpdateMasternodeList -- masternode=%s addr=%s\n", mnb.vin.prevout.ToStringShort(), mnb.addr.ToString()); + + CMasternode* pmn = Find(mnb.vin); + if(pmn == NULL) { + CMasternode mn(mnb); + if(Add(mn)) { + masternodeSync.AddedMasternodeList(mnb.GetHash()); + } + } else if(pmn->UpdateFromNewBroadcast(mnb)) { + masternodeSync.AddedMasternodeList(mnb.GetHash()); + } +} + std::string CMasternodeMan::ToString() const { std::ostringstream info; diff --git a/src/masternodeman.h b/src/masternodeman.h index c11193ca6e254..8a253f7d187aa 100644 --- a/src/masternodeman.h +++ b/src/masternodeman.h @@ -141,6 +141,9 @@ class CMasternodeMan std::string ToString() const; void Remove(CTxIn vin); + + /// Update masternode list and maps using provided CMasternodeBroadcast + void UpdateMasternodeList(CMasternodeBroadcast mnb); }; diff --git a/src/obfuscation.cpp b/src/obfuscation.cpp index 9ef206c952f96..9abd5495907ce 100644 --- a/src/obfuscation.cpp +++ b/src/obfuscation.cpp @@ -3,6 +3,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "coincontrol.h" #include "obfuscation.h" #include "main.h" #include "init.h" @@ -1697,8 +1698,9 @@ bool CObfuscationPool::MakeCollateralAmounts() int64_t nFeeRet = 0; std::string strFail = ""; vector< pair > vecSend; - CCoinControl *coinControl = NULL; - + CCoinControl coinControl; + coinControl.fAllowOtherInputs = false; // TKS probable crash reason + coinControl.fAllowWatchOnly = false; // TKS // make our collateral address CReserveKey reservekeyCollateral(pwalletMain); // make our change address @@ -1713,13 +1715,14 @@ bool CObfuscationPool::MakeCollateralAmounts() // try to use non-denominated and not mn-like funds bool success = pwalletMain->CreateTransaction(vecSend, wtx, reservekeyChange, - nFeeRet, strFail, coinControl, ONLY_NONDENOMINATED_NOT10000IFMN); + nFeeRet, strFail, &coinControl, ONLY_NONDENOMINATED_NOT10000IFMN); if(!success){ // if we failed (most likeky not enough funds), try to use all coins instead - // MN-like funds should not be touched in any case and we can't mix denominated without collaterals anyway + CCoinControl *coinControlNull = NULL; LogPrintf("MakeCollateralAmounts: ONLY_NONDENOMINATED_NOT10000IFMN Error - %s\n", strFail); success = pwalletMain->CreateTransaction(vecSend, wtx, reservekeyChange, - nFeeRet, strFail, coinControl, ONLY_NOT10000IFMN); + nFeeRet, strFail, coinControlNull, ONLY_NOT10000IFMN); if(!success){ LogPrintf("MakeCollateralAmounts: ONLY_NOT10000IFMN Error - %s\n", strFail); reservekeyCollateral.ReturnKey(); @@ -2086,6 +2089,18 @@ bool CObfuScationSigner::SetKey(std::string strSecret, std::string& errorMessage return true; } +bool CObfuScationSigner::GetKeysFromSecret(std::string strSecret, CKey& keyRet, CPubKey& pubkeyRet) +{ + CBitcoinSecret vchSecret; + + if(!vchSecret.SetString(strSecret)) return false; + + keyRet = vchSecret.GetKey(); + pubkeyRet = keyRet.GetPubKey(); + + return true; +} + bool CObfuScationSigner::SignMessage(std::string strMessage, std::string& errorMessage, vector& vchSig, CKey key) { CHashWriter ss(SER_GETHASH, 0); diff --git a/src/obfuscation.h b/src/obfuscation.h index f15946b1520f6..40554e06f3390 100644 --- a/src/obfuscation.h +++ b/src/obfuscation.h @@ -249,6 +249,8 @@ class CObfuScationSigner /// Is the inputs associated with this public key? (and there is 1000 DNET - checking if valid masternode) bool IsVinAssociatedWithPubkey(CTxIn& vin, CPubKey& pubkey); /// Set the private/public key values, returns true if successful + bool GetKeysFromSecret(std::string strSecret, CKey& keyRet, CPubKey& pubkeyRet); + /// Set the private/public key values, returns true if successful bool SetKey(std::string strSecret, std::string& errorMessage, CKey& key, CPubKey& pubkey); /// Sign the message, returns true if successful bool SignMessage(std::string strMessage, std::string& errorMessage, std::vector& vchSig, CKey key); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 1879981b36c68..e7fa1b380abbb 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -29,6 +29,7 @@ #endif #include "init.h" +#include "masternodelist.h" #include "ui_interface.h" #include "util.h" @@ -78,6 +79,7 @@ BitcoinGUI::BitcoinGUI(const NetworkStyle *networkStyle, QWidget *parent) : appMenuBar(0), overviewAction(0), historyAction(0), + masternodeAction(0), quitAction(0), sendCoinsAction(0), usedSendingAddressesAction(0), @@ -316,6 +318,23 @@ void BitcoinGUI::createActions(const NetworkStyle *networkStyle) tabGroup->addAction(historyAction); #ifdef ENABLE_WALLET + + QSettings settings; + if (settings.value("fShowMasternodesTab").toBool()) { + masternodeAction = new QAction(QIcon(":/icons/masternodes"), tr("&Masternodes"), this); + masternodeAction->setStatusTip(tr("Browse masternodes")); + masternodeAction->setToolTip(masternodeAction->statusTip()); + masternodeAction->setCheckable(true); + #ifdef Q_OS_MAC + masternodeAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_5)); + #else + masternodeAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_5)); + #endif + tabGroup->addAction(masternodeAction); + connect(masternodeAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(masternodeAction, SIGNAL(triggered()), this, SLOT(gotoMasternodePage())); + } + // These showNormalIfMinimized are needed because Send Coins and Receive Coins // can be triggered from the tray menu, and need to show the GUI to be useful. connect(overviewAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); @@ -479,6 +498,11 @@ void BitcoinGUI::createToolBars() toolbar->addAction(sendCoinsAction); toolbar->addAction(receiveCoinsAction); toolbar->addAction(historyAction); + QSettings settings; + if (settings.value("fShowMasternodesTab").toBool()) + { + toolbar->addAction(masternodeAction); + } toolbar->setMovable(false); // remove unused icon in upper left corner overviewAction->setChecked(true); @@ -568,6 +592,10 @@ void BitcoinGUI::setWalletActionsEnabled(bool enabled) sendCoinsAction->setEnabled(enabled); receiveCoinsAction->setEnabled(enabled); historyAction->setEnabled(enabled); + QSettings settings; + if (settings.value("fShowMasternodesTab").toBool()) { + masternodeAction->setEnabled(enabled); + } encryptWalletAction->setEnabled(enabled); backupWalletAction->setEnabled(enabled); changePassphraseAction->setEnabled(enabled); @@ -693,6 +721,15 @@ void BitcoinGUI::gotoHistoryPage() if (walletFrame) walletFrame->gotoHistoryPage(); } +void BitcoinGUI::gotoMasternodePage() +{ + QSettings settings; + if (settings.value("fShowMasternodesTab").toBool()) { + masternodeAction->setChecked(true); + if (walletFrame) walletFrame->gotoMasternodePage(); + } +} + void BitcoinGUI::gotoReceiveCoinsPage() { receiveCoinsAction->setChecked(true); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 2d1ebdd33bd16..d8b1e941e2e8a 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -28,6 +28,7 @@ class SendCoinsRecipient; class UnitDisplayStatusBarControl; class WalletFrame; class WalletModel; +class MasternodeList; class CWallet; @@ -90,6 +91,7 @@ class BitcoinGUI : public QMainWindow QMenuBar *appMenuBar; QAction *overviewAction; QAction *historyAction; + QAction *masternodeAction; QAction *quitAction; QAction *sendCoinsAction; QAction *usedSendingAddressesAction; @@ -188,6 +190,8 @@ private slots: void gotoOverviewPage(); /** Switch to history (transactions) page */ void gotoHistoryPage(); + /** Switch to masternode page */ + void gotoMasternodePage(); /** Switch to receive coins page */ void gotoReceiveCoinsPage(); /** Switch to send coins page */ diff --git a/src/qt/darknet.qrc b/src/qt/darknet.qrc index b21c40c4c930f..eb0c5688fbdc1 100644 --- a/src/qt/darknet.qrc +++ b/src/qt/darknet.qrc @@ -29,6 +29,7 @@ res/icons/edit.png res/icons/history.png res/icons/overview.png + res/icons/masternodes.png res/icons/export.png res/icons/synced.png res/icons/remove.png diff --git a/src/qt/forms/masternodelist.ui b/src/qt/forms/masternodelist.ui new file mode 100644 index 0000000000000..437f3c6348060 --- /dev/null +++ b/src/qt/forms/masternodelist.ui @@ -0,0 +1,323 @@ + + + MasternodeList + + + + 0 + 0 + 723 + 457 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 0 + + + + My Masternodes + + + + + + 0 + + + + + 0 + + + + + Note: Status of your masternodes in local wallet can potentially be slightly incorrect.<br />Always wait for wallet to sync additional data and then double check from another node<br />if your node should be running but you still see "MISSING" in "Status" field. + + + + + + + + + + 695 + 0 + + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + true + + + QHeaderView::Interactive + + + + Alias + + + + + Address + + + + + Protocol + + + + + Status + + + + + Active + + + + + Last Seen (UTC) + + + + + Pubkey + + + + + + + + 0 + + + + + S&tart alias + + + + + + + Start &all + + + + + + + Start &MISSING + + + + + + + &Update status + + + + + + + Status will be updated automatically in (sec): + + + + + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + All Masternodes + + + + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::SelectRows + + + true + + + true + + + QHeaderView::Interactive + + + + Address + + + + + Protocol + + + + + Status + + + + + Active + + + + + Last Seen (UTC) + + + + + Pubkey + + + + + + + + 0 + + + + + Filter List: + + + + + + + Filter masternode list + + + + + + + Qt::Horizontal + + + + 10 + 20 + + + + + + + + Node Count: + + + + + + + 0 + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index a6b9a2b4e4d24..72f320f861313 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -222,6 +222,16 @@ + + + + Show additional tab listing all your masternodes in first sub-tab<br/>and all masternodes on the network in second sub-tab. + + + Show Masternodes Tab + + + diff --git a/src/qt/masternodelist.cpp b/src/qt/masternodelist.cpp new file mode 100644 index 0000000000000..9f4de001cabba --- /dev/null +++ b/src/qt/masternodelist.cpp @@ -0,0 +1,396 @@ +#include "masternodelist.h" +#include "ui_masternodelist.h" + +#include "sync.h" +#include "clientmodel.h" +#include "walletmodel.h" +#include "activemasternode.h" +#include "masternode-sync.h" +#include "masternodeconfig.h" +#include "masternodeman.h" +#include "wallet.h" +#include "init.h" +#include "guiutil.h" + +#include +#include + +CCriticalSection cs_masternodes; + +MasternodeList::MasternodeList(QWidget *parent) : + QWidget(parent), + ui(new Ui::MasternodeList), + clientModel(0), + walletModel(0) +{ + ui->setupUi(this); + + ui->startButton->setEnabled(false); + + int columnAliasWidth = 100; + int columnAddressWidth = 200; + int columnProtocolWidth = 60; + int columnStatusWidth = 80; + int columnActiveWidth = 130; + int columnLastSeenWidth = 130; + + ui->tableWidgetMyMasternodes->setColumnWidth(0, columnAliasWidth); + ui->tableWidgetMyMasternodes->setColumnWidth(1, columnAddressWidth); + ui->tableWidgetMyMasternodes->setColumnWidth(2, columnProtocolWidth); + ui->tableWidgetMyMasternodes->setColumnWidth(3, columnStatusWidth); + ui->tableWidgetMyMasternodes->setColumnWidth(4, columnActiveWidth); + ui->tableWidgetMyMasternodes->setColumnWidth(5, columnLastSeenWidth); + + ui->tableWidgetMasternodes->setColumnWidth(0, columnAddressWidth); + ui->tableWidgetMasternodes->setColumnWidth(1, columnProtocolWidth); + ui->tableWidgetMasternodes->setColumnWidth(2, columnStatusWidth); + ui->tableWidgetMasternodes->setColumnWidth(3, columnActiveWidth); + ui->tableWidgetMasternodes->setColumnWidth(4, columnLastSeenWidth); + + ui->tableWidgetMyMasternodes->setContextMenuPolicy(Qt::CustomContextMenu); + + QAction *startAliasAction = new QAction(tr("Start alias"), this); + contextMenu = new QMenu(); + contextMenu->addAction(startAliasAction); + connect(ui->tableWidgetMyMasternodes, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenu(const QPoint&))); + connect(startAliasAction, SIGNAL(triggered()), this, SLOT(on_startButton_clicked())); + + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(updateNodeList())); + connect(timer, SIGNAL(timeout()), this, SLOT(updateMyNodeList())); + timer->start(1000); + + // Fill MN list + fFilterUpdated = true; + nTimeFilterUpdated = GetTime(); + updateNodeList(); +} + +MasternodeList::~MasternodeList() +{ + delete ui; +} + +void MasternodeList::setClientModel(ClientModel *model) +{ + this->clientModel = model; + if(model) { + // try to update list when masternode count changes + connect(clientModel, SIGNAL(strMasternodesChanged(QString)), this, SLOT(updateNodeList())); + } +} + +void MasternodeList::setWalletModel(WalletModel *model) +{ + this->walletModel = model; +} + +void MasternodeList::showContextMenu(const QPoint &point) +{ + QTableWidgetItem *item = ui->tableWidgetMyMasternodes->itemAt(point); + if(item) contextMenu->exec(QCursor::pos()); +} + +void MasternodeList::StartAlias(std::string strAlias) +{ + std::string strStatusHtml; + strStatusHtml += "
Alias: " + strAlias; + + BOOST_FOREACH(CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) { + if(mne.getAlias() == strAlias) { + std::string strError; + CMasternodeBroadcast mnb; + + bool fSuccess = CMasternodeBroadcast::Create(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), strError, mnb); + + if(fSuccess) { + strStatusHtml += "
Successfully started masternode."; + mnodeman.UpdateMasternodeList(mnb); + mnb.Relay(); + } else { + strStatusHtml += "
Failed to start masternode.
Error: " + strError; + } + break; + } + } + strStatusHtml += "
"; + + QMessageBox msg; + msg.setText(QString::fromStdString(strStatusHtml)); + msg.exec(); + + updateMyNodeList(true); +} + +void MasternodeList::StartAll(std::string strCommand) +{ + int nCountSuccessful = 0; + int nCountFailed = 0; + std::string strFailedHtml; + + BOOST_FOREACH(CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) { + std::string strError; + CMasternodeBroadcast mnb; + + CTxIn txin = CTxIn(uint256S(mne.getTxHash()), uint32_t(atoi(mne.getOutputIndex().c_str()))); + CMasternode *pmn = mnodeman.Find(txin); + + if(strCommand == "start-missing" && pmn) continue; + + bool fSuccess = CMasternodeBroadcast::Create(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), strError, mnb); + + if(fSuccess) { + nCountSuccessful++; + mnodeman.UpdateMasternodeList(mnb); + mnb.Relay(); + } else { + nCountFailed++; + strFailedHtml += "\nFailed to start " + mne.getAlias() + ". Error: " + strError; + } + } + pwalletMain->Lock(); + + std::string returnObj; + returnObj = strprintf("Successfully started %d masternodes, failed to start %d, total %d", nCountSuccessful, nCountFailed, nCountFailed + nCountSuccessful); + if (nCountFailed > 0) { + returnObj += strFailedHtml; + } + + QMessageBox msg; + msg.setText(QString::fromStdString(returnObj)); + msg.exec(); + + updateMyNodeList(true); +} + +void MasternodeList::updateMyMasternodeInfo(QString strAlias, QString strAddr, CMasternode *pmn) +{ + LOCK(cs_mnlistupdate); + bool fOldRowFound = false; + int nNewRow = 0; + + for(int i = 0; i < ui->tableWidgetMyMasternodes->rowCount(); i++) { + if(ui->tableWidgetMyMasternodes->item(i, 0)->text() == strAlias) { + fOldRowFound = true; + nNewRow = i; + break; + } + } + + if(nNewRow == 0 && !fOldRowFound) { + nNewRow = ui->tableWidgetMyMasternodes->rowCount(); + ui->tableWidgetMyMasternodes->insertRow(nNewRow); + } + + QTableWidgetItem *aliasItem = new QTableWidgetItem(strAlias); + QTableWidgetItem *addrItem = new QTableWidgetItem(pmn ? QString::fromStdString(pmn->addr.ToString()) : strAddr); + QTableWidgetItem *protocolItem = new QTableWidgetItem(QString::number(pmn ? pmn->protocolVersion : -1)); + QTableWidgetItem *statusItem = new QTableWidgetItem(QString::fromStdString(pmn ? pmn->GetStatus() : "MISSING")); + QTableWidgetItem *activeSecondsItem = new QTableWidgetItem(QString::fromStdString(DurationToDHMS(pmn ? (pmn->lastPing.sigTime - pmn->sigTime) : 0))); + QTableWidgetItem *lastSeenItem = new QTableWidgetItem(QString::fromStdString(DateTimeStrFormat("%Y-%m-%d %H:%M", pmn ? pmn->lastPing.sigTime : 0))); + QTableWidgetItem *pubkeyItem = new QTableWidgetItem(QString::fromStdString(pmn ? CBitcoinAddress(pmn->pubKeyCollateralAddress.GetID()).ToString() : "")); + + ui->tableWidgetMyMasternodes->setItem(nNewRow, 0, aliasItem); + ui->tableWidgetMyMasternodes->setItem(nNewRow, 1, addrItem); + ui->tableWidgetMyMasternodes->setItem(nNewRow, 2, protocolItem); + ui->tableWidgetMyMasternodes->setItem(nNewRow, 3, statusItem); + ui->tableWidgetMyMasternodes->setItem(nNewRow, 4, activeSecondsItem); + ui->tableWidgetMyMasternodes->setItem(nNewRow, 5, lastSeenItem); + ui->tableWidgetMyMasternodes->setItem(nNewRow, 6, pubkeyItem); +} + +void MasternodeList::updateMyNodeList(bool fForce) +{ + static int64_t nTimeMyListUpdated = 0; + + // automatically update my masternode list only once in MY_MASTERNODELIST_UPDATE_SECONDS seconds, + // this update still can be triggered manually at any time via button click + int64_t nSecondsTillUpdate = nTimeMyListUpdated + MY_MASTERNODELIST_UPDATE_SECONDS - GetTime(); + ui->secondsLabel->setText(QString::number(nSecondsTillUpdate)); + + if(nSecondsTillUpdate > 0 && !fForce) return; + nTimeMyListUpdated = GetTime(); + + ui->tableWidgetMasternodes->setSortingEnabled(false); + BOOST_FOREACH(CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) { + CTxIn txin = CTxIn(uint256S(mne.getTxHash()), uint32_t(atoi(mne.getOutputIndex().c_str()))); + CMasternode *pmn = mnodeman.Find(txin); + + updateMyMasternodeInfo(QString::fromStdString(mne.getAlias()), QString::fromStdString(mne.getIp()), pmn); + } + ui->tableWidgetMasternodes->setSortingEnabled(true); + + // reset "timer" + ui->secondsLabel->setText("0"); +} + +void MasternodeList::updateNodeList() +{ + static int64_t nTimeListUpdated = GetTime(); + + // to prevent high cpu usage update only once in MASTERNODELIST_UPDATE_SECONDS seconds + // or MASTERNODELIST_FILTER_COOLDOWN_SECONDS seconds after filter was last changed + int64_t nSecondsToWait = fFilterUpdated + ? nTimeFilterUpdated - GetTime() + MASTERNODELIST_FILTER_COOLDOWN_SECONDS + : nTimeListUpdated - GetTime() + MASTERNODELIST_UPDATE_SECONDS; + + if(fFilterUpdated) ui->countLabel->setText(QString::fromStdString(strprintf("Please wait... %d", nSecondsToWait))); + if(nSecondsToWait > 0) return; + + nTimeListUpdated = GetTime(); + fFilterUpdated = false; + + TRY_LOCK(cs_masternodes, lockMasternodes); + if(!lockMasternodes) return; + + QString strToFilter; + ui->countLabel->setText("Updating..."); + ui->tableWidgetMasternodes->setSortingEnabled(false); + ui->tableWidgetMasternodes->clearContents(); + ui->tableWidgetMasternodes->setRowCount(0); + std::vector vMasternodes = mnodeman.GetFullMasternodeVector(); + + BOOST_FOREACH(CMasternode& mn, vMasternodes) + { + // populate list + // Address, Protocol, Status, Active Seconds, Last Seen, Pub Key + QTableWidgetItem *addressItem = new QTableWidgetItem(QString::fromStdString(mn.addr.ToString())); + QTableWidgetItem *protocolItem = new QTableWidgetItem(QString::number(mn.protocolVersion)); + QTableWidgetItem *statusItem = new QTableWidgetItem(QString::fromStdString(mn.GetStatus())); + QTableWidgetItem *activeSecondsItem = new QTableWidgetItem(QString::fromStdString(DurationToDHMS(mn.lastPing.sigTime - mn.sigTime))); + QTableWidgetItem *lastSeenItem = new QTableWidgetItem(QString::fromStdString(DateTimeStrFormat("%Y-%m-%d %H:%M", mn.lastPing.sigTime))); + QTableWidgetItem *pubkeyItem = new QTableWidgetItem(QString::fromStdString(CBitcoinAddress(mn.pubKeyCollateralAddress.GetID()).ToString())); + + if (strCurrentFilter != "") + { + strToFilter = addressItem->text() + " " + + protocolItem->text() + " " + + statusItem->text() + " " + + activeSecondsItem->text() + " " + + lastSeenItem->text() + " " + + pubkeyItem->text(); + if (!strToFilter.contains(strCurrentFilter)) continue; + } + + ui->tableWidgetMasternodes->insertRow(0); + ui->tableWidgetMasternodes->setItem(0, 0, addressItem); + ui->tableWidgetMasternodes->setItem(0, 1, protocolItem); + ui->tableWidgetMasternodes->setItem(0, 2, statusItem); + ui->tableWidgetMasternodes->setItem(0, 3, activeSecondsItem); + ui->tableWidgetMasternodes->setItem(0, 4, lastSeenItem); + ui->tableWidgetMasternodes->setItem(0, 5, pubkeyItem); + } + + ui->countLabel->setText(QString::number(ui->tableWidgetMasternodes->rowCount())); + ui->tableWidgetMasternodes->setSortingEnabled(true); +} + +void MasternodeList::on_filterLineEdit_textChanged(const QString &strFilterIn) +{ + strCurrentFilter = strFilterIn; + nTimeFilterUpdated = GetTime(); + fFilterUpdated = true; + ui->countLabel->setText(QString::fromStdString(strprintf("Please wait... %d", MASTERNODELIST_FILTER_COOLDOWN_SECONDS))); +} + +void MasternodeList::on_startButton_clicked() +{ + // Find selected node alias + QItemSelectionModel* selectionModel = ui->tableWidgetMyMasternodes->selectionModel(); + QModelIndexList selected = selectionModel->selectedRows(); + + if(selected.count() == 0) return; + + QModelIndex index = selected.at(0); + int nSelectedRow = index.row(); + std::string strAlias = ui->tableWidgetMyMasternodes->item(nSelectedRow, 0)->text().toStdString(); + + // Display message box + QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm masternode start"), + tr("Are you sure you want to start masternode %1?").arg(QString::fromStdString(strAlias)), + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Cancel); + + if(retval != QMessageBox::Yes) return; + + WalletModel::EncryptionStatus encStatus = walletModel->getEncryptionStatus(); + + if(encStatus == walletModel->Locked || encStatus == walletModel->UnlockedForAnonymizationOnly) { + WalletModel::UnlockContext ctx(walletModel->requestUnlock()); + + if(!ctx.isValid()) return; // Unlock wallet was cancelled + + StartAlias(strAlias); + return; + } + + StartAlias(strAlias); +} + +void MasternodeList::on_startAllButton_clicked() +{ + // Display message box + QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm all masternodes start"), + tr("Are you sure you want to start ALL masternodes?"), + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Cancel); + + if(retval != QMessageBox::Yes) return; + + WalletModel::EncryptionStatus encStatus = walletModel->getEncryptionStatus(); + + if(encStatus == walletModel->Locked || encStatus == walletModel->UnlockedForAnonymizationOnly) { + WalletModel::UnlockContext ctx(walletModel->requestUnlock()); + + if(!ctx.isValid()) return; // Unlock wallet was cancelled + + StartAll(); + return; + } + + StartAll(); +} + +void MasternodeList::on_startMissingButton_clicked() +{ + + if(!masternodeSync.IsMasternodeListSynced()) { + QMessageBox::critical(this, tr("Command is not available right now"), + tr("You can't use this command until masternode list is synced")); + return; + } + + // Display message box + QMessageBox::StandardButton retval = QMessageBox::question(this, + tr("Confirm missing masternodes start"), + tr("Are you sure you want to start MISSING masternodes?"), + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Cancel); + + if(retval != QMessageBox::Yes) return; + + WalletModel::EncryptionStatus encStatus = walletModel->getEncryptionStatus(); + + if(encStatus == walletModel->Locked || encStatus == walletModel->UnlockedForAnonymizationOnly) { + WalletModel::UnlockContext ctx(walletModel->requestUnlock()); + + if(!ctx.isValid()) return; // Unlock wallet was cancelled + + StartAll("start-missing"); + return; + } + + StartAll("start-missing"); +} + +void MasternodeList::on_tableWidgetMyMasternodes_itemSelectionChanged() +{ + if(ui->tableWidgetMyMasternodes->selectedItems().count() > 0) { + ui->startButton->setEnabled(true); + } +} + +void MasternodeList::on_UpdateButton_clicked() +{ + updateMyNodeList(true); +} diff --git a/src/qt/masternodelist.h b/src/qt/masternodelist.h new file mode 100644 index 0000000000000..35efd63523418 --- /dev/null +++ b/src/qt/masternodelist.h @@ -0,0 +1,71 @@ +#ifndef MASTERNODELIST_H +#define MASTERNODELIST_H + +#include "masternode.h" +#include "platformstyle.h" +#include "sync.h" +#include "util.h" + +#include +#include +#include + +#define MY_MASTERNODELIST_UPDATE_SECONDS 60 +#define MASTERNODELIST_UPDATE_SECONDS 15 +#define MASTERNODELIST_FILTER_COOLDOWN_SECONDS 3 + +namespace Ui { + class MasternodeList; +} + +class ClientModel; +class WalletModel; + +QT_BEGIN_NAMESPACE +class QModelIndex; +QT_END_NAMESPACE + +/** Masternode Manager page widget */ +class MasternodeList : public QWidget +{ + Q_OBJECT + +public: + explicit MasternodeList(QWidget *parent = 0); + ~MasternodeList(); + + void setClientModel(ClientModel *clientModel); + void setWalletModel(WalletModel *walletModel); + void StartAlias(std::string strAlias); + void StartAll(std::string strCommand = "start-all"); + +private: + QMenu *contextMenu; + int64_t nTimeFilterUpdated; + bool fFilterUpdated; + +public Q_SLOTS: + void updateMyMasternodeInfo(QString strAlias, QString strAddr, CMasternode *pmn); + void updateMyNodeList(bool fForce = false); + void updateNodeList(); + +Q_SIGNALS: + +private: + QTimer *timer; + Ui::MasternodeList *ui; + ClientModel *clientModel; + WalletModel *walletModel; + CCriticalSection cs_mnlistupdate; + QString strCurrentFilter; + +private Q_SLOTS: + void showContextMenu(const QPoint &); + void on_filterLineEdit_textChanged(const QString &strFilterIn); + void on_startButton_clicked(); + void on_startAllButton_clicked(); + void on_startMissingButton_clicked(); + void on_tableWidgetMyMasternodes_itemSelectionChanged(); + void on_UpdateButton_clicked(); +}; +#endif // MASTERNODELIST_H diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 948bd868b3ee0..e15a0fa0290f6 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -173,6 +173,7 @@ void OptionsDialog::setModel(OptionsModel *model) connect(ui->theme, SIGNAL(valueChanged()), this, SLOT(showRestartWarning())); connect(ui->lang, SIGNAL(valueChanged()), this, SLOT(showRestartWarning())); connect(ui->thirdPartyTxUrls, SIGNAL(textChanged(const QString &)), this, SLOT(showRestartWarning())); + connect(ui->showMasternodesTab, SIGNAL(clicked(bool)), this, SLOT(showRestartWarning())); } void OptionsDialog::setMapper() @@ -212,6 +213,7 @@ void OptionsDialog::setMapper() /* Obfuscation Rounds */ mapper->addMapping(ui->obfuscationRounds, OptionsModel::ObfuscationRounds); mapper->addMapping(ui->anonymizeDarknet, OptionsModel::AnonymizeDarknetAmount); + mapper->addMapping(ui->showMasternodesTab, OptionsModel::ShowMasternodesTab); } diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index cfdbd9c2a03eb..0b8fee6ce6eb6 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -20,6 +20,7 @@ #include "txdb.h" // for -dbcache defaults #ifdef ENABLE_WALLET +#include "masternodeconfig.h" #include "wallet.h" #include "walletdb.h" #endif @@ -81,6 +82,9 @@ void OptionsModel::Init() nObfuscationRounds = settings.value("nObfuscationRounds").toLongLong(); nAnonymizeDarknetAmount = settings.value("nAnonymizeDarknetAmount").toLongLong(); + if (!settings.contains("fShowMasternodesTab")) + settings.setValue("fShowMasternodesTab", masternodeConfig.getCount()); + // These are shared with the core or have a command-line parameter // and we want command-line parameters to overwrite the GUI settings. // @@ -203,6 +207,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const #ifdef ENABLE_WALLET case SpendZeroConfChange: return settings.value("bSpendZeroConfChange"); + case ShowMasternodesTab: + return settings.value("fShowMasternodesTab"); #endif case DisplayUnit: return nDisplayUnit; @@ -296,6 +302,12 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in setRestartRequired(true); } break; + case ShowMasternodesTab: + if (settings.value("fShowMasternodesTab") != value) { + settings.setValue("fShowMasternodesTab", value); + setRestartRequired(true); + } + break; #endif case DisplayUnit: setDisplayUnit(value); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 91faacd9ed0f1..07497b8fa3e4a 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -45,6 +45,7 @@ class OptionsModel : public QAbstractListModel SpendZeroConfChange, // bool ObfuscationRounds, // int AnonymizeDarknetAmount, //int + ShowMasternodesTab, // bool Listen, // bool OptionIDRowCount, }; diff --git a/src/qt/platformstyle.cpp b/src/qt/platformstyle.cpp new file mode 100644 index 0000000000000..16b79c939908a --- /dev/null +++ b/src/qt/platformstyle.cpp @@ -0,0 +1,147 @@ +// Copyright (c) 2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "platformstyle.h" + +#include "guiconstants.h" + +#include +#include +#include +#include +#include +#include + +static const struct { + const char *platformId; + /** Show images on push buttons */ + const bool imagesOnButtons; + /** Colorize single-color icons */ + const bool colorizeIcons; + /** Extra padding/spacing in transactionview */ + const bool useExtraSpacing; +} platform_styles[] = { + {"macosx", false, false, true}, + {"windows", true, false, false}, + /* Other: linux, unix, ... */ + {"other", true, false, false} +}; +static const unsigned platform_styles_count = sizeof(platform_styles)/sizeof(*platform_styles); + +namespace { +/* Local functions for colorizing single-color images */ + +void MakeSingleColorImage(QImage& img, const QColor& colorbase) +{ + img = img.convertToFormat(QImage::Format_ARGB32); + for (int x = img.width(); x--; ) + { + for (int y = img.height(); y--; ) + { + const QRgb rgb = img.pixel(x, y); + img.setPixel(x, y, qRgba(colorbase.red(), colorbase.green(), colorbase.blue(), qAlpha(rgb))); + } + } +} + +QIcon ColorizeIcon(const QIcon& ico, const QColor& colorbase) +{ + QIcon new_ico; + QSize sz; + Q_FOREACH(sz, ico.availableSizes()) + { + QImage img(ico.pixmap(sz).toImage()); + MakeSingleColorImage(img, colorbase); + new_ico.addPixmap(QPixmap::fromImage(img)); + } + return new_ico; +} + +QImage ColorizeImage(const QString& filename, const QColor& colorbase) +{ + QImage img(filename); + MakeSingleColorImage(img, colorbase); + return img; +} + +QIcon ColorizeIcon(const QString& filename, const QColor& colorbase) +{ + return QIcon(QPixmap::fromImage(ColorizeImage(filename, colorbase))); +} + +} + + +PlatformStyle::PlatformStyle(const QString &name, bool imagesOnButtons, bool colorizeIcons, bool useExtraSpacing): + name(name), + imagesOnButtons(imagesOnButtons), + colorizeIcons(colorizeIcons), + useExtraSpacing(useExtraSpacing), + singleColor(0,0,0), + textColor(0,0,0) +{ + // Determine icon highlighting color + if (colorizeIcons) { + const QColor colorHighlightBg(QApplication::palette().color(QPalette::Highlight)); + const QColor colorHighlightFg(QApplication::palette().color(QPalette::HighlightedText)); + const QColor colorText(QApplication::palette().color(QPalette::WindowText)); + const int colorTextLightness = colorText.lightness(); + QColor colorbase; + if (abs(colorHighlightBg.lightness() - colorTextLightness) < abs(colorHighlightFg.lightness() - colorTextLightness)) + colorbase = colorHighlightBg; + else + colorbase = colorHighlightFg; + singleColor = colorbase; + } + // Determine text color + textColor = QColor(QApplication::palette().color(QPalette::WindowText)); +} + +QImage PlatformStyle::SingleColorImage(const QString& filename) const +{ + if (!colorizeIcons) + return QImage(filename); + return ColorizeImage(filename, SingleColor()); +} + +QIcon PlatformStyle::SingleColorIcon(const QString& filename) const +{ + if (!colorizeIcons) + return QIcon(filename); + return ColorizeIcon(filename, SingleColor()); +} + +QIcon PlatformStyle::SingleColorIcon(const QIcon& icon) const +{ + if (!colorizeIcons) + return icon; + return ColorizeIcon(icon, SingleColor()); +} + +QIcon PlatformStyle::TextColorIcon(const QString& filename) const +{ + return ColorizeIcon(filename, TextColor()); +} + +QIcon PlatformStyle::TextColorIcon(const QIcon& icon) const +{ + return ColorizeIcon(icon, TextColor()); +} + +const PlatformStyle *PlatformStyle::instantiate(const QString &platformId) +{ + for (unsigned x=0; x +#include +#include + +/* Coin network-specific GUI style information */ +class PlatformStyle +{ +public: + /** Get style associated with provided platform name, or 0 if not known */ + static const PlatformStyle *instantiate(const QString &platformId); + + const QString &getName() const { return name; } + + bool getImagesOnButtons() const { return imagesOnButtons; } + bool getUseExtraSpacing() const { return useExtraSpacing; } + + QColor TextColor() const { return textColor; } + QColor SingleColor() const { return singleColor; } + + /** Colorize an image (given filename) with the icon color */ + QImage SingleColorImage(const QString& filename) const; + + /** Colorize an icon (given filename) with the icon color */ + QIcon SingleColorIcon(const QString& filename) const; + + /** Colorize an icon (given object) with the icon color */ + QIcon SingleColorIcon(const QIcon& icon) const; + + /** Colorize an icon (given filename) with the text color */ + QIcon TextColorIcon(const QString& filename) const; + + /** Colorize an icon (given object) with the text color */ + QIcon TextColorIcon(const QIcon& icon) const; + +private: + PlatformStyle(const QString &name, bool imagesOnButtons, bool colorizeIcons, bool useExtraSpacing); + + QString name; + bool imagesOnButtons; + bool colorizeIcons; + bool useExtraSpacing; + QColor singleColor; + QColor textColor; + /* ... more to come later */ +}; + +#endif // BITCOIN_QT_PLATFORMSTYLE_H + diff --git a/src/qt/res/icons/masternodes.png b/src/qt/res/icons/masternodes.png new file mode 100644 index 0000000000000..f1f653969bdf6 Binary files /dev/null and b/src/qt/res/icons/masternodes.png differ diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index d2b1faaf67e9b..48c6aef80a5e6 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -119,6 +119,13 @@ void WalletFrame::gotoHistoryPage() i.value()->gotoHistoryPage(); } +void WalletFrame::gotoMasternodePage() +{ + QMap::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoMasternodePage(); +} + void WalletFrame::gotoReceiveCoinsPage() { QMap::const_iterator i; diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h index 57b377b9efefc..c31eedb7c7a2d 100644 --- a/src/qt/walletframe.h +++ b/src/qt/walletframe.h @@ -52,6 +52,8 @@ public slots: void gotoOverviewPage(); /** Switch to history (transactions) page */ void gotoHistoryPage(); + /** Switch to masternode page */ + void gotoMasternodePage(); /** Switch to receive coins page */ void gotoReceiveCoinsPage(); /** Switch to send coins page */ diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index a16da688a8564..886137bb504ad 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -193,7 +193,7 @@ class WalletModel : public QObject void CopyFrom(const UnlockContext& rhs); }; - UnlockContext requestUnlock(bool relock); + UnlockContext requestUnlock(bool relock=false); bool getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; void getOutputs(const std::vector& vOutpoints, std::vector& vOutputs); diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 652385958b37b..c2e2109887193 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -9,6 +9,7 @@ #include "bitcoingui.h" #include "clientmodel.h" #include "guiutil.h" +#include "masternodeconfig.h" #include "optionsmodel.h" #include "overviewpage.h" #include "receivecoinsdialog.h" @@ -27,6 +28,7 @@ #include #include #include +#include #include WalletView::WalletView(QWidget *parent): @@ -73,6 +75,12 @@ WalletView::WalletView(QWidget *parent): addWidget(receiveCoinsPage); addWidget(sendCoinsPage); + QSettings settings; + if (settings.value("fShowMasternodesTab").toBool()) { + masternodeListPage = new MasternodeList(); + addWidget(masternodeListPage); + } + // Clicking on a transaction on the overview pre-selects the transaction on the transaction history page connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), transactionView, SLOT(focusTransaction(QModelIndex))); @@ -87,6 +95,7 @@ WalletView::WalletView(QWidget *parent): // Pass through messages from sendCoinsPage connect(sendCoinsPage, SIGNAL(message(QString,QString,unsigned int)), this, SIGNAL(message(QString,QString,unsigned int))); + // Pass through messages from transactionView connect(transactionView, SIGNAL(message(QString,QString,unsigned int)), this, SIGNAL(message(QString,QString,unsigned int))); } @@ -119,6 +128,10 @@ void WalletView::setClientModel(ClientModel *clientModel) overviewPage->setClientModel(clientModel); sendCoinsPage->setClientModel(clientModel); + QSettings settings; + if (settings.value("fShowMasternodesTab").toBool()) { + masternodeListPage->setClientModel(clientModel); + } } void WalletView::setWalletModel(WalletModel *walletModel) @@ -128,6 +141,10 @@ void WalletView::setWalletModel(WalletModel *walletModel) // Put transaction list in tabs transactionView->setModel(walletModel); overviewPage->setWalletModel(walletModel); + QSettings settings; + if (settings.value("fShowMasternodesTab").toBool()) { + masternodeListPage->setWalletModel(walletModel); + } receiveCoinsPage->setModel(walletModel); sendCoinsPage->setModel(walletModel); @@ -180,6 +197,14 @@ void WalletView::gotoHistoryPage() setCurrentWidget(transactionsPage); } +void WalletView::gotoMasternodePage() +{ + QSettings settings; + if (settings.value("fShowMasternodesTab").toBool()) { + setCurrentWidget(masternodeListPage); + } +} + void WalletView::gotoReceiveCoinsPage() { setCurrentWidget(receiveCoinsPage); diff --git a/src/qt/walletview.h b/src/qt/walletview.h index 691875396bc03..c6a7b78b1e657 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -6,6 +6,7 @@ #define BITCOIN_QT_WALLETVIEW_H #include "amount.h" +#include "masternodelist.h" #include @@ -61,6 +62,7 @@ class WalletView : public QStackedWidget QWidget *transactionsPage; ReceiveCoinsDialog *receiveCoinsPage; SendCoinsDialog *sendCoinsPage; + MasternodeList *masternodeListPage; TransactionView *transactionView; @@ -72,6 +74,8 @@ public slots: void gotoOverviewPage(); /** Switch to history (transactions) page */ void gotoHistoryPage(); + /** Switch to masternode page */ + void gotoMasternodePage(); /** Switch to receive coins page */ void gotoReceiveCoinsPage(); /** Switch to send coins page */ diff --git a/src/uint256.h b/src/uint256.h index 0004bb4b7d269..b69fb0a9ac341 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -339,6 +339,27 @@ class uint256 : public base_uint<256> { uint64_t GetHash(const uint256& salt) const; }; +/* uint256 from const char *. + * This is a separate function because the constructor uint256(const char*) can result + * in dangerously catching uint256(0). + */ +inline uint256 uint256S(const char *str) +{ + uint256 rv; + rv.SetHex(str); + return rv; +} +/* uint256 from std::string. + * This is a separate function because the constructor uint256(const std::string &str) can result + * in dangerously catching uint256(0) via std::string(const char*). + */ +inline uint256 uint256S(const std::string& str) +{ + uint256 rv; + rv.SetHex(str); + return rv; +} + /** 512-bit unsigned big integer. */ class uint512 : public base_uint<512> { public: diff --git a/src/utiltime.cpp b/src/utiltime.cpp index 8380c3a6ae34c..f5f9430e05f4f 100644 --- a/src/utiltime.cpp +++ b/src/utiltime.cpp @@ -7,6 +7,7 @@ #include "config/darknet-config.h" #endif +#include "tinyformat.h" #include "utiltime.h" #include @@ -67,3 +68,18 @@ std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime) ss << boost::posix_time::from_time_t(nTime); return ss.str(); } + +std::string DurationToDHMS(int64_t nDurationTime) +{ + int seconds = nDurationTime % 60; + nDurationTime /= 60; + int minutes = nDurationTime % 60; + nDurationTime /= 60; + int hours = nDurationTime % 24; + int days = nDurationTime / 24; + if(days) + return strprintf("%dd %02dh:%02dm:%02ds", days, hours, minutes, seconds); + if(hours) + return strprintf("%02dh:%02dm:%02ds", hours, minutes, seconds); + return strprintf("%02dm:%02ds", minutes, seconds); +} diff --git a/src/utiltime.h b/src/utiltime.h index 9d7d42fe473b8..3f5ced6a56f0d 100644 --- a/src/utiltime.h +++ b/src/utiltime.h @@ -16,5 +16,6 @@ void SetMockTime(int64_t nMockTimeIn); void MilliSleep(int64_t n); std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime); +std::string DurationToDHMS(int64_t nDurationTime); #endif // BITCOIN_UTILTIME_H diff --git a/src/wallet.cpp b/src/wallet.cpp index a458352fe0359..e48275e4170d6 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -478,6 +478,63 @@ void CWallet::AddToSpends(const uint256& wtxid) AddToSpends(txin.prevout, wtxid); } +bool CWallet::GetMasternodeVinAndKeys(CTxIn& txinRet, CPubKey& pubKeyRet, CKey& keyRet, std::string strTxHash, std::string strOutputIndex) +{ + // wait for reindex and/or import to finish + if (fImporting || fReindex) return false; + + // Find possible candidates + std::vector vPossibleCoins; + AvailableCoins(vPossibleCoins, true, NULL, false, ONLY_10000); + if(vPossibleCoins.empty()) { + LogPrintf("CWallet::GetMasternodeVinAndKeys -- Could not locate any valid masternode vin\n"); + return false; + } + + if(strTxHash.empty()) // No output specified, select the first one + return GetVinAndKeysFromOutput(vPossibleCoins[0], txinRet, pubKeyRet, keyRet); + + // Find specific vin + uint256 txHash = uint256S(strTxHash); + int nOutputIndex = atoi(strOutputIndex.c_str()); + + BOOST_FOREACH(COutput& out, vPossibleCoins) + if(out.tx->GetHash() == txHash && out.i == nOutputIndex) // found it! + return GetVinAndKeysFromOutput(out, txinRet, pubKeyRet, keyRet); + + LogPrintf("CWallet::GetMasternodeVinAndKeys -- Could not locate specified masternode vin\n"); + return false; +} + +bool CWallet::GetVinAndKeysFromOutput(COutput out, CTxIn& txinRet, CPubKey& pubKeyRet, CKey& keyRet) +{ + // wait for reindex and/or import to finish + if (fImporting || fReindex) return false; + + CScript pubScript; + + txinRet = CTxIn(out.tx->GetHash(), out.i); + pubScript = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey + + CTxDestination address1; + ExtractDestination(pubScript, address1); + CBitcoinAddress address2(address1); + + CKeyID keyID; + if (!address2.GetKeyID(keyID)) { + LogPrintf("CWallet::GetVinAndKeysFromOutput -- Address does not refer to a key\n"); + return false; + } + + if (!GetKey(keyID, keyRet)) { + LogPrintf ("CWallet::GetVinAndKeysFromOutput -- Private key for address is not known\n"); + return false; + } + + pubKeyRet = keyRet.GetPubKey(); + return true; +} + bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) { if (IsCrypted()) @@ -1192,6 +1249,15 @@ void CWallet::ReacceptWalletTransactions() } } +bool CWalletTx::InMempool() const +{ + LOCK(mempool.cs); + if (mempool.exists(GetHash())) { + return true; + } + return false; +} + void CWalletTx::RelayWalletTransaction(std::string strCommand) { if (!IsCoinBase()) @@ -1488,7 +1554,7 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const /** * populate vCoins with vector of available COutputs. */ -void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, AvailableCoinsType coin_type, bool useIX) const +void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue, AvailableCoinsType nCoinType, bool fUseIX) const { vCoins.clear(); @@ -1499,30 +1565,37 @@ void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const const uint256& wtxid = it->first; const CWalletTx* pcoin = &(*it).second; - if (!IsFinalTx(*pcoin)) + if (!CheckFinalTx(*pcoin)) continue; if (fOnlyConfirmed && !pcoin->IsTrusted()) continue; - if ((pcoin->IsCoinBase() || pcoin->IsCoinStake()) && pcoin->GetBlocksToMaturity() > 0) + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) continue; int nDepth = pcoin->GetDepthInMainChain(false); // do not use IX for inputs that have less then 6 blockchain confirmations - if (useIX && nDepth < 6) + if (fUseIX && nDepth < 6) + continue; + + // We should not consider coins which aren't at least in our mempool + // It's possible for these to be conflicted via ancestors which we may never be able to detect + if (nDepth == 0 && !pcoin->InMempool()) continue; for (unsigned int i = 0; i < pcoin->vout.size(); i++) { bool found = false; - if(coin_type == ONLY_DENOMINATED) { + if(nCoinType == ONLY_DENOMINATED) { found = IsDenominatedAmount(pcoin->vout[i].nValue); - } else if(coin_type == ONLY_NOT10000IFMN) { + } else if(nCoinType == ONLY_NOT10000IFMN) { found = !(fMasterNode && pcoin->vout[i].nValue == 10000*COIN); - } else if(coin_type == ONLY_NONDENOMINATED_NOT10000IFMN) { + } else if(nCoinType == ONLY_NONDENOMINATED_NOT10000IFMN) { if (IsCollateralAmount(pcoin->vout[i].nValue)) continue; // do not use collateral amounts found = !IsDenominatedAmount(pcoin->vout[i].nValue); if(found && fMasterNode) found = pcoin->vout[i].nValue != 10000*COIN; // do not use Hot MN funds + } else if(nCoinType == ONLY_10000) { + found = pcoin->vout[i].nValue == 10000*COIN; } else { found = true; } @@ -1530,9 +1603,12 @@ void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const isminetype mine = IsMine(pcoin->vout[i]); if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && - !IsLockedCoin((*it).first, i) && pcoin->vout[i].nValue > 0 && - (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i))) - vCoins.push_back(COutput(pcoin, i, nDepth, (mine & ISMINE_SPENDABLE) != ISMINE_NO)); + (!IsLockedCoin((*it).first, i) || nCoinType == ONLY_10000) && + (pcoin->vout[i].nValue > 0 || fIncludeZeroValue) && + (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected((*it).first, i))) + vCoins.push_back(COutput(pcoin, i, nDepth, + ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || + (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO))); } } } @@ -1777,7 +1853,7 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set vCoins; - AvailableCoins(vCoins, true, coinControl, coin_type, useIX); + AvailableCoins(vCoins, true, coinControl, false, coin_type, useIX); // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) if (coinControl && coinControl->HasSelected()) diff --git a/src/wallet.h b/src/wallet.h index d51a70830a05c..f9b2c4f773c5c 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -9,6 +9,7 @@ #define BITCOIN_WALLET_H #include "amount.h" +#include "base58.h" #include "primitives/block.h" #include "primitives/transaction.h" #include "crypter.h" @@ -74,9 +75,20 @@ enum AvailableCoinsType ALL_COINS = 1, ONLY_DENOMINATED = 2, ONLY_NOT10000IFMN = 3, - ONLY_NONDENOMINATED_NOT10000IFMN = 4 // ONLY_NONDENOMINATED and not 10000DNET at the same time + ONLY_NONDENOMINATED_NOT10000IFMN = 4, // ONLY_NONDENOMINATED and not 10000DNET at the same time + ONLY_10000 = 5 // find masternode outputs including locked ones (use with caution) }; +struct CompactTallyItem +{ + CBitcoinAddress address; + CAmount nAmount; + std::vector vecTxIn; + CompactTallyItem() + { + nAmount = 0; + } +}; /** A key pool entry */ class CKeyPool @@ -244,9 +256,14 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface //! 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, AvailableCoinsType coin_type=ALL_COINS, bool useIX = false) const; + void AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl = NULL, bool fIncludeZeroValue=false, AvailableCoinsType nCoinType=ALL_COINS, bool fUseIX = false) const; bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, std::vector vCoins, std::set >& setCoinsRet, CAmount& nValueRet) const; + /// Get 1000DASH output and keys which can be used for the Masternode + bool GetMasternodeVinAndKeys(CTxIn& txinRet, CPubKey& pubKeyRet, CKey& keyRet, std::string strTxHash = "", std::string strOutputIndex = ""); + /// Extract txin information and keys from output + bool GetVinAndKeysFromOutput(COutput out, CTxIn& txinRet, CPubKey& pubKeyRet, CKey& keyRet); + bool IsSpent(const uint256& hash, unsigned int n) const; bool IsLockedCoin(uint256 hash, unsigned int n) const; @@ -1073,7 +1090,9 @@ class CWalletTx : public CMerkleTx { return (GetDebit(filter) > 0); } - + + bool InMempool() const; + bool IsTrusted() const { // Quick answer in most cases diff --git a/src/wallet_ismine.h b/src/wallet_ismine.h index 5f0c0c1a012f3..c6536227d7e37 100644 --- a/src/wallet_ismine.h +++ b/src/wallet_ismine.h @@ -16,8 +16,12 @@ class CScript; enum isminetype { ISMINE_NO = 0, - ISMINE_WATCH_ONLY = 1, - ISMINE_SPENDABLE = 2, + //! Indicates that we dont know how to create a scriptSig that would solve this if we were given the appropriate private keys + ISMINE_WATCH_UNSOLVABLE = 1, + //! Indicates that we know how to create a scriptSig that would solve this if we were given the appropriate private keys + ISMINE_WATCH_SOLVABLE = 2, + ISMINE_WATCH_ONLY = ISMINE_WATCH_SOLVABLE | ISMINE_WATCH_UNSOLVABLE, + ISMINE_SPENDABLE = 4, ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE }; /** used for bitflags of isminetype */