From f34ec86f55876bb9e2de39d6bad878d881f4a7b9 Mon Sep 17 00:00:00 2001 From: "snguyen.ntu@gmail.com" Date: Wed, 25 Oct 2017 18:20:18 +0700 Subject: [PATCH 01/35] Reformat the code. Set constants. Implemented RPC listpubcoins (return all pubcoins) --- contrib/seeds/nodes_test.txt | 2 ++ src/consensus/consensus.h | 17 +++++++++++++ src/libzerocoin/Coin.cpp | 18 +++++++------- src/main.cpp | 7 +----- src/main.h | 1 - src/miner.cpp | 24 ++++++++++--------- src/pow.cpp | 23 ++++++++++++++---- src/primitives/block.cpp | 14 +++++++---- src/primitives/precomputed_hash.h | 2 +- src/qt/transactionview.cpp | 26 ++++++++++++++++++++ src/qt/transactionview.h | 2 ++ src/qt/walletmodel.cpp | 39 ++++++++++++++++++++++++++++++ src/qt/walletmodel.h | 3 +++ src/rpc/client.cpp | 1 + src/rpc/mining.cpp | 2 +- src/wallet/rpcwallet.cpp | 40 +++++++++++++++++++++++++++++++ src/wallet/wallet.cpp | 1 + src/wallet/wallet.h | 1 + 18 files changed, 185 insertions(+), 38 deletions(-) diff --git a/contrib/seeds/nodes_test.txt b/contrib/seeds/nodes_test.txt index ca10ba501f..172c5f86c2 100644 --- a/contrib/seeds/nodes_test.txt +++ b/contrib/seeds/nodes_test.txt @@ -1,5 +1,7 @@ # List of fixed seed nodes for testnet 52.175.244.22:28168 +52.232.105.188:28168 +13.76.210.1:28168 # Onion nodes diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index 520cab5714..23ee925dc0 100644 --- a/src/consensus/consensus.h +++ b/src/consensus/consensus.h @@ -1,5 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2016-2017 The Zcoin developers + // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -8,6 +10,21 @@ #include +// HF constants +static const int HF_LYRA2VAR_HEIGHT = 500; +static const int HF_LYRA2_HEIGHT = 8192; +static const int HF_LYRA2Z_HEIGHT = 20500; +static const int HF_ZNODE_HEIGHT = 55000; +//static const int HF_MTP_HEIGHT = 60000; + +static const int HF_LYRA2VAR_HEIGHT_TESTNET = 10; +static const int HF_LYRA2_HEIGHT_TESTNET = 25; // for consistent purpose since the algo hash is so low +static const int HF_LYRA2Z_HEIGHT_TESTNET = 30; +static const int HF_ZNODE_HEIGHT_TESTNET = 1000; +//static const int HF_MTP_HEIGHT_TESTNET = 30; + +static const int HF_ZEROSPEND_FIX = 22000; + /** The maximum allowed size for a serialized block, in bytes (only for buffer size limits) */ static const unsigned int MAX_BLOCK_SERIALIZED_SIZE = 2000000; /** The maximum allowed weight for a block, see BIP 141 (network rule) */ diff --git a/src/libzerocoin/Coin.cpp b/src/libzerocoin/Coin.cpp index 9b93294ed1..07bbcbffab 100755 --- a/src/libzerocoin/Coin.cpp +++ b/src/libzerocoin/Coin.cpp @@ -66,7 +66,7 @@ PrivateCoin::PrivateCoin(const Params* p, const CoinDenomination denomination): // Mint a new coin with a random serial number using the standard process. this->mintCoin(denomination); #endif - + } /** @@ -115,18 +115,18 @@ void PrivateCoin::mintCoin(const CoinDenomination denomination) { } void PrivateCoin::mintCoinFast(const CoinDenomination denomination) { - + // Generate a random serial number in the range 0...{q-1} where // "q" is the order of the commitment group. Bignum s = Bignum::randBignum(this->params->coinCommitmentGroup.groupOrder); - + // Generate a random number "r" in the range 0...{q-1} Bignum r = Bignum::randBignum(this->params->coinCommitmentGroup.groupOrder); - + // Manually compute a Pedersen commitment to the serial number "s" under randomness "r" // C = g^s * h^r mod p Bignum commitmentValue = this->params->coinCommitmentGroup.g.pow_mod(s, this->params->coinCommitmentGroup.modulus).mul_mod(this->params->coinCommitmentGroup.h.pow_mod(r, this->params->coinCommitmentGroup.modulus), this->params->coinCommitmentGroup.modulus); - + // Repeat this process up to MAX_COINMINT_ATTEMPTS times until // we obtain a prime number for (uint32_t attempt = 0; attempt < MAX_COINMINT_ATTEMPTS; attempt++) { @@ -140,11 +140,11 @@ void PrivateCoin::mintCoinFast(const CoinDenomination denomination) { this->serialNumber = s; this->randomness = r; this->publicCoin = PublicCoin(params, commitmentValue, denomination); - + // Success! We're done. return; } - + // Generate a new random "r_delta" in 0...{q-1} Bignum r_delta = Bignum::randBignum(this->params->coinCommitmentGroup.groupOrder); @@ -154,12 +154,12 @@ void PrivateCoin::mintCoinFast(const CoinDenomination denomination) { r = (r + r_delta) % this->params->coinCommitmentGroup.groupOrder; commitmentValue = commitmentValue.mul_mod(this->params->coinCommitmentGroup.h.pow_mod(r_delta, this->params->coinCommitmentGroup.modulus), this->params->coinCommitmentGroup.modulus); } - + // We only get here if we did not find a coin within // MAX_COINMINT_ATTEMPTS. Throw an exception. throw ZerocoinException("Unable to mint a new Zerocoin (too many attempts)"); } - + const PublicCoin& PrivateCoin::getPublicCoin() const { return this->publicCoin; } diff --git a/src/main.cpp b/src/main.cpp index 203eed32d3..f34db130d4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -824,7 +824,6 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) { if (tx.nLockTime == 0) return true; if ((int64_t) tx.nLockTime < ((int64_t) tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t) nBlockHeight : nBlockTime)) { - LogPrintf("IsFinalTx tx=%s --> OK\n", tx.GetHash().ToString()); return true; } BOOST_FOREACH(const CTxIn &txin, tx.vin) { @@ -1260,7 +1259,7 @@ bool CheckSpendZcoinTransaction(const CTransaction &tx, CZerocoinEntry pubCoinTx zccoinSpend.hashTx = hashTx; zccoinSpend.pubCoin = 0; zccoinSpend.id = pubcoinId; - if (nHeight > 22000 && nHeight < INT_MAX) { + if (nHeight > HF_ZEROSPEND_FIX && nHeight < INT_MAX) { zccoinSpend.denomination = targetDenomination; } // LogPrintf("WriteCoinSpendSerialEntry, serialNumber=%s", serialNumber.ToString()); @@ -2958,10 +2957,6 @@ bool ConnectBlock(const CBlock &block, CValidationState &state, CBlockIndex *pin (unsigned) block.vtx.size(), 0.001 * (nTime3 - nTime2), 0.001 * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime3 - nTime2) / (nInputs - 1), nTimeConnect * 0.000001); //btzc: Add time to check - LogPrintf("block=%s\n", block.ToString()); - LogPrintf("nFees=%s\n", nFees); - LogPrintf("GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus(), pindex->nTime)=%s\n", GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus(), pindex->nTime)); - LogPrintf("block.vtx[0].GetValueOut()=%s\n", block.vtx[0].GetValueOut()); CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus(), pindex->nTime); if (block.vtx[0].GetValueOut() > blockReward) return state.DoS(100, error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)", diff --git a/src/main.h b/src/main.h index f65af92bfb..f182704b4d 100644 --- a/src/main.h +++ b/src/main.h @@ -163,7 +163,6 @@ static std::map mapBlockData; static const bool DEFAULT_PEERBLOOMFILTERS = true; - struct BlockHasher { size_t operator()(const uint256& hash) const { return hash.GetCheapHash(); } diff --git a/src/miner.cpp b/src/miner.cpp index 2b9dd8b262..e27905b4b3 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -133,6 +133,7 @@ CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn) // Create new block LogPrintf("BlockAssembler::CreateNewBlock()\n"); bool fTestNet = (Params().NetworkIDString() == CBaseChainParams::TESTNET); + resetBlock(); auto_ptr pblocktemplate(new CBlockTemplate()); if(!pblocktemplate.get()) return NULL; @@ -208,7 +209,7 @@ CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn) unsigned int COUNT_SPEND_ZC_TX = 0; unsigned int MAX_SPEND_ZC_TX_PER_BLOCK = 0; - if(fTestNet || nHeight > 22000){ + if(fTestNet || nHeight > HF_ZEROSPEND_FIX){ MAX_SPEND_ZC_TX_PER_BLOCK = 1; } @@ -939,7 +940,7 @@ void BlockAssembler::addPriorityTxs() unsigned int COUNT_SPEND_ZC_TX = 0; unsigned int MAX_SPEND_ZC_TX_PER_BLOCK = 0; - if (nHeight + 1 > 22000) { + if (nHeight + 1 > HF_ZEROSPEND_FIX) { MAX_SPEND_ZC_TX_PER_BLOCK = 1; } @@ -1176,18 +1177,19 @@ void static ZcoinMiner(const CChainParams &chainparams) { uint256 thash; while (true) { - if ((!fTestNet && pindexPrev->nHeight + 1 >= 20500)) { + if ((!fTestNet && pindexPrev->nHeight + 1 >= HF_LYRA2Z_HEIGHT)) { lyra2z_hash(BEGIN(pblock->nVersion), BEGIN(thash)); - } else if (!fTestNet && pindexPrev->nHeight + 1 >= 8192) { + } else if (!fTestNet && pindexPrev->nHeight + 1 >= HF_LYRA2_HEIGHT) { LYRA2(BEGIN(thash), 32, BEGIN(pblock->nVersion), 80, BEGIN(pblock->nVersion), 80, 2, 8192, 256); - } else if (!fTestNet && pindexPrev->nHeight + 1 >= 500) { - LYRA2(BEGIN(thash), 32, BEGIN(pblock->nVersion), 80, BEGIN(pblock->nVersion), 80, 2, - pindexPrev->nHeight + 1, 256); - } else if (fTestNet && pindexPrev->nHeight + 1 >= 90) { // testnet + } else if (!fTestNet && pindexPrev->nHeight + 1 >= HF_LYRA2VAR_HEIGHT) { + LYRA2(BEGIN(thash), 32, BEGIN(pblock->nVersion), 80, BEGIN(pblock->nVersion), 80, 2, pindexPrev->nHeight + 1, 256); + } else if (fTestNet && pindexPrev->nHeight + 1 >= HF_LYRA2Z_HEIGHT_TESTNET) { // testnet lyra2z_hash(BEGIN(pblock->nVersion), BEGIN(thash)); - } else if (fTestNet && pindexPrev->nHeight + 1 >= 80) { // testnet - LYRA2(BEGIN(thash), 32, BEGIN(pblock->nVersion), 80, BEGIN(pblock->nVersion), 80, 2, 8192, - 256); + } else if (fTestNet && pindexPrev->nHeight + 1 >= HF_LYRA2_HEIGHT_TESTNET) { // testnet + LYRA2(BEGIN(thash), 32, BEGIN(pblock->nVersion), 80, BEGIN(pblock->nVersion), 80, 2, 8192, 256); + } else if (fTestNet && pindexPrev->nHeight + 1 >= HF_LYRA2VAR_HEIGHT_TESTNET) { // testnet + LYRA2(BEGIN(thash), 32, BEGIN(pblock->nVersion), 80, BEGIN(pblock->nVersion), 80, 2, pindexPrev->nHeight + 1, 256); + } else { unsigned long int scrypt_scratpad_size_current_block = ((1 << (GetNfactor(pblock->nTime) + 1)) * 128) + 63; diff --git a/src/pow.cpp b/src/pow.cpp index 4a4dc282a3..a53599458f 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -14,6 +14,7 @@ #include "chainparams.h" #include "libzerocoin/bitcoin_bignum/bignum.h" #include "fixed.h" +#include "consensus/consensus.h" static CBigNum bnProofOfWorkLimit(~arith_uint256(0) >> 8); @@ -56,16 +57,23 @@ unsigned int GetNextWorkRequired(const CBlockIndex *pindexLast, const CBlockHead // 9/29/2016 - Reset to Lyra2(2,block_height,256) due to ASIC KnC Miner Scrypt // 36 block look back, reset to mininmum diff - if (!fTestNet && pindexLast->nHeight + 1 >= 500 && pindexLast->nHeight + 1 <= 535) { + if (!fTestNet && pindexLast->nHeight + 1 >= HF_LYRA2VAR_HEIGHT && pindexLast->nHeight + 1 <= (HF_LYRA2VAR_HEIGHT + 35)) { return bnProofOfWorkLimit.GetCompact(); } // reset to minimum diff at testnet after scrypt_n, 6 block look back - if (fTestNet && pindexLast->nHeight + 1 >= 80 && pindexLast->nHeight + 1 <= 85) { + if (fTestNet && pindexLast->nHeight + 1 >= HF_LYRA2VAR_HEIGHT_TESTNET && pindexLast->nHeight + 1 <= (HF_LYRA2VAR_HEIGHT_TESTNET + 5)) { return bnProofOfWorkLimit.GetCompact(); } + // reset to minimum diff at testnet for lyra2 with matrix height = 8192 until lyra2z + if(fTestNet && pindexLast->nHeight + 1 >= HF_LYRA2_HEIGHT_TESTNET && pindexLast->nHeight + 1 <= (HF_LYRA2Z_HEIGHT_TESTNET - 1)){ + printf("Lyra2 with 8192 matrix height.\n"); + return bnProofOfWorkLimit.GetCompact(); + } + + // 02/11/2017 - Increase diff to match with new hashrates of Lyra2Z algo - if ((!fTestNet && pindexLast->nHeight + 1 == 20500) || (fTestNet && pindexLast->nHeight + 1 == 90)) { + if ((!fTestNet && pindexLast->nHeight + 1 == HF_LYRA2Z_HEIGHT) || (fTestNet && pindexLast->nHeight + 1 == HF_LYRA2Z_HEIGHT_TESTNET)) { CBigNum bnNew; bnNew.SetCompact(pindexLast->nBits); bnNew /= 20000; // increase the diff by 20000x since the new hashrate is approx. 20000 times higher @@ -75,6 +83,12 @@ unsigned int GetNextWorkRequired(const CBlockIndex *pindexLast, const CBlockHead return bnNew.GetCompact(); } + // 04/09/2017 - Reset diff on testnet for MTP, 6 blocks look back + /* + if(fTestNet && pindexLast->nHeight + 1 >= HF_MTP_HEIGHT_TESTNET && pindexLast->nHeight + 1 <= HF_MTP_HEIGHT_TESTNET + 5) { + return bnProofOfWorkLimit.GetCompact(); + }*/ + if ((pindexLast->nHeight + 1) % params.DifficultyAdjustmentInterval() != 0) // Retarget every nInterval blocks { return pindexLast->nBits; @@ -83,6 +97,7 @@ unsigned int GetNextWorkRequired(const CBlockIndex *pindexLast, const CBlockHead return BorisRidiculouslyNamedDifficultyFunction(pindexLast, BlocksTargetSpacing, PastBlocksMin, PastBlocksMax); } +/* unsigned int GetNextWorkRequired_Bitcoin(const CBlockIndex *pindexLast, const CBlockHeader *pblock, const Consensus::Params ¶ms) { unsigned int nProofOfWorkLimit = UintToArith256(params.powLimit).GetCompact(); @@ -118,7 +133,7 @@ unsigned int GetNextWorkRequired_Bitcoin(const CBlockIndex *pindexLast, const CB return CalculateNextWorkRequired(pindexLast, pindexFirst->GetBlockTime(), params); } - +*/ unsigned int CalculateNextWorkRequired(const CBlockIndex *pindexLast, int64_t nFirstBlockTime, const Consensus::Params ¶ms) { if (params.fPowNoRetargeting) return pindexLast->nBits; diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index 12a736f641..28bfda5254 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -4,6 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "primitives/block.h" +#include "consensus/consensus.h" #include "hash.h" #include "tinyformat.h" @@ -22,6 +23,7 @@ #include "precomputed_hash.h" + unsigned char GetNfactor(int64_t nTimestamp) { int l = 0; if (nTimestamp <= Params().GetConsensus().nChainStartTime) @@ -65,16 +67,18 @@ uint256 CBlockHeader::GetPoWHash(int nHeight) const { } uint256 powHash; try { - if (!fTestNet && nHeight >= 20500) { + if (!fTestNet && nHeight >= HF_LYRA2Z_HEIGHT) { lyra2z_hash(BEGIN(nVersion), BEGIN(powHash)); - } else if (!fTestNet && nHeight >= 8192) { + } else if (!fTestNet && nHeight >= HF_LYRA2_HEIGHT) { LYRA2(BEGIN(powHash), 32, BEGIN(nVersion), 80, BEGIN(nVersion), 80, 2, 8192, 256); - } else if (!fTestNet && nHeight >= 500) { + } else if (!fTestNet && nHeight >= HF_LYRA2VAR_HEIGHT) { LYRA2(BEGIN(powHash), 32, BEGIN(nVersion), 80, BEGIN(nVersion), 80, 2, nHeight, 256); - } else if (fTestNet && nHeight >= 90) { // testnet + } else if (fTestNet && nHeight >= HF_LYRA2Z_HEIGHT_TESTNET) { // testnet lyra2z_hash(BEGIN(nVersion), BEGIN(powHash)); - } else if (fTestNet && nHeight >= 80) { // testnet + } else if (fTestNet && nHeight >= HF_LYRA2_HEIGHT_TESTNET) { // testnet LYRA2(BEGIN(powHash), 32, BEGIN(nVersion), 80, BEGIN(nVersion), 80, 2, 8192, 256); + } else if (fTestNet && nHeight >= HF_LYRA2VAR_HEIGHT_TESTNET) { // testnet + LYRA2(BEGIN(powHash), 32, BEGIN(nVersion), 80, BEGIN(nVersion), 80, 2, nHeight, 256); } else { scrypt_N_1_1_256(BEGIN(nVersion), BEGIN(powHash), GetNfactor(nTime)); } diff --git a/src/primitives/precomputed_hash.h b/src/primitives/precomputed_hash.h index b0a29ebdcc..c6c6a149a6 100644 --- a/src/primitives/precomputed_hash.h +++ b/src/primitives/precomputed_hash.h @@ -5139,4 +5139,4 @@ void buildMapPoWHash() { for (int i=1; i<20500; i++) { mapPoWHash.insert(make_pair(i, uint256S(precomputedHash[i]))); } -}; +}; \ No newline at end of file diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 79af4a1f9c..9712901b44 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -138,6 +138,8 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa // Actions abandonAction = new QAction(tr("Abandon transaction"), this); + resendAction = new QAction(tr("Re-broadcast transaction"), this); + QAction *copyAddressAction = new QAction(tr("Copy address"), this); QAction *copyLabelAction = new QAction(tr("Copy label"), this); QAction *copyAmountAction = new QAction(tr("Copy amount"), this); @@ -158,6 +160,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa contextMenu->addSeparator(); contextMenu->addAction(abandonAction); contextMenu->addAction(editLabelAction); + contextMenu->addAction(resendAction); mapperThirdPartyTxUrls = new QSignalMapper(this); @@ -182,6 +185,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa connect(copyTxPlainText, SIGNAL(triggered()), this, SLOT(copyTxPlainText())); connect(editLabelAction, SIGNAL(triggered()), this, SLOT(editLabel())); connect(showDetailsAction, SIGNAL(triggered()), this, SLOT(showDetails())); + connect(resendAction, SIGNAL(triggered()), this, SLOT(rebroadcastTx())); } void TransactionView::setModel(WalletModel *model) @@ -372,6 +376,7 @@ void TransactionView::contextualMenu(const QPoint &point) uint256 hash; hash.SetHex(selection.at(0).data(TransactionTableModel::TxHashRole).toString().toStdString()); abandonAction->setEnabled(model->transactionCanBeAbandoned(hash)); + resendAction->setEnabled(model->transactionCanBeRebroadcast(hash)); if(index.isValid()) { @@ -397,6 +402,27 @@ void TransactionView::abandonTx() model->getTransactionTableModel()->updateTransaction(hashQStr, CT_UPDATED, false); } +void TransactionView::rebroadcastTx() +{ + if(!transactionView || !transactionView->selectionModel()) + return; + QModelIndexList selection = transactionView->selectionModel()->selectedRows(0); + + // get the hash from the TxHashRole (QVariant / QString) + uint256 hash; + QString hashQStr = selection.at(0).data(TransactionTableModel::TxHashRole).toString(); + hash.SetHex(hashQStr.toStdString()); + + if (model->rebroadcastTransaction(hash)) + Q_EMIT message(tr("Re-broadcast"), tr("Broadcast succeeded"), CClientUIInterface::MSG_INFORMATION); + else + Q_EMIT message(tr("Re-broadcast"), tr("There was an error trying to broadcast the message"), + CClientUIInterface::MSG_ERROR); + + // Update the table + model->getTransactionTableModel()->updateTransaction(hashQStr, CT_UPDATED, true); +} + void TransactionView::copyAddress() { GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::AddressRole); diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index e9b9d5b6bc..4e5bbbc054 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -76,6 +76,7 @@ class TransactionView : public QWidget QDateTimeEdit *dateFrom; QDateTimeEdit *dateTo; QAction *abandonAction; + QAction *resendAction; QWidget *createDateRangeWidget(); @@ -99,6 +100,7 @@ private Q_SLOTS: void openThirdPartyTxUrl(QString url); void updateWatchOnlyColumn(bool fHaveWatchOnly); void abandonTx(); + void rebroadcastTx(); Q_SIGNALS: void doubleClicked(const QModelIndex&); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 4ab7f44516..d2d5c1979d 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -18,6 +18,8 @@ #include "ui_interface.h" #include "wallet/wallet.h" #include "wallet/walletdb.h" // for BackupWallet +#include "txmempool.h" +#include "consensus/validation.h" #include @@ -703,3 +705,40 @@ bool WalletModel::abandonTransaction(uint256 hash) const LOCK2(cs_main, wallet->cs_wallet); return wallet->AbandonTransaction(hash); } + +bool WalletModel::transactionCanBeRebroadcast(uint256 hash) const +{ + LOCK2(cs_main, wallet->cs_wallet); + const CWalletTx *wtx = wallet->GetWalletTx(hash); + if (!wtx || wtx->isAbandoned() || wtx->GetDepthInMainChain() > 0) + return false; + return wtx->GetRequestCount() <= 0; +} + +bool WalletModel::rebroadcastTransaction(uint256 hash) +{ + LOCK2(cs_main, wallet->cs_wallet); + CWalletTx *wtx = const_cast(wallet->GetWalletTx(hash)); + + if (!wtx || wtx->isAbandoned() || wtx->GetDepthInMainChain() > 0) + return false; + if (wtx->GetRequestCount() > 0) + return false; + + CCoinsViewCache &view = *pcoinsTip; + const CCoins* existingCoins = view.AccessCoins(hash); + bool fHaveMempool = mempool.exists(hash); + bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000; + if (!fHaveMempool && !fHaveChain) { + // push to local node and sync with wallets + CValidationState state; + bool fMissingInputs; + if (!AcceptToMemoryPool(mempool, state, (CTransaction)*wtx, true, false, &fMissingInputs, true, false, maxTxFee)) + return false; + } else if (fHaveChain) { + return false; + } + + RelayTransaction((CTransaction)*wtx); + return true; +} diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 081260dbea..0263a95508 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -203,6 +203,9 @@ class WalletModel : public QObject bool transactionCanBeAbandoned(uint256 hash) const; bool abandonTransaction(uint256 hash) const; + bool transactionCanBeRebroadcast(uint256 hash) const; + bool rebroadcastTransaction(uint256 hash); + private: CWallet *wallet; bool fHaveWatchOnly; diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 786c186a74..7084d9769d 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -113,6 +113,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "setmintzerocoinstatus", 2 }, { "setmintzerocoinstatus", 1 }, { "listmintzerocoins", 0 }, + { "listpubcoins", 0 }, }; class CRPCConvertTable diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 88127a2f08..dbb8fa0726 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -627,7 +627,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) int i = 0; unsigned int COUNT_SPEND_ZC_TX = 0; unsigned int MAX_SPEND_ZC_TX_PER_BLOCK = 0; - if(chainActive.Height() + 1 > 22000){ + if(chainActive.Height() + 1 > HF_ZEROSPEND_FIX){ MAX_SPEND_ZC_TX_PER_BLOCK = 1; } BOOST_FOREACH (CTransaction& tx, pblock->vtx) { diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 3a5ceaffab..39c2a7a41e 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2847,6 +2847,45 @@ UniValue listmintzerocoins(const UniValue& params, bool fHelp) { return results; } + +UniValue listpubcoins(const UniValue& params, bool fHelp) { + if (fHelp || params.size() > 1) + throw runtime_error( + "listpubcoin (1/10/25/50/100)\n" + "\nArguments:\n" + "1. (int, optional) 1,10,25,50,100 (default) to return all pubcoin with denomination. empty to return all pubcoin.\n" + "\nResults are an array of Objects, each of which has:\n" + "{id, IsUsed, denomination, value, serialNumber, nHeight, randomness}"); + + int denomination = -1; + if (params.size() > 0) { + denomination = params[0].get_int(); + } + + list listPubcoin; + CWalletDB walletdb(pwalletMain->strWalletFile); + walletdb.ListPubCoin(listPubcoin); + UniValue results(UniValue::VARR); + listPubcoin.sort(CompID); + + BOOST_FOREACH(const CZerocoinEntry &zerocoinItem, listPubcoin) { + if (zerocoinItem.id > 0 && (denomination < 0 || zerocoinItem.denomination == denomination)) { + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("id", zerocoinItem.id)); + entry.push_back(Pair("IsUsed", zerocoinItem.IsUsed)); + entry.push_back(Pair("denomination", zerocoinItem.denomination)); + entry.push_back(Pair("value", zerocoinItem.value.GetHex())); + entry.push_back(Pair("serialNumber", zerocoinItem.serialNumber.GetHex())); + entry.push_back(Pair("nHeight", zerocoinItem.nHeight)); + entry.push_back(Pair("randomness", zerocoinItem.randomness.GetHex())); + results.push_back(entry); + } + } + + return results; +} + + UniValue setmintzerocoinstatus(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 2) throw runtime_error( @@ -3009,6 +3048,7 @@ static const CRPCCommand commands[] = { "wallet", "resetmintzerocoin", &resetmintzerocoin, false }, { "wallet", "setmintzerocoinstatus", &setmintzerocoinstatus, false }, { "wallet", "listmintzerocoins", &listmintzerocoins, false }, + { "wallet", "listpubcoins", &listpubcoins, false }, { "wallet", "removetxmempool", &removetxmempool, false }, { "wallet", "removetxwallet", &removetxwallet, false }, }; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 56ad8935b7..66f8d8cb1f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4667,3 +4667,4 @@ bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, CAmount nAbsurdFee, CValidat } bool CompHeight(const CZerocoinEntry & a, const CZerocoinEntry & b) { return a.nHeight < b.nHeight; } +bool CompID(const CZerocoinEntry & a, const CZerocoinEntry & b) { return a.id < b.id; } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index c0f2e2e4be..d76e1fe87b 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1075,4 +1075,5 @@ class CZerocoinSpendEntry }; bool CompHeight(const CZerocoinEntry & a, const CZerocoinEntry & b); +bool CompID(const CZerocoinEntry & a, const CZerocoinEntry & b); #endif // BITCOIN_WALLET_WALLET_H From 8e4aebef24bbb16e1d7fbe530a49757f914b74c7 Mon Sep 17 00:00:00 2001 From: aizen Date: Fri, 3 Nov 2017 15:18:09 +0100 Subject: [PATCH 02/35] Initial work for znode --- src/Makefile.am | 22 + src/activeznode.cpp | 331 +++ src/activeznode.h | 78 + src/chainparams.cpp | 109 +- src/chainparams.h | 12 + src/chainparamsseeds.h | 108 +- src/checkpoints.cpp | 10 + src/checkpoints.h | 3 + src/consensus/consensus.h | 4 +- src/consensus/params.h | 10 + src/darksend-relay.cpp | 115 ++ src/darksend-relay.h | 51 + src/darksend.cpp | 2522 +++++++++++++++++++++++ src/darksend.h | 483 +++++ src/init.cpp | 39 +- src/instantx.cpp | 1161 +++++++++++ src/instantx.h | 249 +++ src/libzerocoin/ParamGeneration.cpp | 24 +- src/libzerocoin/ParamGeneration.h | 2 +- src/libzerocoin/bitcoin_bignum/bignum.h | 146 +- src/main.cpp | 116 +- src/main.h | 16 + src/net.cpp | 97 + src/net.h | 10 + src/netfulfilledman.cpp | 72 + src/netfulfilledman.h | 49 + src/primitives/transaction.cpp | 21 + src/primitives/transaction.h | 23 + src/protocol.cpp | 50 + src/protocol.h | 29 + src/qt/res/icons/zcoin.svg | 3 + src/random.h | 28 + src/rpc/rpcznode.cpp | 808 ++++++++ src/rpc/server.cpp | 5 + src/rpc/server.h | 6 + src/script/script.cpp | 24 + src/script/script.h | 1 + src/spork.cpp | 263 +++ src/spork.h | 122 ++ src/util.cpp | 40 + src/util.h | 5 + src/wallet/crypter.h | 6 +- src/wallet/wallet.cpp | 585 ++++++ src/wallet/wallet.h | 55 +- src/wallet/walletdb.cpp | 119 ++ src/wallet/walletdb.h | 1 + src/znode-payments.cpp | 958 +++++++++ src/znode-payments.h | 224 ++ src/znode-sync.cpp | 520 +++++ src/znode-sync.h | 92 + src/znode.cpp | 936 +++++++++ src/znode.h | 440 ++++ src/znodeconfig.cpp | 91 + src/znodeconfig.h | 99 + src/znodeman.cpp | 1680 +++++++++++++++ src/znodeman.h | 366 ++++ 56 files changed, 13209 insertions(+), 230 deletions(-) create mode 100644 src/activeznode.cpp create mode 100644 src/activeznode.h create mode 100644 src/darksend-relay.cpp create mode 100644 src/darksend-relay.h 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/netfulfilledman.cpp create mode 100644 src/netfulfilledman.h create mode 100644 src/qt/res/icons/zcoin.svg create mode 100644 src/rpc/rpcznode.cpp create mode 100644 src/spork.cpp create mode 100644 src/spork.h create mode 100644 src/znode-payments.cpp create mode 100644 src/znode-payments.h create mode 100644 src/znode-sync.cpp create mode 100644 src/znode-sync.h create mode 100644 src/znode.cpp create mode 100644 src/znode.h create mode 100644 src/znodeconfig.cpp create mode 100644 src/znodeconfig.h create mode 100644 src/znodeman.cpp create mode 100644 src/znodeman.h diff --git a/src/Makefile.am b/src/Makefile.am index 6cde135a50..6c86091ff4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -76,6 +76,7 @@ endif .PHONY: FORCE check-symbols check-security # bitcoin core # BITCOIN_CORE_H = \ + activeznode.h \ addrman.h \ base58.h \ bloom.h \ @@ -100,17 +101,26 @@ BITCOIN_CORE_H = \ httprpc.h \ httpserver.h \ indirectmap.h \ + darksend.h \ + darksend-relay.h \ init.h \ + instantx.h \ key.h \ keystore.h \ dbwrapper.h \ limitedmap.h \ main.h \ + znode.h \ + znode-payments.h \ + znode-sync.h \ + znodeman.h \ + znodeconfig.h \ memusage.h \ merkleblock.h \ miner.h \ net.h \ netbase.h \ + netfulfilledman.h \ noui.h \ policy/fees.h \ policy/policy.h \ @@ -182,6 +192,7 @@ libbitcoin_server_a_SOURCES = \ merkleblock.cpp \ miner.cpp \ net.cpp \ + netfulfilledman.cpp \ noui.cpp \ policy/fees.cpp \ policy/policy.cpp \ @@ -192,6 +203,7 @@ libbitcoin_server_a_SOURCES = \ rpc/misc.cpp \ rpc/net.cpp \ rpc/rawtransaction.cpp \ + rpc/rpcznode.cpp \ rpc/server.cpp \ script/sigcache.cpp \ script/ismine.cpp \ @@ -220,6 +232,14 @@ endif libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_wallet_a_SOURCES = \ + activeznode.cpp \ + darksend.cpp \ + znode.cpp \ + instantx.cpp \ + znode-payments.cpp \ + znode-sync.cpp \ + znodeconfig.cpp \ + znodeman.cpp \ wallet/crypter.cpp \ wallet/db.cpp \ wallet/rpcdump.cpp \ @@ -278,6 +298,7 @@ libbitcoin_consensus_a_SOURCES = \ script/script_error.cpp \ script/script_error.h \ serialize.h \ + spork.h \ tinyformat.h \ uint256.cpp \ uint256.h \ @@ -314,6 +335,7 @@ libbitcoin_common_a_SOURCES = \ scheduler.cpp \ script/sign.cpp \ script/standard.cpp \ + spork.cpp \ $(BITCOIN_CORE_H) # util: shared between all executables. diff --git a/src/activeznode.cpp b/src/activeznode.cpp new file mode 100644 index 0000000000..1b477a570e --- /dev/null +++ b/src/activeznode.cpp @@ -0,0 +1,331 @@ +// Copyright (c) 2014-2017 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "activeznode.h" +#include "znode.h" +#include "znode-sync.h" +#include "znodeman.h" +#include "protocol.h" + +extern CWallet *pwalletMain; + +// Keep track of the active Znode +CActiveZnode activeZnode; + +void CActiveZnode::ManageState() { + LogPrint("znode", "CActiveZnode::ManageState -- Start\n"); + if (!fZNode) { + LogPrint("znode", "CActiveZnode::ManageState -- Not a znode, returning\n"); + return; + } + + if (Params().NetworkIDString() != CBaseChainParams::REGTEST && !znodeSync.IsBlockchainSynced()) { + nState = ACTIVE_ZNODE_SYNC_IN_PROCESS; + LogPrintf("CActiveZnode::ManageState -- %s: %s\n", GetStateString(), GetStatus()); + return; + } + + if (nState == ACTIVE_ZNODE_SYNC_IN_PROCESS) { + nState = ACTIVE_ZNODE_INITIAL; + } + + LogPrint("znode", "CActiveZnode::ManageState -- status = %s, type = %s, pinger enabled = %d\n", + GetStatus(), GetTypeString(), fPingerEnabled); + + if (eType == ZNODE_UNKNOWN) { + ManageStateInitial(); + } + + if (eType == ZNODE_REMOTE) { + ManageStateRemote(); + } else if (eType == ZNODE_LOCAL) { + // Try Remote Start first so the started local znode can be restarted without recreate znode broadcast. + ManageStateRemote(); + if (nState != ACTIVE_ZNODE_STARTED) + ManageStateLocal(); + } + + SendZnodePing(); +} + +std::string CActiveZnode::GetStateString() const { + switch (nState) { + case ACTIVE_ZNODE_INITIAL: + return "INITIAL"; + case ACTIVE_ZNODE_SYNC_IN_PROCESS: + return "SYNC_IN_PROCESS"; + case ACTIVE_ZNODE_INPUT_TOO_NEW: + return "INPUT_TOO_NEW"; + case ACTIVE_ZNODE_NOT_CAPABLE: + return "NOT_CAPABLE"; + case ACTIVE_ZNODE_STARTED: + return "STARTED"; + default: + return "UNKNOWN"; + } +} + +std::string CActiveZnode::GetStatus() const { + switch (nState) { + case ACTIVE_ZNODE_INITIAL: + return "Node just started, not yet activated"; + case ACTIVE_ZNODE_SYNC_IN_PROCESS: + return "Sync in progress. Must wait until sync is complete to start Znode"; + case ACTIVE_ZNODE_INPUT_TOO_NEW: + return strprintf("Znode input must have at least %d confirmations", + Params().GetConsensus().nZnodeMinimumConfirmations); + case ACTIVE_ZNODE_NOT_CAPABLE: + return "Not capable znode: " + strNotCapableReason; + case ACTIVE_ZNODE_STARTED: + return "Znode successfully started"; + default: + return "Unknown"; + } +} + +std::string CActiveZnode::GetTypeString() const { + std::string strType; + switch (eType) { + case ZNODE_UNKNOWN: + strType = "UNKNOWN"; + break; + case ZNODE_REMOTE: + strType = "REMOTE"; + break; + case ZNODE_LOCAL: + strType = "LOCAL"; + break; + default: + strType = "UNKNOWN"; + break; + } + return strType; +} + +bool CActiveZnode::SendZnodePing() { + if (!fPingerEnabled) { + LogPrint("znode", + "CActiveZnode::SendZnodePing -- %s: znode ping service is disabled, skipping...\n", + GetStateString()); + return false; + } + + if (!mnodeman.Has(vin)) { + strNotCapableReason = "Znode not in znode list"; + nState = ACTIVE_ZNODE_NOT_CAPABLE; + LogPrintf("CActiveZnode::SendZnodePing -- %s: %s\n", GetStateString(), strNotCapableReason); + return false; + } + + CZnodePing mnp(vin); + if (!mnp.Sign(keyZnode, pubKeyZnode)) { + LogPrintf("CActiveZnode::SendZnodePing -- ERROR: Couldn't sign Znode Ping\n"); + return false; + } + + // Update lastPing for our znode in Znode list + if (mnodeman.IsZnodePingedWithin(vin, ZNODE_MIN_MNP_SECONDS, mnp.sigTime)) { + LogPrintf("CActiveZnode::SendZnodePing -- Too early to send Znode Ping\n"); + return false; + } + + mnodeman.SetZnodeLastPing(vin, mnp); + + LogPrintf("CActiveZnode::SendZnodePing -- Relaying ping, collateral=%s\n", vin.ToString()); + mnp.Relay(); + + return true; +} + +void CActiveZnode::ManageStateInitial() { + LogPrint("znode", "CActiveZnode::ManageStateInitial -- status = %s, type = %s, pinger enabled = %d\n", + GetStatus(), GetTypeString(), fPingerEnabled); + + // Check that our local network configuration is correct + if (!fListen) { + // listen option is probably overwritten by smth else, no good + nState = ACTIVE_ZNODE_NOT_CAPABLE; + strNotCapableReason = "Znode must accept connections from outside. Make sure listen configuration option is not overwritten by some another parameter."; + LogPrintf("CActiveZnode::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); + return; + } + + bool fFoundLocal = false; + { + LOCK(cs_vNodes); + + // First try to find whatever local address is specified by externalip option + fFoundLocal = GetLocal(service) && CZnode::IsValidNetAddr(service); + if (!fFoundLocal) { + // nothing and no live connections, can't do anything for now + if (vNodes.empty()) { + nState = ACTIVE_ZNODE_NOT_CAPABLE; + strNotCapableReason = "Can't detect valid external address. Will retry when there are some connections available."; + LogPrintf("CActiveZnode::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); + return; + } + // We have some peers, let's try to find our local address from one of them + BOOST_FOREACH(CNode * pnode, vNodes) + { + if (pnode->fSuccessfullyConnected && pnode->addr.IsIPv4()) { + fFoundLocal = GetLocal(service, &pnode->addr) && CZnode::IsValidNetAddr(service); + if (fFoundLocal) break; + } + } + } + } + + if (!fFoundLocal) { + nState = ACTIVE_ZNODE_NOT_CAPABLE; + strNotCapableReason = "Can't detect valid external address. Please consider using the externalip configuration option if problem persists. Make sure to use IPv4 address only."; + LogPrintf("CActiveZnode::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); + return; + } + + int mainnetDefaultPort = Params(CBaseChainParams::MAIN).GetDefaultPort(); + if (Params().NetworkIDString() == CBaseChainParams::MAIN) { + if (service.GetPort() != mainnetDefaultPort) { + nState = ACTIVE_ZNODE_NOT_CAPABLE; + strNotCapableReason = strprintf("Invalid port: %u - only %d is supported on mainnet.", service.GetPort(), + mainnetDefaultPort); + LogPrintf("CActiveZnode::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); + return; + } + } else if (service.GetPort() == mainnetDefaultPort) { + nState = ACTIVE_ZNODE_NOT_CAPABLE; + strNotCapableReason = strprintf("Invalid port: %u - %d is only supported on mainnet.", service.GetPort(), + mainnetDefaultPort); + LogPrintf("CActiveZnode::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); + return; + } + + LogPrintf("CActiveZnode::ManageStateInitial -- Checking inbound connection to '%s'\n", service.ToString()); + //TODO +// if (!ConnectNodeDash(CAddress(service, NODE_NETWORK), NULL, true)) { +// nState = ACTIVE_ZNODE_NOT_CAPABLE; +// strNotCapableReason = "Could not connect to " + service.ToString(); +// LogPrintf("CActiveZnode::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); +// return; +// } + + // Default to REMOTE + eType = ZNODE_REMOTE; + + // Check if wallet funds are available + if (!pwalletMain) { + LogPrintf("CActiveZnode::ManageStateInitial -- %s: Wallet not available\n", GetStateString()); + return; + } + + if (pwalletMain->IsLocked()) { + LogPrintf("CActiveZnode::ManageStateInitial -- %s: Wallet is locked\n", GetStateString()); + return; + } + + if (pwalletMain->GetBalance() < 1000 * COIN) { + LogPrintf("CActiveZnode::ManageStateInitial -- %s: Wallet balance is < 1000 DASH\n", GetStateString()); + return; + } + + // Choose coins to use + CPubKey pubKeyCollateral; + CKey keyCollateral; + + // If collateral is found switch to LOCAL mode + if (pwalletMain->GetZnodeVinAndKeys(vin, pubKeyCollateral, keyCollateral)) { + eType = ZNODE_LOCAL; + } + + LogPrint("znode", "CActiveZnode::ManageStateInitial -- End status = %s, type = %s, pinger enabled = %d\n", + GetStatus(), GetTypeString(), fPingerEnabled); +} + +void CActiveZnode::ManageStateRemote() { + LogPrint("znode", + "CActiveZnode::ManageStateRemote -- Start status = %s, type = %s, pinger enabled = %d, pubKeyZnode.GetID() = %s\n", + GetStatus(), fPingerEnabled, GetTypeString(), pubKeyZnode.GetID().ToString()); + + mnodeman.CheckZnode(pubKeyZnode); + znode_info_t infoMn = mnodeman.GetZnodeInfo(pubKeyZnode); + if (infoMn.fInfoValid) { + if (infoMn.nProtocolVersion != PROTOCOL_VERSION) { + nState = ACTIVE_ZNODE_NOT_CAPABLE; + strNotCapableReason = "Invalid protocol version"; + LogPrintf("CActiveZnode::ManageStateRemote -- %s: %s\n", GetStateString(), strNotCapableReason); + return; + } + if (service != infoMn.addr) { + nState = ACTIVE_ZNODE_NOT_CAPABLE; + strNotCapableReason = "Broadcasted IP doesn't match our external address. Make sure you issued a new broadcast if IP of this znode changed recently."; + LogPrintf("CActiveZnode::ManageStateRemote -- %s: %s\n", GetStateString(), strNotCapableReason); + return; + } + if (!CZnode::IsValidStateForAutoStart(infoMn.nActiveState)) { + nState = ACTIVE_ZNODE_NOT_CAPABLE; + strNotCapableReason = strprintf("Znode in %s state", CZnode::StateToString(infoMn.nActiveState)); + LogPrintf("CActiveZnode::ManageStateRemote -- %s: %s\n", GetStateString(), strNotCapableReason); + return; + } + if (nState != ACTIVE_ZNODE_STARTED) { + LogPrintf("CActiveZnode::ManageStateRemote -- STARTED!\n"); + vin = infoMn.vin; + service = infoMn.addr; + fPingerEnabled = true; + nState = ACTIVE_ZNODE_STARTED; + } + } else { + nState = ACTIVE_ZNODE_NOT_CAPABLE; + strNotCapableReason = "Znode not in znode list"; + LogPrintf("CActiveZnode::ManageStateRemote -- %s: %s\n", GetStateString(), strNotCapableReason); + } +} + +void CActiveZnode::ManageStateLocal() { + LogPrint("znode", "CActiveZnode::ManageStateLocal -- status = %s, type = %s, pinger enabled = %d\n", + GetStatus(), GetTypeString(), fPingerEnabled); + if (nState == ACTIVE_ZNODE_STARTED) { + return; + } + + // Choose coins to use + CPubKey pubKeyCollateral; + CKey keyCollateral; + + if (pwalletMain->GetZnodeVinAndKeys(vin, pubKeyCollateral, keyCollateral)) { + int nInputAge = GetInputAge(vin); + if (nInputAge < Params().GetConsensus().nZnodeMinimumConfirmations) { + nState = ACTIVE_ZNODE_INPUT_TOO_NEW; + strNotCapableReason = strprintf(_("%s - %d confirmations"), GetStatus(), nInputAge); + LogPrintf("CActiveZnode::ManageStateLocal -- %s: %s\n", GetStateString(), strNotCapableReason); + return; + } + + { + LOCK(pwalletMain->cs_wallet); + pwalletMain->LockCoin(vin.prevout); + } + + CZnodeBroadcast mnb; + std::string strError; + if (!CZnodeBroadcast::Create(vin, service, keyCollateral, pubKeyCollateral, keyZnode, + pubKeyZnode, strError, mnb)) { + nState = ACTIVE_ZNODE_NOT_CAPABLE; + strNotCapableReason = "Error creating mastenode broadcast: " + strError; + LogPrintf("CActiveZnode::ManageStateLocal -- %s: %s\n", GetStateString(), strNotCapableReason); + return; + } + + fPingerEnabled = true; + nState = ACTIVE_ZNODE_STARTED; + + //update to znode list + LogPrintf("CActiveZnode::ManageStateLocal -- Update Znode List\n"); + mnodeman.UpdateZnodeList(mnb); + mnodeman.NotifyZnodeUpdates(); + + //send to all peers + LogPrintf("CActiveZnode::ManageStateLocal -- Relay broadcast, vin=%s\n", vin.ToString()); + mnb.Relay(); + } +} diff --git a/src/activeznode.h b/src/activeznode.h new file mode 100644 index 0000000000..6b70f99793 --- /dev/null +++ b/src/activeznode.h @@ -0,0 +1,78 @@ +// Copyright (c) 2014-2017 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef ACTIVEZNODE_H +#define ACTIVEZNODE_H + +#include "net.h" +#include "key.h" +#include "wallet/wallet.h" + +class CActiveZnode; + +static const int ACTIVE_ZNODE_INITIAL = 0; // initial state +static const int ACTIVE_ZNODE_SYNC_IN_PROCESS = 1; +static const int ACTIVE_ZNODE_INPUT_TOO_NEW = 2; +static const int ACTIVE_ZNODE_NOT_CAPABLE = 3; +static const int ACTIVE_ZNODE_STARTED = 4; + +extern CActiveZnode activeZnode; + +// Responsible for activating the Znode and pinging the network +class CActiveZnode +{ +public: + enum znode_type_enum_t { + ZNODE_UNKNOWN = 0, + ZNODE_REMOTE = 1, + ZNODE_LOCAL = 2 + }; + +private: + // critical section to protect the inner data structures + mutable CCriticalSection cs; + + znode_type_enum_t eType; + + bool fPingerEnabled; + + /// Ping Znode + bool SendZnodePing(); + +public: + // Keys for the active Znode + CPubKey pubKeyZnode; + CKey keyZnode; + + // Initialized while registering Znode + CTxIn vin; + CService service; + + int nState; // should be one of ACTIVE_ZNODE_XXXX + std::string strNotCapableReason; + + CActiveZnode() + : eType(ZNODE_UNKNOWN), + fPingerEnabled(false), + pubKeyZnode(), + keyZnode(), + vin(), + service(), + nState(ACTIVE_ZNODE_INITIAL) + {} + + /// Manage state of active Znode + void ManageState(); + + std::string GetStateString() const; + std::string GetStatus() const; + std::string GetTypeString() const; + +private: + void ManageStateInitial(); + void ManageStateRemote(); + void ManageStateLocal(); +}; + +#endif diff --git a/src/chainparams.cpp b/src/chainparams.cpp index ddfa0b22b3..7d89175066 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -19,7 +19,8 @@ #include "arith_uint256.h" -static CBlock CreateGenesisBlock(const char *pszTimestamp, const CScript &genesisOutputScript, uint32_t nTime, uint32_t nNonce, +static CBlock +CreateGenesisBlock(const char *pszTimestamp, const CScript &genesisOutputScript, uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount &genesisReward, std::vector extraNonce) { CMutableTransaction txNew; @@ -94,7 +95,7 @@ class CMainParams : public CChainParams { consensus.BIP34Height = 227931; consensus.BIP34Hash = uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8"); consensus.powLimit = uint256S("00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); -// static const int64 nInterval = nTargetTimespan / nTargetSpacing; + //static const int64 nInterval = nTargetTimespan / nTargetSpacing; consensus.nPowTargetTimespan = 60 * 60; // 60 minutes between retargets consensus.nPowTargetSpacing = 10 * 60; // 10 minute blocks consensus.fPowAllowMinDifficultyBlocks = false; @@ -116,14 +117,28 @@ class CMainParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = 1510704000; // November 15th, 2017. // The best chain should have at least this much work. -// consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000003418b3ccbe5e93bcb39b43"); consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000000708f98bf623f02e"); - /** - * The message start string is designed to be unlikely to occur in normal data. - * The characters are rarely used upper ASCII, not valid as UTF-8, and produce - ` * a large 32-bit integer with any alignment. - */ + // znode params + consensus.nZnodePaymentsStartBlock = 100000; // not true, but it's ok as long as it's less then nZnodePaymentsIncreaseBlock + consensus.nZnodePaymentsIncreaseBlock = 158000; // actual historical value + consensus.nZnodePaymentsIncreasePeriod = 576*30; // 17280 - actual historical value + consensus.nSuperblockStartBlock = 614820; + consensus.nBudgetPaymentsStartBlock = 328008; // actual historical value + consensus.nBudgetPaymentsCycleBlocks = 16616; // ~(60*24*30)/2.6, actual number of blocks per month is 200700 / 12 = 16725 + consensus.nBudgetPaymentsWindowBlocks = 100; + nMaxTipAge = 6 * 60 * 60; // ~144 blocks behind -> 2 x fork detection time, was 24 * 60 * 60 in bitcoin + + nPoolMaxTransactions = 3; + nFulfilledRequestExpireTime = 60*60; // fulfilled requests expire in 1 hour + strSporkPubKey = "04549ac134f694c0243f503e8c8a9a986f5de6610049c40b07816809b0d1d06a21b07be27b9bb555931773f62ba6cf35a25fd52f694d4e1106ccd237a7bb899fdd"; + strZnodePaymentsPubKey = "04549ac134f694c0243f503e8c8a9a986f5de6610049c40b07816809b0d1d06a21b07be27b9bb555931773f62ba6cf35a25fd52f694d4e1106ccd237a7bb899fdd"; + + /** + * The message start string is designed to be unlikely to occur in normal data. + * The characters are rarely used upper ASCII, not valid as UTF-8, and produce + ` * a large 32-bit integer with any alignment. + */ //btzc: update zcoin pchMessage pchMessageStart[0] = 0xe3; pchMessageStart[1] = 0xd9; @@ -161,11 +176,18 @@ class CMainParams : public CChainParams { vSeeds.push_back(CDNSSeedData("singapore.zcoin.io", "singapore.zcoin.io", false)); vSeeds.push_back(CDNSSeedData("nyc.zcoin.io", "nyc.zcoin.io", false)); // Note that of those with the service bits flag, most only support a subset of possible options - base58Prefixes[PUBKEY_ADDRESS] = std::vector < unsigned char > (1, 82); - base58Prefixes[SCRIPT_ADDRESS] = std::vector < unsigned char > (1, 7); - base58Prefixes[SECRET_KEY] = std::vector < unsigned char > (1, 210); - base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x88)(0xB2)(0x1E).convert_to_container < std::vector < unsigned char > > (); - base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x88)(0xAD)(0xE4).convert_to_container < std::vector < unsigned char > > (); + base58Prefixes[PUBKEY_ADDRESS] = std::vector < unsigned + char > (1, 82); + base58Prefixes[SCRIPT_ADDRESS] = std::vector < unsigned + char > (1, 7); + base58Prefixes[SECRET_KEY] = std::vector < unsigned + char > (1, 210); + base58Prefixes[EXT_PUBLIC_KEY] = + boost::assign::list_of(0x04)(0x88)(0xB2)(0x1E).convert_to_container < std::vector < unsigned + char > > (); + base58Prefixes[EXT_SECRET_KEY] = + boost::assign::list_of(0x04)(0x88)(0xAD)(0xE4).convert_to_container < std::vector < unsigned + char > > (); vFixedSeeds = std::vector(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main)); @@ -233,6 +255,20 @@ class CTestNetParams : public CChainParams { // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000000708f98bf623f02e"); + // Maternode params + consensus.nZnodePaymentsStartBlock = 10000; // not true, but it's ok as long as it's less then n + consensus.nZnodePaymentsIncreaseBlock = 46000; + consensus.nZnodePaymentsIncreasePeriod = 576; + consensus.nSuperblockStartBlock = 61000; + consensus.nBudgetPaymentsStartBlock = 60000; + consensus.nBudgetPaymentsCycleBlocks = 50; + consensus.nBudgetPaymentsWindowBlocks = 10; + nMaxTipAge = 0x7fffffff; // allow mining on top of old blocks for testnet + + nPoolMaxTransactions = 3; + nFulfilledRequestExpireTime = 5*60; // fulfilled requests expire in 5 minutes + strSporkPubKey = "046f78dcf911fbd61910136f7f0f8d90578f68d0b3ac973b5040fb7afb501b5939f39b108b0569dca71488f5bbf498d92e4d1194f6f941307ffd95f75e76869f0e"; + strZnodePaymentsPubKey = "046f78dcf911fbd61910136f7f0f8d90578f68d0b3ac973b5040fb7afb501b5939f39b108b0569dca71488f5bbf498d92e4d1194f6f941307ffd95f75e76869f0e"; pchMessageStart[0] = 0xcf; pchMessageStart[1] = 0xfc; @@ -263,18 +299,27 @@ class CTestNetParams : public CChainParams { vSeeds.clear(); // nodes with support for servicebits filtering should be at the top // zcoin test seeds - vSeeds.push_back(CDNSSeedData("52.175.244.22", "52.175.244.22", false)); + vSeeds.push_back(CDNSSeedData("beta1.zcoin.io", "beta1.zcoin.io", false)); + vSeeds.push_back(CDNSSeedData("beta2.zcoin.io", "beta2.zcoin.io", false)); + // vSeeds.push_back(CDNSSeedData("testnetbitcoin.jonasschnelli.ch", "testnet-seed.bitcoin.jonasschnelli.ch", true)); // vSeeds.push_back(CDNSSeedData("petertodd.org", "seed.tbtc.petertodd.org", true)); // vSeeds.push_back(CDNSSeedData("bluematt.me", "testnet-seed.bluematt.me")); // vSeeds.push_back(CDNSSeedData("bitcoin.schildbach.de", "testnet-seed.bitcoin.schildbach.de")); - base58Prefixes[PUBKEY_ADDRESS] = std::vector < unsigned char > (1, 65); - base58Prefixes[SCRIPT_ADDRESS] = std::vector < unsigned char > (1, 178); - base58Prefixes[SECRET_KEY] = std::vector < unsigned char > (1, 185); - base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container < std::vector < unsigned char > > (); - base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container < std::vector < unsigned char > > (); + base58Prefixes[PUBKEY_ADDRESS] = std::vector < unsigned + char > (1, 65); + base58Prefixes[SCRIPT_ADDRESS] = std::vector < unsigned + char > (1, 178); + base58Prefixes[SECRET_KEY] = std::vector < unsigned + char > (1, 185); + base58Prefixes[EXT_PUBLIC_KEY] = + boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container < std::vector < unsigned + char > > (); + base58Prefixes[EXT_SECRET_KEY] = + boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container < std::vector < unsigned + char > > (); vFixedSeeds = std::vector(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test)); fMiningRequiresPeers = true; @@ -287,9 +332,9 @@ class CTestNetParams : public CChainParams { checkpointData = (CCheckpointData) { boost::assign::map_list_of (0, uint256S("0x")), - 1414776313, - 0, - 100.0 + 1414776313, + 0, + 100.0 }; } @@ -329,6 +374,9 @@ class CRegTestParams : public CChainParams { // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00"); + // Znode code + nFulfilledRequestExpireTime = 5*60; // fulfilled requests expire in 5 minutes + nMaxTipAge = 6 * 60 * 60; // ~144 blocks behind -> 2 x fork detection time, was 24 * 60 * 60 in bitcoin pchMessageStart[0] = 0xfa; pchMessageStart[1] = 0xbf; @@ -374,11 +422,18 @@ class CRegTestParams : public CChainParams { 0, 0 }; - base58Prefixes[PUBKEY_ADDRESS] = std::vector < unsigned char > (1, 65); - base58Prefixes[SCRIPT_ADDRESS] = std::vector < unsigned char > (1, 178); - base58Prefixes[SECRET_KEY] = std::vector < unsigned char > (1, 239); - base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container < std::vector < unsigned char > > (); - base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container < std::vector < unsigned char > > (); + base58Prefixes[PUBKEY_ADDRESS] = std::vector < unsigned + char > (1, 65); + base58Prefixes[SCRIPT_ADDRESS] = std::vector < unsigned + char > (1, 178); + base58Prefixes[SECRET_KEY] = std::vector < unsigned + char > (1, 239); + base58Prefixes[EXT_PUBLIC_KEY] = + boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container < std::vector < unsigned + char > > (); + base58Prefixes[EXT_SECRET_KEY] = + boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container < std::vector < unsigned + char > > (); } void UpdateBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout) { diff --git a/src/chainparams.h b/src/chainparams.h index 0c3820b7c6..85e1c9d8bc 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -75,6 +75,12 @@ class CChainParams const std::vector& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } const std::vector& FixedSeeds() const { return vFixedSeeds; } const CCheckpointData& Checkpoints() const { return checkpointData; } + /** znode code from Dash*/ + int64_t MaxTipAge() const { return nMaxTipAge; } + int PoolMaxTransactions() const { return nPoolMaxTransactions; } + int FulfilledRequestExpireTime() const { return nFulfilledRequestExpireTime; } + std::string SporkPubKey() const { return strSporkPubKey; } + std::string ZnodePaymentPubKey() const { return strZnodePaymentsPubKey; } protected: CChainParams() {} @@ -93,6 +99,12 @@ class CChainParams bool fMineBlocksOnDemand; bool fTestnetToBeDeprecatedFieldRPC; CCheckpointData checkpointData; + /** znode params*/ + long nMaxTipAge; + int nPoolMaxTransactions; + int nFulfilledRequestExpireTime; + std::string strSporkPubKey; + std::string strZnodePaymentsPubKey; }; /** diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h index 5d78c9e254..aca114bfa7 100644 --- a/src/chainparamsseeds.h +++ b/src/chainparamsseeds.h @@ -8,109 +8,15 @@ * IPv4 as well as onion addresses are wrapped inside a IPv6 address accordingly. */ static SeedSpec6 pnSeed6_main[] = { -// {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0xbb,0x29,0x58}, 8168}, -// {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0xa6,0x0b,0x6c}, 8168}, -// {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x28,0x4c,0x48,0xd2}, 8168}, -// {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2f,0x34,0x00,0xc1}, 8168}, -// {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0xb2,0x22,0x72}, 8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x34,0xbb,0x29,0x58},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x34,0xb2,0x22,0x72},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x28,0x4c,0x48,0xd2},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x1,0xa,0x88,0x78},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x1,0xa,0x88,0x54},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x68,0xed,0x4,0x21},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x6e,0x50,0x1,0xfa},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x70,0x4a,0x39,0xc2},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x71,0x4e,0xfd,0xbe},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x72,0xd7,0x2e,0xd0},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x73,0xca,0x9f,0x1c},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x73,0xd7,0xe9,0xa2},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x73,0xd8,0xb1,0x7e},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x75,0x96,0xb9,0x11},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x76,0xbe,0x4d,0x45},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x77,0x82,0xe5,0x89},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x77,0x89,0x35,0x60},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x77,0x89,0x37,0x4c},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x77,0x31,0x50,0xe0},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x78,0x4d,0x39,0x3f},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x79,0xdb,0x7b,0x43},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x79,0x28,0x76,0x3e},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x7b,0x74,0x6,0x89},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x7d,0x6f,0x8d,0x2},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x7d,0x24,0x2f,0xef},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xd,0x5b,0x2d,0xfa},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x86,0x82,0xac,0x58},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x86,0xc4,0x17,0xee},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x88,0xf3,0x32,0x9f},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x8b,0x3b,0xe6,0x56},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x8c,0xe0,0x74,0x9d},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x90,0x4c,0xed,0x27},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x9e,0x45,0x7e,0x9c},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x9e,0x45,0xf8,0x5d},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xac,0x68,0x2d,0x73},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xad,0xef,0xd4,0x29},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xae,0x4c,0x9e,0xc2},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xaf,0x91,0x57,0xc8},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xb0,0xe2,0x8f,0xc7},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xb6,0x37,0xd2,0x67},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xb7,0x59,0x5f,0xb4},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xb8,0xa4,0x81,0xca},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xb9,0x8d,0x1b,0x95},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xb9,0x59,0xf4,0x3f},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xc2,0xe4,0xb,0x90},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xd5,0xfe,0x67,0x42},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xd8,0x63,0x64,0xca},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xd9,0x4f,0x2e,0x9f},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xd9,0x4f,0x2e,0x9f},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xdb,0x83,0x7a,0xee},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xdc,0x82,0xea,0xe2},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xdc,0xa0,0xc4,0x26},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xdd,0xb,0x16,0x85},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xde,0xa1,0x1a,0xeb},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xde,0x5c,0x48,0xae},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0xdf,0x49,0xea,0x1c},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x1b,0x98,0x5b,0xe9},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x1f,0xa,0x9d,0xe8},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x1f,0x86,0x27,0x69},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x22,0xc1,0x82,0xdd},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x22,0xca,0x6,0xce},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x22,0xe0,0x83,0xf8},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x23,0x9c,0x54,0x5a},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x24,0x2f,0x89,0xed},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x25,0x3b,0x18,0xf},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x27,0x6d,0x7d,0x49},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x28,0x4c,0x48,0xd2},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x2b,0xe9,0x82,0x89},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x2e,0x0,0xc0,0x10},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x2e,0x79,0xc6,0x4a},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x2e,0xa6,0xcc,0x3e},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x2f,0x95,0x24,0x1d},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x2f,0x34,0x0,0xc1},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x2f,0x5a,0x17,0x3},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x31,0x23,0xc,0x6},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x34,0xa6,0xb,0x6c},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x34,0xa9,0xb,0x6d},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x34,0xb2,0x22,0x72},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x34,0xbb,0x16,0x34},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x34,0xbb,0x29,0x58},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x34,0x6,0x3d,0xa3},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x3b,0xa8,0xa8,0x76},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x3d,0x64,0x12,0x7d},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x41,0xbe,0x25,0x93},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x42,0xb,0xb2,0x76},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x49,0x78,0x40,0xf9},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x4a,0xd0,0x58,0xa2},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x4e,0x9d,0x18,0x50},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x4e,0x5e,0x20,0xc2},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x50,0x96,0xa3,0x22},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x54,0xd0,0x32,0x40},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x54,0x19,0x5a,0xe4},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x59,0x8e,0x28,0xdc},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x5c,0x3f,0x39,0xaa},8168}, - {{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xff,0x5d,0x32,0x72,0x98},8168}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0xbb,0x29,0x58}, 8168}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0xa6,0x0b,0x6c}, 8168}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x28,0x4c,0x48,0xd2}, 8168}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2f,0x34,0x00,0xc1}, 8168}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0xb2,0x22,0x72}, 8168} }; static SeedSpec6 pnSeed6_test[] = { - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0xaf,0xf4,0x16}, 28168} + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0xe8,0x69,0xbc}, 28168}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x0d,0x4c,0xd2,0x01}, 28168} }; #endif // BITCOIN_CHAINPARAMSSEEDS_H \ No newline at end of file diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index d22c188c16..aefddce464 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -55,6 +55,16 @@ namespace Checkpoints { return fWorkBefore / (fWorkBefore + fWorkAfter); } + int GetTotalBlocksEstimate(const CCheckpointData& data) + { + const MapCheckpoints& checkpoints = data.mapCheckpoints; + + if (checkpoints.empty()) + return 0; + + return checkpoints.rbegin()->first; + } + CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) { const MapCheckpoints& checkpoints = data.mapCheckpoints; diff --git a/src/checkpoints.h b/src/checkpoints.h index 04346f35ff..8fda4cb1eb 100644 --- a/src/checkpoints.h +++ b/src/checkpoints.h @@ -22,6 +22,9 @@ namespace Checkpoints //! Returns last CBlockIndex* in mapBlockIndex that is a checkpoint CBlockIndex* GetLastCheckpoint(const CCheckpointData& data); +//! Return conservative estimate of total number of blocks, 0 if unknown +int GetTotalBlocksEstimate(const CCheckpointData& data); + double GuessVerificationProgress(const CCheckpointData& data, CBlockIndex* pindex, bool fSigchecks = true); } //namespace Checkpoints diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index 23ee925dc0..23a75c964b 100644 --- a/src/consensus/consensus.h +++ b/src/consensus/consensus.h @@ -14,8 +14,8 @@ static const int HF_LYRA2VAR_HEIGHT = 500; static const int HF_LYRA2_HEIGHT = 8192; static const int HF_LYRA2Z_HEIGHT = 20500; -static const int HF_ZNODE_HEIGHT = 55000; -//static const int HF_MTP_HEIGHT = 60000; +static const int HF_ZNODE_HEIGHT = 65000; +//static const int HF_MTP_HEIGHT = 70000; static const int HF_LYRA2VAR_HEIGHT_TESTNET = 10; static const int HF_LYRA2_HEIGHT_TESTNET = 25; // for consistent purpose since the algo hash is so low diff --git a/src/consensus/params.h b/src/consensus/params.h index de40c7d1db..68a5a7247b 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -63,6 +63,16 @@ struct Params { int64_t nChainStartTime; unsigned char nMinNFactor; unsigned char nMaxNFactor; + int nInstantSendKeepLock; // in blocks + int nBudgetPaymentsStartBlock; + int nBudgetPaymentsCycleBlocks; + int nBudgetPaymentsWindowBlocks; + int nZnodeMinimumConfirmations; + int nZnodePaymentsStartBlock; + int nZnodePaymentsIncreaseBlock; + int nZnodePaymentsIncreasePeriod; // in blocks + int nSuperblockStartBlock; + int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; } uint256 nMinimumChainWork; }; diff --git a/src/darksend-relay.cpp b/src/darksend-relay.cpp new file mode 100644 index 0000000000..6a6b2d6fc8 --- /dev/null +++ b/src/darksend-relay.cpp @@ -0,0 +1,115 @@ +#include "darksend.h" +#include "darksend-relay.h" + + +CDarkSendRelay::CDarkSendRelay() +{ + vinZnode = CTxIn(); + nBlockHeight = 0; + nRelayType = 0; + in = CTxIn(); + out = CTxOut(); +} + +CDarkSendRelay::CDarkSendRelay(CTxIn& vinZnodeIn, vector& vchSigIn, int nBlockHeightIn, int nRelayTypeIn, CTxIn& in2, CTxOut& out2) +{ + vinZnode = vinZnodeIn; + vchSig = vchSigIn; + nBlockHeight = nBlockHeightIn; + nRelayType = nRelayTypeIn; + in = in2; + out = out2; +} + +std::string CDarkSendRelay::ToString() +{ + std::ostringstream info; + + info << "vin: " << vinZnode.ToString() << + " nBlockHeight: " << (int)nBlockHeight << + " nRelayType: " << (int)nRelayType << + " in " << in.ToString() << + " out " << out.ToString(); + + return info.str(); +} + +bool CDarkSendRelay::Sign(std::string strSharedKey) +{ + std::string strError = ""; + std::string strMessage = in.ToString() + out.ToString(); + + CKey key2; + CPubKey pubkey2; + + if(!darkSendSigner.GetKeysFromSecret(strSharedKey, key2, pubkey2)) { + LogPrintf("CDarkSendRelay::Sign -- GetKeysFromSecret() failed, invalid shared key %s\n", strSharedKey); + return false; + } + + if(!darkSendSigner.SignMessage(strMessage, vchSig2, key2)) { + LogPrintf("CDarkSendRelay::Sign -- SignMessage() failed\n"); + return false; + } + + if(!darkSendSigner.VerifyMessage(pubkey2, vchSig2, strMessage, strError)) { + LogPrintf("CDarkSendRelay::Sign -- VerifyMessage() failed, error: %s\n", strError); + return false; + } + + return true; +} + +bool CDarkSendRelay::VerifyMessage(std::string strSharedKey) +{ + std::string strError = ""; + std::string strMessage = in.ToString() + out.ToString(); + + CKey key2; + CPubKey pubkey2; + + if(!darkSendSigner.GetKeysFromSecret(strSharedKey, key2, pubkey2)) { + LogPrintf("CDarkSendRelay::VerifyMessage -- GetKeysFromSecret() failed, invalid shared key %s\n", strSharedKey); + return false; + } + + if(!darkSendSigner.VerifyMessage(pubkey2, vchSig2, strMessage, strError)) { + LogPrintf("CDarkSendRelay::VerifyMessage -- VerifyMessage() failed, error: %s\n", strError); + return false; + } + + return true; +} + +void CDarkSendRelay::Relay() +{ + int nCount = std::min(mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION), 20); + int nRank1 = (rand() % nCount)+1; + int nRank2 = (rand() % nCount)+1; + + //keep picking another second number till we get one that doesn't match + while(nRank1 == nRank2) nRank2 = (rand() % nCount)+1; + + //printf("rank 1 - rank2 %d %d \n", nRank1, nRank2); + + //relay this message through 2 separate nodes for redundancy + RelayThroughNode(nRank1); + RelayThroughNode(nRank2); +} + +void CDarkSendRelay::RelayThroughNode(int nRank) +{ + CZnode* pmn = mnodeman.GetZnodeByRank(nRank, nBlockHeight, MIN_PRIVATESEND_PEER_PROTO_VERSION); + + if(pmn != NULL){ + //printf("RelayThroughNode %s\n", pmn->addr.ToString().c_str()); + CNode* pnode = ConnectNode((CAddress)pmn->addr, NULL); + if(pnode) { + //printf("Connected\n"); + pnode->PushMessage("dsr", (*this)); + return; + } + } else { + //printf("RelayThroughNode NULL\n"); + } +} diff --git a/src/darksend-relay.h b/src/darksend-relay.h new file mode 100644 index 0000000000..ee6ac57487 --- /dev/null +++ b/src/darksend-relay.h @@ -0,0 +1,51 @@ + +// Copyright (c) 2014-2017 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef DARKSEND_RELAY_H +#define DARKSEND_RELAY_H + +#include "main.h" +#include "activeznode.h" +#include "znodeman.h" + + +class CDarkSendRelay +{ +public: + CTxIn vinZnode; + vector vchSig; + vector vchSig2; + int nBlockHeight; + int nRelayType; + CTxIn in; + CTxOut out; + + CDarkSendRelay(); + CDarkSendRelay(CTxIn& vinZnodeIn, vector& vchSigIn, int nBlockHeightIn, int nRelayTypeIn, CTxIn& in2, CTxOut& out2); + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(vinZnode); + READWRITE(vchSig); + READWRITE(vchSig2); + READWRITE(nBlockHeight); + READWRITE(nRelayType); + READWRITE(in); + READWRITE(out); + } + + std::string ToString(); + + bool Sign(std::string strSharedKey); + bool VerifyMessage(std::string strSharedKey); + void Relay(); + void RelayThroughNode(int nRank); +}; + + + +#endif diff --git a/src/darksend.cpp b/src/darksend.cpp new file mode 100644 index 0000000000..14e533bc41 --- /dev/null +++ b/src/darksend.cpp @@ -0,0 +1,2522 @@ +// Copyright (c) 2014-2017 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "activeznode.h" +#include "coincontrol.h" +#include "consensus/validation.h" +#include "darksend.h" +//#include "governance.h" +#include "init.h" +#include "instantx.h" +#include "znode-payments.h" +#include "znode-sync.h" +#include "znodeman.h" +#include "script/sign.h" +#include "txmempool.h" +#include "util.h" +#include "utilmoneystr.h" + +#include + +int nPrivateSendRounds = DEFAULT_PRIVATESEND_ROUNDS; +int nPrivateSendAmount = DEFAULT_PRIVATESEND_AMOUNT; +int nLiquidityProvider = DEFAULT_PRIVATESEND_LIQUIDITY; +bool fEnablePrivateSend = false; +bool fPrivateSendMultiSession = DEFAULT_PRIVATESEND_MULTISESSION; + +CDarksendPool darkSendPool; +CDarkSendSigner darkSendSigner; +std::map mapDarksendBroadcastTxes; +std::vector vecPrivateSendDenominations; + +void CDarksendPool::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) +{ + if(fLiteMode) return; // ignore all Dash related functionality + if(!znodeSync.IsBlockchainSynced()) return; + + if(strCommand == NetMsgType::DSACCEPT) { + + if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { + LogPrintf("DSACCEPT -- incompatible version! nVersion: %d\n", pfrom->nVersion); + PushStatus(pfrom, STATUS_REJECTED, ERR_VERSION); + return; + } + + if(!fZNode) { + LogPrintf("DSACCEPT -- not a Znode!\n"); + PushStatus(pfrom, STATUS_REJECTED, ERR_NOT_A_MN); + return; + } + + if(IsSessionReady()) { + // too many users in this session already, reject new ones + LogPrintf("DSACCEPT -- queue is already full!\n"); + PushStatus(pfrom, STATUS_ACCEPTED, ERR_QUEUE_FULL); + return; + } + + int nDenom; + CTransaction txCollateral; + vRecv >> nDenom >> txCollateral; + + LogPrint("privatesend", "DSACCEPT -- nDenom %d (%s) txCollateral %s", nDenom, GetDenominationsToString(nDenom), txCollateral.ToString()); + + CZnode* pmn = mnodeman.Find(activeZnode.vin); + if(pmn == NULL) { + PushStatus(pfrom, STATUS_REJECTED, ERR_MN_LIST); + return; + } + + if(vecSessionCollaterals.size() == 0 && pmn->nLastDsq != 0 && + pmn->nLastDsq + mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION)/5 > mnodeman.nDsqCount) + { + LogPrintf("DSACCEPT -- last dsq too recent, must wait: addr=%s\n", pfrom->addr.ToString()); + PushStatus(pfrom, STATUS_REJECTED, ERR_RECENT); + return; + } + + PoolMessage nMessageID = MSG_NOERR; + + bool fResult = nSessionID == 0 ? CreateNewSession(nDenom, txCollateral, nMessageID) + : AddUserToExistingSession(nDenom, txCollateral, nMessageID); + if(fResult) { + LogPrintf("DSACCEPT -- is compatible, please submit!\n"); + PushStatus(pfrom, STATUS_ACCEPTED, nMessageID); + return; + } else { + LogPrintf("DSACCEPT -- not compatible with existing transactions!\n"); + PushStatus(pfrom, STATUS_REJECTED, nMessageID); + return; + } + + } else if(strCommand == NetMsgType::DSQUEUE) { + TRY_LOCK(cs_darksend, lockRecv); + if(!lockRecv) return; + + if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { + LogPrint("privatesend", "DSQUEUE -- incompatible version! nVersion: %d\n", pfrom->nVersion); + return; + } + + CDarksendQueue dsq; + vRecv >> dsq; + + // process every dsq only once + BOOST_FOREACH(CDarksendQueue q, vecDarksendQueue) { + if(q == dsq) { + // LogPrint("privatesend", "DSQUEUE -- %s seen\n", dsq.ToString()); + return; + } + } + + LogPrint("privatesend", "DSQUEUE -- %s new\n", dsq.ToString()); + + if(dsq.IsExpired() || dsq.nTime > GetTime() + PRIVATESEND_QUEUE_TIMEOUT) return; + + CZnode* pmn = mnodeman.Find(dsq.vin); + if(pmn == NULL) return; + + if(!dsq.CheckSignature(pmn->pubKeyZnode)) { + // we probably have outdated info + mnodeman.AskForMN(pfrom, dsq.vin); + return; + } + + // if the queue is ready, submit if we can + if(dsq.fReady) { + if(!pSubmittedToZnode) return; + if((CNetAddr)pSubmittedToZnode->addr != (CNetAddr)pmn->addr) { + LogPrintf("DSQUEUE -- message doesn't match current Znode: pSubmittedToZnode=%s, addr=%s\n", pSubmittedToZnode->addr.ToString(), pmn->addr.ToString()); + return; + } + + if(nState == POOL_STATE_QUEUE) { + LogPrint("privatesend", "DSQUEUE -- PrivateSend queue (%s) is ready on znode %s\n", dsq.ToString(), pmn->addr.ToString()); + SubmitDenominate(); + } + } else { + BOOST_FOREACH(CDarksendQueue q, vecDarksendQueue) { + if(q.vin == dsq.vin) { + // no way same mn can send another "not yet ready" dsq this soon + LogPrint("privatesend", "DSQUEUE -- Znode %s is sending WAY too many dsq messages\n", pmn->addr.ToString()); + return; + } + } + + int nThreshold = pmn->nLastDsq + mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION)/5; + LogPrint("privatesend", "DSQUEUE -- nLastDsq: %d threshold: %d nDsqCount: %d\n", pmn->nLastDsq, nThreshold, mnodeman.nDsqCount); + //don't allow a few nodes to dominate the queuing process + if(pmn->nLastDsq != 0 && nThreshold > mnodeman.nDsqCount) { + LogPrint("privatesend", "DSQUEUE -- Znode %s is sending too many dsq messages\n", pmn->addr.ToString()); + return; + } + mnodeman.nDsqCount++; + pmn->nLastDsq = mnodeman.nDsqCount; + pmn->fAllowMixingTx = true; + + LogPrint("privatesend", "DSQUEUE -- new PrivateSend queue (%s) from znode %s\n", dsq.ToString(), pmn->addr.ToString()); + if(pSubmittedToZnode && pSubmittedToZnode->vin.prevout == dsq.vin.prevout) { + dsq.fTried = true; + } + vecDarksendQueue.push_back(dsq); + dsq.Relay(); + } + + } else if(strCommand == NetMsgType::DSVIN) { + + if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { + LogPrintf("DSVIN -- incompatible version! nVersion: %d\n", pfrom->nVersion); + PushStatus(pfrom, STATUS_REJECTED, ERR_VERSION); + return; + } + + if(!fZNode) { + LogPrintf("DSVIN -- not a Znode!\n"); + PushStatus(pfrom, STATUS_REJECTED, ERR_NOT_A_MN); + return; + } + + //do we have enough users in the current session? + if(!IsSessionReady()) { + LogPrintf("DSVIN -- session not complete!\n"); + PushStatus(pfrom, STATUS_REJECTED, ERR_SESSION); + return; + } + + CDarkSendEntry entry; + vRecv >> entry; + + LogPrint("privatesend", "DSVIN -- txCollateral %s", entry.txCollateral.ToString()); + + //do we have the same denominations as the current session? + if(!IsOutputsCompatibleWithSessionDenom(entry.vecTxDSOut)) { + LogPrintf("DSVIN -- not compatible with existing transactions!\n"); + PushStatus(pfrom, STATUS_REJECTED, ERR_EXISTING_TX); + return; + } + + //check it like a transaction + { + CAmount nValueIn = 0; + CAmount nValueOut = 0; + + CMutableTransaction tx; + + BOOST_FOREACH(const CTxOut txout, entry.vecTxDSOut) { + nValueOut += txout.nValue; + tx.vout.push_back(txout); + + if(txout.scriptPubKey.size() != 25) { + LogPrintf("DSVIN -- non-standard pubkey detected! scriptPubKey=%s\n", ScriptToAsmStr(txout.scriptPubKey)); + PushStatus(pfrom, STATUS_REJECTED, ERR_NON_STANDARD_PUBKEY); + return; + } + if(!txout.scriptPubKey.IsNormalPaymentScript()) { + LogPrintf("DSVIN -- invalid script! scriptPubKey=%s\n", ScriptToAsmStr(txout.scriptPubKey)); + PushStatus(pfrom, STATUS_REJECTED, ERR_INVALID_SCRIPT); + return; + } + } + + BOOST_FOREACH(const CTxIn txin, entry.vecTxDSIn) { + tx.vin.push_back(txin); + + LogPrint("privatesend", "DSVIN -- txin=%s\n", txin.ToString()); + + CTransaction txPrev; + uint256 hash; + if(GetTransaction(txin.prevout.hash, txPrev, Params().GetConsensus(), hash, true)) { + if(txPrev.vout.size() > txin.prevout.n) + nValueIn += txPrev.vout[txin.prevout.n].nValue; + } else { + LogPrintf("DSVIN -- missing input! tx=%s", tx.ToString()); + PushStatus(pfrom, STATUS_REJECTED, ERR_MISSING_TX); + return; + } + } + + if(nValueIn > PRIVATESEND_POOL_MAX) { + LogPrintf("DSVIN -- more than PrivateSend pool max! nValueIn: %lld, tx=%s", nValueIn, tx.ToString()); + PushStatus(pfrom, STATUS_REJECTED, ERR_MAXIMUM); + return; + } + + // Allow lowest denom (at max) as a a fee. Normally shouldn't happen though. + // TODO: Or do not allow fees at all? + if(nValueIn - nValueOut > vecPrivateSendDenominations.back()) { + LogPrintf("DSVIN -- fees are too high! fees: %lld, tx=%s", nValueIn - nValueOut, tx.ToString()); + PushStatus(pfrom, STATUS_REJECTED, ERR_FEES); + return; + } + + { + LOCK(cs_main); + CValidationState validationState; + mempool.PrioritiseTransaction(tx.GetHash(), tx.GetHash().ToString(), 1000, 0.1*COIN); + if(!AcceptToMemoryPool(mempool, validationState, CTransaction(tx), true, false, NULL, false, true, true)) { + LogPrintf("DSVIN -- transaction not valid! tx=%s", tx.ToString()); + PushStatus(pfrom, STATUS_REJECTED, ERR_INVALID_TX); + return; + } + } + } + + PoolMessage nMessageID = MSG_NOERR; + + if(AddEntry(entry, nMessageID)) { + PushStatus(pfrom, STATUS_ACCEPTED, nMessageID); + CheckPool(); + RelayStatus(STATUS_ACCEPTED); + } else { + PushStatus(pfrom, STATUS_REJECTED, nMessageID); + SetNull(); + } + + } else if(strCommand == NetMsgType::DSSTATUSUPDATE) { + + if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { + LogPrintf("DSSTATUSUPDATE -- incompatible version! nVersion: %d\n", pfrom->nVersion); + return; + } + + if(fZNode) { + // LogPrintf("DSSTATUSUPDATE -- Can't run on a Znode!\n"); + return; + } + + if(!pSubmittedToZnode) return; + if((CNetAddr)pSubmittedToZnode->addr != (CNetAddr)pfrom->addr) { + //LogPrintf("DSSTATUSUPDATE -- message doesn't match current Znode: pSubmittedToZnode %s addr %s\n", pSubmittedToZnode->addr.ToString(), pfrom->addr.ToString()); + return; + } + + int nMsgSessionID; + int nMsgState; + int nMsgEntriesCount; + int nMsgStatusUpdate; + int nMsgMessageID; + vRecv >> nMsgSessionID >> nMsgState >> nMsgEntriesCount >> nMsgStatusUpdate >> nMsgMessageID; + + LogPrint("privatesend", "DSSTATUSUPDATE -- nMsgSessionID %d nMsgState: %d nEntriesCount: %d nMsgStatusUpdate: %d nMsgMessageID %d\n", + nMsgSessionID, nMsgState, nEntriesCount, nMsgStatusUpdate, nMsgMessageID); + + if(nMsgState < POOL_STATE_MIN || nMsgState > POOL_STATE_MAX) { + LogPrint("privatesend", "DSSTATUSUPDATE -- nMsgState is out of bounds: %d\n", nMsgState); + return; + } + + if(nMsgStatusUpdate < STATUS_REJECTED || nMsgStatusUpdate > STATUS_ACCEPTED) { + LogPrint("privatesend", "DSSTATUSUPDATE -- nMsgStatusUpdate is out of bounds: %d\n", nMsgStatusUpdate); + return; + } + + if(nMsgMessageID < MSG_POOL_MIN || nMsgMessageID > MSG_POOL_MAX) { + LogPrint("privatesend", "DSSTATUSUPDATE -- nMsgMessageID is out of bounds: %d\n", nMsgMessageID); + return; + } + + LogPrint("privatesend", "DSSTATUSUPDATE -- GetMessageByID: %s\n", GetMessageByID(PoolMessage(nMsgMessageID))); + + if(!CheckPoolStateUpdate(PoolState(nMsgState), nMsgEntriesCount, PoolStatusUpdate(nMsgStatusUpdate), PoolMessage(nMsgMessageID), nMsgSessionID)) { + LogPrint("privatesend", "DSSTATUSUPDATE -- CheckPoolStateUpdate failed\n"); + } + + } else if(strCommand == NetMsgType::DSSIGNFINALTX) { + + if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { + LogPrintf("DSSIGNFINALTX -- incompatible version! nVersion: %d\n", pfrom->nVersion); + return; + } + + if(!fZNode) { + LogPrintf("DSSIGNFINALTX -- not a Znode!\n"); + return; + } + + std::vector vecTxIn; + vRecv >> vecTxIn; + + LogPrint("privatesend", "DSSIGNFINALTX -- vecTxIn.size() %s\n", vecTxIn.size()); + + int nTxInIndex = 0; + int nTxInsCount = (int)vecTxIn.size(); + + BOOST_FOREACH(const CTxIn txin, vecTxIn) { + nTxInIndex++; + if(!AddScriptSig(txin)) { + LogPrint("privatesend", "DSSIGNFINALTX -- AddScriptSig() failed at %d/%d, session: %d\n", nTxInIndex, nTxInsCount, nSessionID); + RelayStatus(STATUS_REJECTED); + return; + } + LogPrint("privatesend", "DSSIGNFINALTX -- AddScriptSig() %d/%d success\n", nTxInIndex, nTxInsCount); + } + // all is good + CheckPool(); + + } else if(strCommand == NetMsgType::DSFINALTX) { + + if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { + LogPrintf("DSFINALTX -- incompatible version! nVersion: %d\n", pfrom->nVersion); + return; + } + + if(fZNode) { + // LogPrintf("DSFINALTX -- Can't run on a Znode!\n"); + return; + } + + if(!pSubmittedToZnode) return; + if((CNetAddr)pSubmittedToZnode->addr != (CNetAddr)pfrom->addr) { + //LogPrintf("DSFINALTX -- message doesn't match current Znode: pSubmittedToZnode %s addr %s\n", pSubmittedToZnode->addr.ToString(), pfrom->addr.ToString()); + return; + } + + int nMsgSessionID; + CTransaction txNew; + vRecv >> nMsgSessionID >> txNew; + + if(nSessionID != nMsgSessionID) { + LogPrint("privatesend", "DSFINALTX -- message doesn't match current PrivateSend session: nSessionID: %d nMsgSessionID: %d\n", nSessionID, nMsgSessionID); + return; + } + + LogPrint("privatesend", "DSFINALTX -- txNew %s", txNew.ToString()); + + //check to see if input is spent already? (and probably not confirmed) + SignFinalTransaction(txNew, pfrom); + + } else if(strCommand == NetMsgType::DSCOMPLETE) { + + if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { + LogPrintf("DSCOMPLETE -- incompatible version! nVersion: %d\n", pfrom->nVersion); + return; + } + + if(fZNode) { + // LogPrintf("DSCOMPLETE -- Can't run on a Znode!\n"); + return; + } + + if(!pSubmittedToZnode) return; + if((CNetAddr)pSubmittedToZnode->addr != (CNetAddr)pfrom->addr) { + LogPrint("privatesend", "DSCOMPLETE -- message doesn't match current Znode: pSubmittedToZnode=%s addr=%s\n", pSubmittedToZnode->addr.ToString(), pfrom->addr.ToString()); + return; + } + + int nMsgSessionID; + int nMsgMessageID; + vRecv >> nMsgSessionID >> nMsgMessageID; + + if(nMsgMessageID < MSG_POOL_MIN || nMsgMessageID > MSG_POOL_MAX) { + LogPrint("privatesend", "DSCOMPLETE -- nMsgMessageID is out of bounds: %d\n", nMsgMessageID); + return; + } + + if(nSessionID != nMsgSessionID) { + LogPrint("privatesend", "DSCOMPLETE -- message doesn't match current PrivateSend session: nSessionID: %d nMsgSessionID: %d\n", darkSendPool.nSessionID, nMsgSessionID); + return; + } + + LogPrint("privatesend", "DSCOMPLETE -- nMsgSessionID %d nMsgMessageID %d (%s)\n", nMsgSessionID, nMsgMessageID, GetMessageByID(PoolMessage(nMsgMessageID))); + + CompletedTransaction(PoolMessage(nMsgMessageID)); + } +} + +void CDarksendPool::InitDenominations() +{ + vecPrivateSendDenominations.clear(); + /* Denominations + + A note about convertability. Within mixing pools, each denomination + is convertable to another. + + For example: + 1DRK+1000 == (.1DRK+100)*10 + 10DRK+10000 == (1DRK+1000)*10 + */ + /* Disabled + vecPrivateSendDenominations.push_back( (100 * COIN)+100000 ); + */ + vecPrivateSendDenominations.push_back( (10 * COIN)+10000 ); + vecPrivateSendDenominations.push_back( (1 * COIN)+1000 ); + vecPrivateSendDenominations.push_back( (.1 * COIN)+100 ); + vecPrivateSendDenominations.push_back( (.01 * COIN)+10 ); + /* Disabled till we need them + vecPrivateSendDenominations.push_back( (.001 * COIN)+1 ); + */ +} + +void CDarksendPool::ResetPool() +{ + nCachedLastSuccessBlock = 0; + txMyCollateral = CMutableTransaction(); + vecZnodesUsed.clear(); + UnlockCoins(); + SetNull(); +} + +void CDarksendPool::SetNull() +{ + // MN side + vecSessionCollaterals.clear(); + + // Client side + nEntriesCount = 0; + fLastEntryAccepted = false; + pSubmittedToZnode = NULL; + + // Both sides + nState = POOL_STATE_IDLE; + nSessionID = 0; + nSessionDenom = 0; + vecEntries.clear(); + finalMutableTransaction.vin.clear(); + finalMutableTransaction.vout.clear(); + nTimeLastSuccessfulStep = GetTimeMillis(); +} + +// +// Unlock coins after mixing fails or succeeds +// +void CDarksendPool::UnlockCoins() +{ + while(true) { + TRY_LOCK(pwalletMain->cs_wallet, lockWallet); + if(!lockWallet) {MilliSleep(50); continue;} + BOOST_FOREACH(COutPoint outpoint, vecOutPointLocked) + pwalletMain->UnlockCoin(outpoint); + break; + } + + vecOutPointLocked.clear(); +} + +std::string CDarksendPool::GetStateString() const +{ + switch(nState) { + case POOL_STATE_IDLE: return "IDLE"; + case POOL_STATE_QUEUE: return "QUEUE"; + case POOL_STATE_ACCEPTING_ENTRIES: return "ACCEPTING_ENTRIES"; + case POOL_STATE_SIGNING: return "SIGNING"; + case POOL_STATE_ERROR: return "ERROR"; + case POOL_STATE_SUCCESS: return "SUCCESS"; + default: return "UNKNOWN"; + } +} + +std::string CDarksendPool::GetStatus() +{ + static int nStatusMessageProgress = 0; + nStatusMessageProgress += 10; + std::string strSuffix = ""; + + if((pCurrentBlockIndex && pCurrentBlockIndex->nHeight - nCachedLastSuccessBlock < nMinBlockSpacing) || !znodeSync.IsBlockchainSynced()) + return strAutoDenomResult; + + switch(nState) { + case POOL_STATE_IDLE: + return _("PrivateSend is idle."); + case POOL_STATE_QUEUE: + if( nStatusMessageProgress % 70 <= 30) strSuffix = "."; + else if(nStatusMessageProgress % 70 <= 50) strSuffix = ".."; + else if(nStatusMessageProgress % 70 <= 70) strSuffix = "..."; + return strprintf(_("Submitted to znode, waiting in queue %s"), strSuffix);; + case POOL_STATE_ACCEPTING_ENTRIES: + if(nEntriesCount == 0) { + nStatusMessageProgress = 0; + return strAutoDenomResult; + } else if(fLastEntryAccepted) { + if(nStatusMessageProgress % 10 > 8) { + fLastEntryAccepted = false; + nStatusMessageProgress = 0; + } + return _("PrivateSend request complete:") + " " + _("Your transaction was accepted into the pool!"); + } else { + if( nStatusMessageProgress % 70 <= 40) return strprintf(_("Submitted following entries to znode: %u / %d"), nEntriesCount, GetMaxPoolTransactions()); + else if(nStatusMessageProgress % 70 <= 50) strSuffix = "."; + else if(nStatusMessageProgress % 70 <= 60) strSuffix = ".."; + else if(nStatusMessageProgress % 70 <= 70) strSuffix = "..."; + return strprintf(_("Submitted to znode, waiting for more entries ( %u / %d ) %s"), nEntriesCount, GetMaxPoolTransactions(), strSuffix); + } + case POOL_STATE_SIGNING: + if( nStatusMessageProgress % 70 <= 40) return _("Found enough users, signing ..."); + else if(nStatusMessageProgress % 70 <= 50) strSuffix = "."; + else if(nStatusMessageProgress % 70 <= 60) strSuffix = ".."; + else if(nStatusMessageProgress % 70 <= 70) strSuffix = "..."; + return strprintf(_("Found enough users, signing ( waiting %s )"), strSuffix); + case POOL_STATE_ERROR: + return _("PrivateSend request incomplete:") + " " + strLastMessage + " " + _("Will retry..."); + case POOL_STATE_SUCCESS: + return _("PrivateSend request complete:") + " " + strLastMessage; + default: + return strprintf(_("Unknown state: id = %u"), nState); + } +} + +// +// Check the mixing progress and send client updates if a Znode +// +void CDarksendPool::CheckPool() +{ + if(fZNode) { + LogPrint("privatesend", "CDarksendPool::CheckPool -- entries count %lu\n", GetEntriesCount()); + + // If entries are full, create finalized transaction + if(nState == POOL_STATE_ACCEPTING_ENTRIES && GetEntriesCount() >= GetMaxPoolTransactions()) { + LogPrint("privatesend", "CDarksendPool::CheckPool -- FINALIZE TRANSACTIONS\n"); + CreateFinalTransaction(); + return; + } + + // If we have all of the signatures, try to compile the transaction + if(nState == POOL_STATE_SIGNING && IsSignaturesComplete()) { + LogPrint("privatesend", "CDarksendPool::CheckPool -- SIGNING\n"); + CommitFinalTransaction(); + return; + } + } + + // reset if we're here for 10 seconds + if((nState == POOL_STATE_ERROR || nState == POOL_STATE_SUCCESS) && GetTimeMillis() - nTimeLastSuccessfulStep >= 10000) { + LogPrint("privatesend", "CDarksendPool::CheckPool -- timeout, RESETTING\n"); + UnlockCoins(); + SetNull(); + } +} + +void CDarksendPool::CreateFinalTransaction() +{ + LogPrint("privatesend", "CDarksendPool::CreateFinalTransaction -- FINALIZE TRANSACTIONS\n"); + + CMutableTransaction txNew; + + // make our new transaction + for(int i = 0; i < GetEntriesCount(); i++) { + BOOST_FOREACH(const CTxDSOut& txdsout, vecEntries[i].vecTxDSOut) + txNew.vout.push_back(txdsout); + + BOOST_FOREACH(const CTxDSIn& txdsin, vecEntries[i].vecTxDSIn) + txNew.vin.push_back(txdsin); + } + + // BIP69 https://github.com/kristovatlas/bips/blob/master/bip-0069.mediawiki + sort(txNew.vin.begin(), txNew.vin.end()); + sort(txNew.vout.begin(), txNew.vout.end()); + + finalMutableTransaction = txNew; + LogPrint("privatesend", "CDarksendPool::CreateFinalTransaction -- finalMutableTransaction=%s", txNew.ToString()); + + // request signatures from clients + RelayFinalTransaction(finalMutableTransaction); + SetState(POOL_STATE_SIGNING); +} + +void CDarksendPool::CommitFinalTransaction() +{ + if(!fZNode) return; // check and relay final tx only on znode + + CTransaction finalTransaction = CTransaction(finalMutableTransaction); + uint256 hashTx = finalTransaction.GetHash(); + + LogPrint("privatesend", "CDarksendPool::CommitFinalTransaction -- finalTransaction=%s", finalTransaction.ToString()); + + { + // See if the transaction is valid + TRY_LOCK(cs_main, lockMain); + CValidationState validationState; + mempool.PrioritiseTransaction(hashTx, hashTx.ToString(), 1000, 0.1*COIN); + if(!lockMain || !AcceptToMemoryPool(mempool, validationState, finalTransaction, true, false, NULL, false, true, true)) + { + LogPrintf("CDarksendPool::CommitFinalTransaction -- AcceptToMemoryPool() error: Transaction not valid\n"); + SetNull(); + // not much we can do in this case, just notify clients + RelayCompletedTransaction(ERR_INVALID_TX); + return; + } + } + + LogPrintf("CDarksendPool::CommitFinalTransaction -- CREATING DSTX\n"); + + // create and sign znode dstx transaction + if(!mapDarksendBroadcastTxes.count(hashTx)) { + CDarksendBroadcastTx dstx(finalTransaction, activeZnode.vin, GetAdjustedTime()); + dstx.Sign(); + mapDarksendBroadcastTxes.insert(std::make_pair(hashTx, dstx)); + } + + LogPrintf("CDarksendPool::CommitFinalTransaction -- TRANSMITTING DSTX\n"); + + CInv inv(MSG_DSTX, hashTx); + RelayInv(inv); + + // Tell the clients it was successful + RelayCompletedTransaction(MSG_SUCCESS); + + // Randomly charge clients + ChargeRandomFees(); + + // Reset + LogPrint("privatesend", "CDarksendPool::CommitFinalTransaction -- COMPLETED -- RESETTING\n"); + SetNull(); +} + +// +// Charge clients a fee if they're abusive +// +// Why bother? PrivateSend uses collateral to ensure abuse to the process is kept to a minimum. +// The submission and signing stages 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 Znodes come in via NetMsgType::DSVIN, these require a valid collateral +// transaction for the client to be able to enter the pool. This transaction is kept by the Znode +// until the transaction is either complete or fails. +// +void CDarksendPool::ChargeFees() +{ + if(!fZNode) return; + + //we don't need to charge collateral for every offence. + if(GetRandInt(100) > 33) return; + + std::vector vecOffendersCollaterals; + + if(nState == POOL_STATE_ACCEPTING_ENTRIES) { + BOOST_FOREACH(const CTransaction& txCollateral, vecSessionCollaterals) { + bool fFound = false; + BOOST_FOREACH(const CDarkSendEntry& entry, vecEntries) + if(entry.txCollateral == txCollateral) + fFound = true; + + // This queue entry didn't send us the promised transaction + if(!fFound) { + LogPrintf("CDarksendPool::ChargeFees -- found uncooperative node (didn't send transaction), found offence\n"); + vecOffendersCollaterals.push_back(txCollateral); + } + } + } + + if(nState == POOL_STATE_SIGNING) { + // who didn't sign? + BOOST_FOREACH(const CDarkSendEntry entry, vecEntries) { + BOOST_FOREACH(const CTxDSIn txdsin, entry.vecTxDSIn) { + if(!txdsin.fHasSig) { + LogPrintf("CDarksendPool::ChargeFees -- found uncooperative node (didn't sign), found offence\n"); + vecOffendersCollaterals.push_back(entry.txCollateral); + } + } + } + } + + // no offences found + if(vecOffendersCollaterals.empty()) return; + + //mostly offending? Charge sometimes + if((int)vecOffendersCollaterals.size() >= Params().PoolMaxTransactions() - 1 && GetRandInt(100) > 33) return; + + //everyone is an offender? That's not right + if((int)vecOffendersCollaterals.size() >= Params().PoolMaxTransactions()) return; + + //charge one of the offenders randomly + std::random_shuffle(vecOffendersCollaterals.begin(), vecOffendersCollaterals.end()); + + if(nState == POOL_STATE_ACCEPTING_ENTRIES || nState == POOL_STATE_SIGNING) { + LogPrintf("CDarksendPool::ChargeFees -- found uncooperative node (didn't %s transaction), charging fees: %s\n", + (nState == POOL_STATE_SIGNING) ? "sign" : "send", vecOffendersCollaterals[0].ToString()); + + LOCK(cs_main); + + CValidationState state; + bool fMissingInputs; + if(!AcceptToMemoryPool(mempool, state, vecOffendersCollaterals[0],true, false, &fMissingInputs, false, true)) { + // should never really happen + LogPrintf("CDarksendPool::ChargeFees -- ERROR: AcceptToMemoryPool failed!\n"); + } else { + RelayTransaction(vecOffendersCollaterals[0]); + } + } +} + +/* + Charge the collateral randomly. + Mixing is completely free, to pay miners we randomly pay the collateral of users. + + Collateral Fee Charges: + + Being that mixing has "no fees" we need to have some kind of cost associated + with using it to stop abuse. Otherwise it could serve as an attack vector and + allow endless transaction that would bloat Dash and make it unusable. To + stop these kinds of attacks 1 in 10 successful transactions are charged. This + adds up to a cost of 0.001DRK per transaction on average. +*/ +void CDarksendPool::ChargeRandomFees() +{ + if(!fZNode) return; + + LOCK(cs_main); + + BOOST_FOREACH(const CTransaction& txCollateral, vecSessionCollaterals) { + + if(GetRandInt(100) > 10) return; + + LogPrintf("CDarksendPool::ChargeRandomFees -- charging random fees, txCollateral=%s", txCollateral.ToString()); + + CValidationState state; + bool fMissingInputs; + if(!AcceptToMemoryPool(mempool, state, txCollateral,true, false, &fMissingInputs, false, true)) { + // should never really happen + LogPrintf("CDarksendPool::ChargeRandomFees -- ERROR: AcceptToMemoryPool failed!\n"); + } else { + RelayTransaction(txCollateral); + } + } +} + +// +// Check for various timeouts (queue objects, mixing, etc) +// +void CDarksendPool::CheckTimeout() +{ + { + TRY_LOCK(cs_darksend, lockDS); + if(!lockDS) return; // it's ok to fail here, we run this quite frequently + + // check mixing queue objects for timeouts + std::vector::iterator it = vecDarksendQueue.begin(); + while(it != vecDarksendQueue.end()) { + if((*it).IsExpired()) { + LogPrint("privatesend", "CDarksendPool::CheckTimeout -- Removing expired queue (%s)\n", (*it).ToString()); + it = vecDarksendQueue.erase(it); + } else ++it; + } + } + + if(!fEnablePrivateSend && !fZNode) return; + + // catching hanging sessions + if(!fZNode) { + switch(nState) { + case POOL_STATE_ERROR: + LogPrint("privatesend", "CDarksendPool::CheckTimeout -- Pool error -- Running CheckPool\n"); + CheckPool(); + break; + case POOL_STATE_SUCCESS: + LogPrint("privatesend", "CDarksendPool::CheckTimeout -- Pool success -- Running CheckPool\n"); + CheckPool(); + break; + default: + break; + } + } + + int nLagTime = fZNode ? 0 : 10000; // if we're the client, give the server a few extra seconds before resetting. + int nTimeout = (nState == POOL_STATE_SIGNING) ? PRIVATESEND_SIGNING_TIMEOUT : PRIVATESEND_QUEUE_TIMEOUT; + bool fTimeout = GetTimeMillis() - nTimeLastSuccessfulStep >= nTimeout*1000 + nLagTime; + + if(nState != POOL_STATE_IDLE && fTimeout) { + LogPrint("privatesend", "CDarksendPool::CheckTimeout -- %s timed out (%ds) -- restting\n", + (nState == POOL_STATE_SIGNING) ? "Signing" : "Session", nTimeout); + ChargeFees(); + UnlockCoins(); + SetNull(); + SetState(POOL_STATE_ERROR); + strLastMessage = _("Session timed out."); + } +} + +/* + Check to see if we're ready for submissions from clients + After receiving multiple dsa messages, the queue will switch to "accepting entries" + which is the active state right before merging the transaction +*/ +void CDarksendPool::CheckForCompleteQueue() +{ + if(!fEnablePrivateSend && !fZNode) return; + + if(nState == POOL_STATE_QUEUE && IsSessionReady()) { + SetState(POOL_STATE_ACCEPTING_ENTRIES); + + CDarksendQueue dsq(nSessionDenom, activeZnode.vin, GetTime(), true); + LogPrint("privatesend", "CDarksendPool::CheckForCompleteQueue -- queue is ready, signing and relaying (%s)\n", dsq.ToString()); + dsq.Sign(); + dsq.Relay(); + } +} + +// Check to make sure a given input matches an input in the pool and its scriptSig is valid +bool CDarksendPool::IsInputScriptSigValid(const CTxIn& txin) +{ + CMutableTransaction txNew; + txNew.vin.clear(); + txNew.vout.clear(); + + int i = 0; + int nTxInIndex = -1; + CScript sigPubKey = CScript(); + + BOOST_FOREACH(CDarkSendEntry& entry, vecEntries) { + + BOOST_FOREACH(const CTxDSOut& txdsout, entry.vecTxDSOut) + txNew.vout.push_back(txdsout); + + BOOST_FOREACH(const CTxDSIn& txdsin, entry.vecTxDSIn) { + txNew.vin.push_back(txdsin); + + if(txdsin.prevout == txin.prevout) { + nTxInIndex = i; + sigPubKey = txdsin.prevPubKey; + } + i++; + } + } + + if(nTxInIndex >= 0) { //might have to do this one input at a time? + txNew.vin[nTxInIndex].scriptSig = txin.scriptSig; + const CAmount& amount = txNew.vout[nTxInIndex].nValue; + LogPrint("privatesend", "CDarksendPool::IsInputScriptSigValid -- verifying scriptSig %s\n", ScriptToAsmStr(txin.scriptSig).substr(0,24)); +// if(!VerifyScript(txNew.vin[nTxInIndex].scriptSig, sigPubKey, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, MutableTransactionSignatureChecker(&txNew, nTxInIndex, amount))) { +// LogPrint("privatesend", "CDarksendPool::IsInputScriptSigValid -- VerifyScript() failed on input %d\n", nTxInIndex); +// return false; +// } + } else { + LogPrint("privatesend", "CDarksendPool::IsInputScriptSigValid -- Failed to find matching input in pool, %s\n", txin.ToString()); + return false; + } + + LogPrint("privatesend", "CDarksendPool::IsInputScriptSigValid -- Successfully validated input and scriptSig\n"); + return true; +} + +// check to make sure the collateral provided by the client is valid +bool CDarksendPool::IsCollateralValid(const CTransaction& txCollateral) +{ + if(txCollateral.vout.empty()) return false; + if(txCollateral.nLockTime != 0) return false; + + CAmount nValueIn = 0; + CAmount nValueOut = 0; + bool fMissingTx = false; + + BOOST_FOREACH(const CTxOut txout, txCollateral.vout) { + nValueOut += txout.nValue; + + if(!txout.scriptPubKey.IsNormalPaymentScript()) { + LogPrintf ("CDarksendPool::IsCollateralValid -- Invalid Script, txCollateral=%s", txCollateral.ToString()); + return false; + } + } + + BOOST_FOREACH(const CTxIn txin, txCollateral.vin) { + CTransaction txPrev; + uint256 hash; + if(GetTransaction(txin.prevout.hash, txPrev, Params().GetConsensus(), hash, true)) { + if(txPrev.vout.size() > txin.prevout.n) + nValueIn += txPrev.vout[txin.prevout.n].nValue; + } else { + fMissingTx = true; + } + } + + if(fMissingTx) { + LogPrint("privatesend", "CDarksendPool::IsCollateralValid -- Unknown inputs in collateral transaction, txCollateral=%s", txCollateral.ToString()); + return false; + } + + //collateral transactions are required to pay out PRIVATESEND_COLLATERAL as a fee to the miners + if(nValueIn - nValueOut < PRIVATESEND_COLLATERAL) { + LogPrint("privatesend", "CDarksendPool::IsCollateralValid -- did not include enough fees in transaction: fees: %d, txCollateral=%s", nValueOut - nValueIn, txCollateral.ToString()); + return false; + } + + LogPrint("privatesend", "CDarksendPool::IsCollateralValid -- %s", txCollateral.ToString()); + + { + LOCK(cs_main); + CValidationState validationState; + if(!AcceptToMemoryPool(mempool, validationState, txCollateral, true, false, NULL, false, true, true)) { + LogPrint("privatesend", "CDarksendPool::IsCollateralValid -- didn't pass AcceptToMemoryPool()\n"); + return false; + } + } + + return true; +} + + +// +// Add a clients transaction to the pool +// +bool CDarksendPool::AddEntry(const CDarkSendEntry& entryNew, PoolMessage& nMessageIDRet) +{ + if(!fZNode) return false; + + BOOST_FOREACH(CTxIn txin, entryNew.vecTxDSIn) { + if(txin.prevout.IsNull()) { + LogPrint("privatesend", "CDarksendPool::AddEntry -- input not valid!\n"); + nMessageIDRet = ERR_INVALID_INPUT; + return false; + } + } + + if(!IsCollateralValid(entryNew.txCollateral)) { + LogPrint("privatesend", "CDarksendPool::AddEntry -- collateral not valid!\n"); + nMessageIDRet = ERR_INVALID_COLLATERAL; + return false; + } + + if(GetEntriesCount() >= GetMaxPoolTransactions()) { + LogPrint("privatesend", "CDarksendPool::AddEntry -- entries is full!\n"); + nMessageIDRet = ERR_ENTRIES_FULL; + return false; + } + + BOOST_FOREACH(CTxIn txin, entryNew.vecTxDSIn) { + LogPrint("privatesend", "looking for txin -- %s\n", txin.ToString()); + BOOST_FOREACH(const CDarkSendEntry& entry, vecEntries) { + BOOST_FOREACH(const CTxDSIn& txdsin, entry.vecTxDSIn) { + if(txdsin.prevout == txin.prevout) { + LogPrint("privatesend", "CDarksendPool::AddEntry -- found in txin\n"); + nMessageIDRet = ERR_ALREADY_HAVE; + return false; + } + } + } + } + + vecEntries.push_back(entryNew); + + LogPrint("privatesend", "CDarksendPool::AddEntry -- adding entry\n"); + nMessageIDRet = MSG_ENTRIES_ADDED; + nTimeLastSuccessfulStep = GetTimeMillis(); + + return true; +} + +bool CDarksendPool::AddScriptSig(const CTxIn& txinNew) +{ + LogPrint("privatesend", "CDarksendPool::AddScriptSig -- scriptSig=%s\n", ScriptToAsmStr(txinNew.scriptSig).substr(0,24)); + + BOOST_FOREACH(const CDarkSendEntry& entry, vecEntries) { + BOOST_FOREACH(const CTxDSIn& txdsin, entry.vecTxDSIn) { + if(txdsin.scriptSig == txinNew.scriptSig) { + LogPrint("privatesend", "CDarksendPool::AddScriptSig -- already exists\n"); + return false; + } + } + } + + if(!IsInputScriptSigValid(txinNew)) { + LogPrint("privatesend", "CDarksendPool::AddScriptSig -- Invalid scriptSig\n"); + return false; + } + + LogPrint("privatesend", "CDarksendPool::AddScriptSig -- scriptSig=%s new\n", ScriptToAsmStr(txinNew.scriptSig).substr(0,24)); + + BOOST_FOREACH(CTxIn& txin, finalMutableTransaction.vin) { + if(txinNew.prevout == txin.prevout && txin.nSequence == txinNew.nSequence) { + txin.scriptSig = txinNew.scriptSig; + txin.prevPubKey = txinNew.prevPubKey; + LogPrint("privatesend", "CDarksendPool::AddScriptSig -- adding to finalMutableTransaction, scriptSig=%s\n", ScriptToAsmStr(txinNew.scriptSig).substr(0,24)); + } + } + for(int i = 0; i < GetEntriesCount(); i++) { + if(vecEntries[i].AddScriptSig(txinNew)) { + LogPrint("privatesend", "CDarksendPool::AddScriptSig -- adding to entries, scriptSig=%s\n", ScriptToAsmStr(txinNew.scriptSig).substr(0,24)); + return true; + } + } + + LogPrintf("CDarksendPool::AddScriptSig -- Couldn't set sig!\n" ); + return false; +} + +// Check to make sure everything is signed +bool CDarksendPool::IsSignaturesComplete() +{ + BOOST_FOREACH(const CDarkSendEntry& entry, vecEntries) + BOOST_FOREACH(const CTxDSIn& txdsin, entry.vecTxDSIn) + if(!txdsin.fHasSig) return false; + + return true; +} + +// +// Execute a mixing denomination via a Znode. +// This is only ran from clients +// +bool CDarksendPool::SendDenominate(const std::vector& vecTxIn, const std::vector& vecTxOut) +{ + if(fZNode) { + LogPrintf("CDarksendPool::SendDenominate -- PrivateSend from a Znode is not supported currently.\n"); + return false; + } + + if(txMyCollateral == CMutableTransaction()) { + LogPrintf("CDarksendPool:SendDenominate -- PrivateSend collateral not set\n"); + return false; + } + + // lock the funds we're going to use + BOOST_FOREACH(CTxIn txin, txMyCollateral.vin) + vecOutPointLocked.push_back(txin.prevout); + + BOOST_FOREACH(CTxIn txin, vecTxIn) + vecOutPointLocked.push_back(txin.prevout); + + // we should already be connected to a Znode + if(!nSessionID) { + LogPrintf("CDarksendPool::SendDenominate -- No Znode has been selected yet.\n"); + UnlockCoins(); + SetNull(); + return false; + } + + if(!CheckDiskSpace()) { + UnlockCoins(); + SetNull(); + fEnablePrivateSend = false; + LogPrintf("CDarksendPool::SendDenominate -- Not enough disk space, disabling PrivateSend.\n"); + return false; + } + + SetState(POOL_STATE_ACCEPTING_ENTRIES); + strLastMessage = ""; + + LogPrintf("CDarksendPool::SendDenominate -- Added transaction to pool.\n"); + + //check it against the memory pool to make sure it's valid + { + CValidationState validationState; + CMutableTransaction tx; + + BOOST_FOREACH(const CTxIn& txin, vecTxIn) { + LogPrint("privatesend", "CDarksendPool::SendDenominate -- txin=%s\n", txin.ToString()); + tx.vin.push_back(txin); + } + + BOOST_FOREACH(const CTxOut& txout, vecTxOut) { + LogPrint("privatesend", "CDarksendPool::SendDenominate -- txout=%s\n", txout.ToString()); + tx.vout.push_back(txout); + } + + LogPrintf("CDarksendPool::SendDenominate -- Submitting partial tx %s", tx.ToString()); + + mempool.PrioritiseTransaction(tx.GetHash(), tx.GetHash().ToString(), 1000, 0.1*COIN); + TRY_LOCK(cs_main, lockMain); + if(!lockMain || !AcceptToMemoryPool(mempool, validationState, CTransaction(tx), true, false, NULL, false, true, true)) { + LogPrintf("CDarksendPool::SendDenominate -- AcceptToMemoryPool() failed! tx=%s", tx.ToString()); + UnlockCoins(); + SetNull(); + return false; + } + } + + // store our entry for later use + CDarkSendEntry entry(vecTxIn, vecTxOut, txMyCollateral); + vecEntries.push_back(entry); + RelayIn(entry); + nTimeLastSuccessfulStep = GetTimeMillis(); + + return true; +} + +// Incoming message from Znode updating the progress of mixing +bool CDarksendPool::CheckPoolStateUpdate(PoolState nStateNew, int nEntriesCountNew, PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID, int nSessionIDNew) +{ + if(fZNode) return false; + + // do not update state when mixing client state is one of these + if(nState == POOL_STATE_IDLE || nState == POOL_STATE_ERROR || nState == POOL_STATE_SUCCESS) return false; + + strAutoDenomResult = _("Znode:") + " " + GetMessageByID(nMessageID); + + // if rejected at any state + if(nStatusUpdate == STATUS_REJECTED) { + LogPrintf("CDarksendPool::CheckPoolStateUpdate -- entry is rejected by Znode\n"); + UnlockCoins(); + SetNull(); + SetState(POOL_STATE_ERROR); + strLastMessage = GetMessageByID(nMessageID); + return true; + } + + if(nStatusUpdate == STATUS_ACCEPTED && nState == nStateNew) { + if(nStateNew == POOL_STATE_QUEUE && nSessionID == 0 && nSessionIDNew != 0) { + // new session id should be set only in POOL_STATE_QUEUE state + nSessionID = nSessionIDNew; + nTimeLastSuccessfulStep = GetTimeMillis(); + LogPrintf("CDarksendPool::CheckPoolStateUpdate -- set nSessionID to %d\n", nSessionID); + return true; + } + else if(nStateNew == POOL_STATE_ACCEPTING_ENTRIES && nEntriesCount != nEntriesCountNew) { + nEntriesCount = nEntriesCountNew; + nTimeLastSuccessfulStep = GetTimeMillis(); + fLastEntryAccepted = true; + LogPrintf("CDarksendPool::CheckPoolStateUpdate -- new entry accepted!\n"); + return true; + } + } + + // only situations above are allowed, fail in any other case + return false; +} + +// +// After we receive the finalized transaction from the Znode, 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(const CTransaction& finalTransactionNew, CNode* pnode) +{ + if(fZNode || pnode == NULL) return false; + + finalMutableTransaction = finalTransactionNew; + LogPrintf("CDarksendPool::SignFinalTransaction -- finalMutableTransaction=%s", finalMutableTransaction.ToString()); + + std::vector sigs; + + //make sure my inputs/outputs are present, otherwise refuse to sign + BOOST_FOREACH(const CDarkSendEntry entry, vecEntries) { + BOOST_FOREACH(const CTxDSIn txdsin, entry.vecTxDSIn) { + /* Sign my transaction and all outputs */ + int nMyInputIndex = -1; + CScript prevPubKey = CScript(); + CTxIn txin = CTxIn(); + + for(unsigned int i = 0; i < finalMutableTransaction.vin.size(); i++) { + if(finalMutableTransaction.vin[i] == txdsin) { + nMyInputIndex = i; + prevPubKey = txdsin.prevPubKey; + txin = txdsin; + } + } + + if(nMyInputIndex >= 0) { //might have to do this one input at a time? + int nFoundOutputsCount = 0; + CAmount nValue1 = 0; + CAmount nValue2 = 0; + + for(unsigned int i = 0; i < finalMutableTransaction.vout.size(); i++) { + BOOST_FOREACH(const CTxOut& txout, entry.vecTxDSOut) { + if(finalMutableTransaction.vout[i] == txout) { + nFoundOutputsCount++; + nValue1 += finalMutableTransaction.vout[i].nValue; + } + } + } + + BOOST_FOREACH(const CTxOut txout, entry.vecTxDSOut) + nValue2 += txout.nValue; + + int nTargetOuputsCount = entry.vecTxDSOut.size(); + if(nFoundOutputsCount < nTargetOuputsCount || 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::SignFinalTransaction -- My entries are not correct! Refusing to sign: nFoundOutputsCount: %d, nTargetOuputsCount: %d\n", nFoundOutputsCount, nTargetOuputsCount); + UnlockCoins(); + SetNull(); + + return false; + } + + const CKeyStore& keystore = *pwalletMain; + + LogPrint("privatesend", "CDarksendPool::SignFinalTransaction -- Signing my input %i\n", nMyInputIndex); + if(!SignSignature(keystore, prevPubKey, finalMutableTransaction, nMyInputIndex, NULL, int(SIGHASH_ALL|SIGHASH_ANYONECANPAY))) { // changes scriptSig + LogPrint("privatesend", "CDarksendPool::SignFinalTransaction -- Unable to sign my own transaction!\n"); + // not sure what to do here, it will timeout...? + } + + sigs.push_back(finalMutableTransaction.vin[nMyInputIndex]); + LogPrint("privatesend", "CDarksendPool::SignFinalTransaction -- nMyInputIndex: %d, sigs.size(): %d, scriptSig=%s\n", nMyInputIndex, (int)sigs.size(), ScriptToAsmStr(finalMutableTransaction.vin[nMyInputIndex].scriptSig)); + } + } + } + + if(sigs.empty()) { + LogPrintf("CDarksendPool::SignFinalTransaction -- can't sign anything!\n"); + UnlockCoins(); + SetNull(); + + return false; + } + + // push all of our signatures to the Znode + LogPrintf("CDarksendPool::SignFinalTransaction -- pushing sigs to the znode, finalMutableTransaction=%s", finalMutableTransaction.ToString()); + pnode->PushMessage(NetMsgType::DSSIGNFINALTX, sigs); + SetState(POOL_STATE_SIGNING); + nTimeLastSuccessfulStep = GetTimeMillis(); + + return true; +} + +void CDarksendPool::NewBlock() +{ + static int64_t nTimeNewBlockReceived = 0; + + //we we're processing lots of blocks, we'll just leave + if(GetTime() - nTimeNewBlockReceived < 10) return; + nTimeNewBlockReceived = GetTime(); + LogPrint("privatesend", "CDarksendPool::NewBlock\n"); + + CheckTimeout(); +} + +// mixing transaction was completed (failed or successful) +void CDarksendPool::CompletedTransaction(PoolMessage nMessageID) +{ + if(fZNode) return; + + if(nMessageID == MSG_SUCCESS) { + LogPrintf("CompletedTransaction -- success\n"); + nCachedLastSuccessBlock = pCurrentBlockIndex->nHeight; + } else { + LogPrintf("CompletedTransaction -- error\n"); + } + UnlockCoins(); + SetNull(); + strLastMessage = GetMessageByID(nMessageID); +} + +// +// Passively run mixing in the background to anonymize funds based on the given configuration. +// +bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) +{ + if(!fEnablePrivateSend || fZNode || !pCurrentBlockIndex) return false; + if(!pwalletMain || pwalletMain->IsLocked(true)) return false; + if(nState != POOL_STATE_IDLE) return false; + + if(!znodeSync.IsZnodeListSynced()) { + strAutoDenomResult = _("Can't mix while sync in progress."); + return false; + } + + switch(nWalletBackups) { + case 0: + LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating -- Automatic backups disabled, no mixing available.\n"); + strAutoDenomResult = _("Automatic backups disabled") + ", " + _("no mixing available."); + fEnablePrivateSend = false; // stop mixing + pwalletMain->nKeysLeftSinceAutoBackup = 0; // no backup, no "keys since last backup" + return false; + case -1: + // Automatic backup failed, nothing else we can do until user fixes the issue manually. + // There is no way to bring user attention in daemon mode so we just update status and + // keep spaming if debug is on. + LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating -- ERROR! Failed to create automatic backup.\n"); + strAutoDenomResult = _("ERROR! Failed to create automatic backup") + ", " + _("see debug.log for details."); + return false; + case -2: + // We were able to create automatic backup but keypool was not replenished because wallet is locked. + // There is no way to bring user attention in daemon mode so we just update status and + // keep spaming if debug is on. + LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating -- WARNING! Failed to create replenish keypool, please unlock your wallet to do so.\n"); + strAutoDenomResult = _("WARNING! Failed to replenish keypool, please unlock your wallet to do so.") + ", " + _("see debug.log for details."); + return false; + } + + if(pwalletMain->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_STOP) { + // We should never get here via mixing itself but probably smth else is still actively using keypool + LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating -- Very low number of keys left: %d, no mixing available.\n", pwalletMain->nKeysLeftSinceAutoBackup); + strAutoDenomResult = strprintf(_("Very low number of keys left: %d") + ", " + _("no mixing available."), pwalletMain->nKeysLeftSinceAutoBackup); + // It's getting really dangerous, stop mixing + fEnablePrivateSend = false; + return false; + } else if(pwalletMain->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_WARNING) { + // Low number of keys left but it's still more or less safe to continue + LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating -- Very low number of keys left: %d\n", pwalletMain->nKeysLeftSinceAutoBackup); + strAutoDenomResult = strprintf(_("Very low number of keys left: %d"), pwalletMain->nKeysLeftSinceAutoBackup); + + if(fCreateAutoBackups) { + LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating -- Trying to create new backup.\n"); + std::string warningString; + std::string errorString; + + if(!AutoBackupWallet(pwalletMain, "", warningString, errorString)) { + if(!warningString.empty()) { + // There were some issues saving backup but yet more or less safe to continue + LogPrintf("CDarksendPool::DoAutomaticDenominating -- WARNING! Something went wrong on automatic backup: %s\n", warningString); + } + if(!errorString.empty()) { + // Things are really broken + LogPrintf("CDarksendPool::DoAutomaticDenominating -- ERROR! Failed to create automatic backup: %s\n", errorString); + strAutoDenomResult = strprintf(_("ERROR! Failed to create automatic backup") + ": %s", errorString); + return false; + } + } + } else { + // Wait for someone else (e.g. GUI action) to create automatic backup for us + return false; + } + } + + LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating -- Keys left since latest backup: %d\n", pwalletMain->nKeysLeftSinceAutoBackup); + + if(GetEntriesCount() > 0) { + strAutoDenomResult = _("Mixing in progress..."); + return false; + } + + TRY_LOCK(cs_darksend, lockDS); + if(!lockDS) { + strAutoDenomResult = _("Lock is already in place."); + return false; + } + + if(!fDryRun && pwalletMain->IsLocked(true)) { + strAutoDenomResult = _("Wallet is locked."); + return false; + } + + if(!fPrivateSendMultiSession && pCurrentBlockIndex->nHeight - nCachedLastSuccessBlock < nMinBlockSpacing) { + LogPrintf("CDarksendPool::DoAutomaticDenominating -- Last successful PrivateSend action was too recent\n"); + strAutoDenomResult = _("Last successful PrivateSend action was too recent."); + return false; + } + + if(mnodeman.size() == 0) { + LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating -- No Znodes detected\n"); + strAutoDenomResult = _("No Znodes detected."); + return false; + } + + CAmount nValueMin = vecPrivateSendDenominations.back(); + + // if there are no confirmed DS collateral inputs yet + if(!pwalletMain->HasCollateralInputs()) { + // should have some additional amount for them + nValueMin += PRIVATESEND_COLLATERAL*4; + } + + // including denoms but applying some restrictions + CAmount nBalanceNeedsAnonymized = pwalletMain->GetNeedsToBeAnonymizedBalance(nValueMin); + + // anonymizable balance is way too small + if(nBalanceNeedsAnonymized < nValueMin) { + LogPrintf("CDarksendPool::DoAutomaticDenominating -- Not enough funds to anonymize\n"); + strAutoDenomResult = _("Not enough funds to anonymize."); + return false; + } + + // excluding denoms + CAmount nBalanceAnonimizableNonDenom = pwalletMain->GetAnonymizableBalance(true); + // denoms + CAmount nBalanceDenominatedConf = pwalletMain->GetDenominatedBalance(); + CAmount nBalanceDenominatedUnconf = pwalletMain->GetDenominatedBalance(true); + CAmount nBalanceDenominated = nBalanceDenominatedConf + nBalanceDenominatedUnconf; + + LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating -- nValueMin: %f, nBalanceNeedsAnonymized: %f, nBalanceAnonimizableNonDenom: %f, nBalanceDenominatedConf: %f, nBalanceDenominatedUnconf: %f, nBalanceDenominated: %f\n", + (float)nValueMin/COIN, + (float)nBalanceNeedsAnonymized/COIN, + (float)nBalanceAnonimizableNonDenom/COIN, + (float)nBalanceDenominatedConf/COIN, + (float)nBalanceDenominatedUnconf/COIN, + (float)nBalanceDenominated/COIN); + + if(fDryRun) return true; + + // Check if we have should create more denominated inputs i.e. + // there are funds to denominate and denominated balance does not exceed + // max amount to mix yet. + if(nBalanceAnonimizableNonDenom >= nValueMin + PRIVATESEND_COLLATERAL && nBalanceDenominated < nPrivateSendAmount*COIN) + return CreateDenominated(); + + //check if we have the collateral sized inputs + if(!pwalletMain->HasCollateralInputs()) + return !pwalletMain->HasCollateralInputs(false) && MakeCollateralAmounts(); + + if(nSessionID) { + strAutoDenomResult = _("Mixing in progress..."); + return false; + } + + // Initial phase, find a Znode + // Clean if there is anything left from previous session + UnlockCoins(); + SetNull(); + + // should be no unconfirmed denoms in non-multi-session mode + if(!fPrivateSendMultiSession && nBalanceDenominatedUnconf > 0) { + LogPrintf("CDarksendPool::DoAutomaticDenominating -- Found unconfirmed denominated outputs, will wait till they confirm to continue.\n"); + strAutoDenomResult = _("Found unconfirmed denominated outputs, will wait till they confirm to continue."); + return false; + } + + //check our collateral and create new if needed + std::string strReason; + if(txMyCollateral == CMutableTransaction()) { + if(!pwalletMain->CreateCollateralTransaction(txMyCollateral, strReason)) { + LogPrintf("CDarksendPool::DoAutomaticDenominating -- create collateral error:%s\n", strReason); + return false; + } + } else { + if(!IsCollateralValid(txMyCollateral)) { + LogPrintf("CDarksendPool::DoAutomaticDenominating -- invalid collateral, recreating...\n"); + if(!pwalletMain->CreateCollateralTransaction(txMyCollateral, strReason)) { + LogPrintf("CDarksendPool::DoAutomaticDenominating -- create collateral error: %s\n", strReason); + return false; + } + } + } + + int nMnCountEnabled = mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION); + + // If we've used 90% of the Znode list then drop the oldest first ~30% + int nThreshold_high = nMnCountEnabled * 0.9; + int nThreshold_low = nThreshold_high * 0.7; + LogPrint("privatesend", "Checking vecZnodesUsed: size: %d, threshold: %d\n", (int)vecZnodesUsed.size(), nThreshold_high); + + if((int)vecZnodesUsed.size() > nThreshold_high) { + vecZnodesUsed.erase(vecZnodesUsed.begin(), vecZnodesUsed.begin() + vecZnodesUsed.size() - nThreshold_low); + LogPrint("privatesend", " vecZnodesUsed: new size: %d, threshold: %d\n", (int)vecZnodesUsed.size(), nThreshold_high); + } + + bool fUseQueue = GetRandInt(100) > 33; + // don't use the queues all of the time for mixing unless we are a liquidity provider + if(nLiquidityProvider || fUseQueue) { + + // Look through the queues and see if anything matches + BOOST_FOREACH(CDarksendQueue& dsq, vecDarksendQueue) { + // only try each queue once + if(dsq.fTried) continue; + dsq.fTried = true; + + if(dsq.IsExpired()) continue; + + CZnode* pmn = mnodeman.Find(dsq.vin); + if(pmn == NULL) { + LogPrintf("CDarksendPool::DoAutomaticDenominating -- dsq znode is not in znode list, znode=%s\n", dsq.vin.prevout.ToStringShort()); + continue; + } + + if(pmn->nProtocolVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) continue; + + std::vector vecBits; + if(!GetDenominationsBits(dsq.nDenom, vecBits)) { + // incompatible denom + continue; + } + + // mixing rate limit i.e. nLastDsq check should already pass in DSQUEUE ProcessMessage + // in order for dsq to get into vecDarksendQueue, so we should be safe to mix already, + // no need for additional verification here + + LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating -- found valid queue: %s\n", dsq.ToString()); + + CAmount nValueInTmp = 0; + std::vector vecTxInTmp; + std::vector vCoinsTmp; + + // Try to match their denominations if possible, select at least 1 denominations + if(!pwalletMain->SelectCoinsByDenominations(dsq.nDenom, vecPrivateSendDenominations[vecBits.front()], nBalanceNeedsAnonymized, vecTxInTmp, vCoinsTmp, nValueInTmp, 0, nPrivateSendRounds)) { + LogPrintf("CDarksendPool::DoAutomaticDenominating -- Couldn't match denominations %d %d (%s)\n", vecBits.front(), dsq.nDenom, GetDenominationsToString(dsq.nDenom)); + continue; + } + + vecZnodesUsed.push_back(dsq.vin); + + CNode* pnodeFound = NULL; + { + LOCK(cs_vNodes); + pnodeFound = FindNode(pmn->addr); + if(pnodeFound) { + if(pnodeFound->fDisconnect) { + continue; + } else { + pnodeFound->AddRef(); + } + } + } + + LogPrintf("CDarksendPool::DoAutomaticDenominating -- attempt to connect to znode from queue, addr=%s\n", pmn->addr.ToString()); + // connect to Znode and submit the queue request + CNode* pnode = (pnodeFound && pnodeFound->fZnode) ? pnodeFound : ConnectNodeDash(CAddress(pmn->addr, NODE_NETWORK), NULL, true); + if(pnode) { + pSubmittedToZnode = pmn; + nSessionDenom = dsq.nDenom; + + pnode->PushMessage(NetMsgType::DSACCEPT, nSessionDenom, txMyCollateral); + LogPrintf("CDarksendPool::DoAutomaticDenominating -- connected (from queue), sending DSACCEPT: nSessionDenom: %d (%s), addr=%s\n", + nSessionDenom, GetDenominationsToString(nSessionDenom), pnode->addr.ToString()); + strAutoDenomResult = _("Mixing in progress..."); + SetState(POOL_STATE_QUEUE); + nTimeLastSuccessfulStep = GetTimeMillis(); + if(pnodeFound) { + pnodeFound->Release(); + } + return true; + } else { + LogPrintf("CDarksendPool::DoAutomaticDenominating -- can't connect, addr=%s\n", pmn->addr.ToString()); + strAutoDenomResult = _("Error connecting to Znode."); + continue; + } + } + } + + // do not initiate queue if we are a liquidity provider to avoid useless inter-mixing + if(nLiquidityProvider) return false; + + int nTries = 0; + + // ** find the coins we'll use + std::vector vecTxIn; + CAmount nValueInTmp = 0; + if(!pwalletMain->SelectCoinsDark(nValueMin, nBalanceNeedsAnonymized, vecTxIn, nValueInTmp, 0, nPrivateSendRounds)) { + // this should never happen + LogPrintf("CDarksendPool::DoAutomaticDenominating -- Can't mix: no compatible inputs found!\n"); + strAutoDenomResult = _("Can't mix: no compatible inputs found!"); + return false; + } + + // otherwise, try one randomly + while(nTries < 10) { + CZnode* pmn = mnodeman.FindRandomNotInVec(vecZnodesUsed, MIN_PRIVATESEND_PEER_PROTO_VERSION); + if(pmn == NULL) { + LogPrintf("CDarksendPool::DoAutomaticDenominating -- Can't find random znode!\n"); + strAutoDenomResult = _("Can't find random Znode."); + return false; + } + vecZnodesUsed.push_back(pmn->vin); + + if(pmn->nLastDsq != 0 && pmn->nLastDsq + nMnCountEnabled/5 > mnodeman.nDsqCount) { + LogPrintf("CDarksendPool::DoAutomaticDenominating -- Too early to mix on this znode!" + " znode=%s addr=%s nLastDsq=%d CountEnabled/5=%d nDsqCount=%d\n", + pmn->vin.prevout.ToStringShort(), pmn->addr.ToString(), pmn->nLastDsq, + nMnCountEnabled/5, mnodeman.nDsqCount); + nTries++; + continue; + } + + CNode* pnodeFound = NULL; + { + LOCK(cs_vNodes); + pnodeFound = FindNode(pmn->addr); + if(pnodeFound) { + if(pnodeFound->fDisconnect) { + nTries++; + continue; + } else { + pnodeFound->AddRef(); + } + } + } + + LogPrintf("CDarksendPool::DoAutomaticDenominating -- attempt %d connection to Znode %s\n", nTries, pmn->addr.ToString()); + CNode* pnode = (pnodeFound && pnodeFound->fZnode) ? pnodeFound : ConnectNodeDash(CAddress(pmn->addr, NODE_NETWORK), NULL, true); + if(pnode) { + LogPrintf("CDarksendPool::DoAutomaticDenominating -- connected, addr=%s\n", pmn->addr.ToString()); + pSubmittedToZnode = pmn; + + std::vector vecAmounts; + pwalletMain->ConvertList(vecTxIn, vecAmounts); + // try to get a single random denom out of vecAmounts + while(nSessionDenom == 0) { + nSessionDenom = GetDenominationsByAmounts(vecAmounts); + } + + pnode->PushMessage(NetMsgType::DSACCEPT, nSessionDenom, txMyCollateral); + LogPrintf("CDarksendPool::DoAutomaticDenominating -- connected, sending DSACCEPT, nSessionDenom: %d (%s)\n", + nSessionDenom, GetDenominationsToString(nSessionDenom)); + strAutoDenomResult = _("Mixing in progress..."); + SetState(POOL_STATE_QUEUE); + nTimeLastSuccessfulStep = GetTimeMillis(); + if(pnodeFound) { + pnodeFound->Release(); + } + return true; + } else { + LogPrintf("CDarksendPool::DoAutomaticDenominating -- can't connect, addr=%s\n", pmn->addr.ToString()); + nTries++; + continue; + } + } + + strAutoDenomResult = _("No compatible Znode found."); + return false; +} + +bool CDarksendPool::SubmitDenominate() +{ + std::string strError; + std::vector vecTxInRet; + std::vector vecTxOutRet; + + // Submit transaction to the pool if we get here + // Try to use only inputs with the same number of rounds starting from lowest number of rounds possible + for(int i = 0; i < nPrivateSendRounds; i++) { + if(PrepareDenominate(i, i+1, strError, vecTxInRet, vecTxOutRet)) { + LogPrintf("CDarksendPool::SubmitDenominate -- Running PrivateSend denominate for %d rounds, success\n", i); + return SendDenominate(vecTxInRet, vecTxOutRet); + } + LogPrintf("CDarksendPool::SubmitDenominate -- Running PrivateSend denominate for %d rounds, error: %s\n", i, strError); + } + + // We failed? That's strange but let's just make final attempt and try to mix everything + if(PrepareDenominate(0, nPrivateSendRounds, strError, vecTxInRet, vecTxOutRet)) { + LogPrintf("CDarksendPool::SubmitDenominate -- Running PrivateSend denominate for all rounds, success\n"); + return SendDenominate(vecTxInRet, vecTxOutRet); + } + + // Should never actually get here but just in case + LogPrintf("CDarksendPool::SubmitDenominate -- Running PrivateSend denominate for all rounds, error: %s\n", strError); + strAutoDenomResult = strError; + return false; +} + +bool CDarksendPool::PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, std::vector& vecTxInRet, std::vector& vecTxOutRet) +{ + if (pwalletMain->IsLocked(true)) { + strErrorRet = "Wallet locked, unable to create transaction!"; + return false; + } + + if (GetEntriesCount() > 0) { + strErrorRet = "Already have pending entries in the PrivateSend pool"; + return false; + } + + // make sure returning vectors are empty before filling them up + vecTxInRet.clear(); + vecTxOutRet.clear(); + + // ** find the coins we'll use + std::vector vecTxIn; + std::vector vCoins; + CAmount nValueIn = 0; + CReserveKey reservekey(pwalletMain); + + /* + Select the coins we'll use + + if nMinRounds >= 0 it means only denominated inputs are going in and coming out + */ + std::vector vecBits; + if (!GetDenominationsBits(nSessionDenom, vecBits)) { + strErrorRet = "Incorrect session denom"; + return false; + } + bool fSelected = pwalletMain->SelectCoinsByDenominations(nSessionDenom, vecPrivateSendDenominations[vecBits.front()], PRIVATESEND_POOL_MAX, vecTxIn, vCoins, nValueIn, nMinRounds, nMaxRounds); + if (nMinRounds >= 0 && !fSelected) { + strErrorRet = "Can't select current denominated inputs"; + return false; + } + + LogPrintf("CDarksendPool::PrepareDenominate -- max value: %f\n", (double)nValueIn/COIN); + + { + LOCK(pwalletMain->cs_wallet); + BOOST_FOREACH(CTxIn txin, vecTxIn) { + pwalletMain->LockCoin(txin.prevout); + } + } + + CAmount nValueLeft = nValueIn; + + // Try to add every needed denomination, repeat up to 5-9 times. + // NOTE: No need to randomize order of inputs because they were + // initially shuffled in CWallet::SelectCoinsByDenominations already. + int nStep = 0; + int nStepsMax = 5 + GetRandInt(5); + + while (nStep < nStepsMax) { + BOOST_FOREACH(int nBit, vecBits) { + CAmount nValueDenom = vecPrivateSendDenominations[nBit]; + if (nValueLeft - nValueDenom < 0) continue; + + // Note: this relies on a fact that both vectors MUST have same size + std::vector::iterator it = vecTxIn.begin(); + std::vector::iterator it2 = vCoins.begin(); + while (it2 != vCoins.end()) { + // we have matching inputs + if ((*it2).tx->vout[(*it2).i].nValue == nValueDenom) { + // add new input in resulting vector + vecTxInRet.push_back(*it); + // remove corresponting items from initial vectors + vecTxIn.erase(it); + vCoins.erase(it2); + + CScript scriptChange; + CPubKey vchPubKey; + // use a unique change address + assert(reservekey.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + reservekey.KeepKey(); + + // add new output + CTxOut txout(nValueDenom, scriptChange); + vecTxOutRet.push_back(txout); + + // subtract denomination amount + nValueLeft -= nValueDenom; + + // step is complete + break; + } + ++it; + ++it2; + } + } + if(nValueLeft == 0) break; + nStep++; + } + + { + // unlock unused coins + LOCK(pwalletMain->cs_wallet); + BOOST_FOREACH(CTxIn txin, vecTxIn) { + pwalletMain->UnlockCoin(txin.prevout); + } + } + + if (GetDenominations(vecTxOutRet) != nSessionDenom) { + // unlock used coins on failure + LOCK(pwalletMain->cs_wallet); + BOOST_FOREACH(CTxIn txin, vecTxInRet) { + pwalletMain->UnlockCoin(txin.prevout); + } + strErrorRet = "Can't make current denominated outputs"; + return false; + } + + // We also do not care about full amount as long as we have right denominations + return true; +} + +// Create collaterals by looping through inputs grouped by addresses +bool CDarksendPool::MakeCollateralAmounts() +{ + std::vector vecTally; + if(!pwalletMain->SelectCoinsGrouppedByAddresses(vecTally, false)) { + LogPrint("privatesend", "CDarksendPool::MakeCollateralAmounts -- SelectCoinsGrouppedByAddresses can't find any inputs!\n"); + return false; + } + + BOOST_FOREACH(CompactTallyItem& item, vecTally) { + if(!MakeCollateralAmounts(item)) continue; + return true; + } + + LogPrintf("CDarksendPool::MakeCollateralAmounts -- failed!\n"); + return false; +} + +// Split up large inputs or create fee sized inputs +bool CDarksendPool::MakeCollateralAmounts(const CompactTallyItem& tallyItem) +{ + CWalletTx wtx; + CAmount nFeeRet = 0; + int nChangePosRet = -1; + std::string strFail = ""; + std::vector vecSend; + + // make our collateral address + CReserveKey reservekeyCollateral(pwalletMain); + // make our change address + CReserveKey reservekeyChange(pwalletMain); + + CScript scriptCollateral; + CPubKey vchPubKey; + assert(reservekeyCollateral.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked + scriptCollateral = GetScriptForDestination(vchPubKey.GetID()); + + vecSend.push_back((CRecipient){scriptCollateral, PRIVATESEND_COLLATERAL*4, false}); + + // try to use non-denominated and not mn-like funds first, select them explicitly + CCoinControl coinControl; + coinControl.fAllowOtherInputs = false; + coinControl.fAllowWatchOnly = false; + // send change to the same address so that we were able create more denoms out of it later + coinControl.destChange = tallyItem.address.Get(); + BOOST_FOREACH(const CTxIn& txin, tallyItem.vecTxIn) + coinControl.Select(txin.prevout); + //TODO + //bool fSuccess = pwalletMain->CreateTransaction(vecSend, wtx, reservekeyChange, nFeeRet, nChangePosRet, strFail, &coinControl, true, ONLY_NONDENOMINATED_NOT1000IFMN); + bool fSuccess = false; + if(!fSuccess) { + // 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 + LogPrintf("CDarksendPool::MakeCollateralAmounts -- ONLY_NONDENOMINATED_NOT1000IFMN Error: %s\n", strFail); + CCoinControl *coinControlNull = NULL; + //TODO +// fSuccess = pwalletMain->CreateTransaction(vecSend, wtx, reservekeyChange, nFeeRet, nChangePosRet, strFail, coinControlNull, true, ONLY_NOT1000IFMN); + fSuccess = false; + if(!fSuccess) { + LogPrintf("CDarksendPool::MakeCollateralAmounts -- ONLY_NOT1000IFMN Error: %s\n", strFail); + reservekeyCollateral.ReturnKey(); + return false; + } + } + + reservekeyCollateral.KeepKey(); + + LogPrintf("CDarksendPool::MakeCollateralAmounts -- txid=%s\n", wtx.GetHash().GetHex()); + + // use the same nCachedLastSuccessBlock as for DS mixinx to prevent race + if(!pwalletMain->CommitTransaction(wtx, reservekeyChange)) { + LogPrintf("CDarksendPool::MakeCollateralAmounts -- CommitTransaction failed!\n"); + return false; + } + + nCachedLastSuccessBlock = pCurrentBlockIndex->nHeight; + + return true; +} + +// Create denominations by looping through inputs grouped by addresses +bool CDarksendPool::CreateDenominated() +{ + std::vector vecTally; + if(!pwalletMain->SelectCoinsGrouppedByAddresses(vecTally)) { + LogPrint("privatesend", "CDarksendPool::CreateDenominated -- SelectCoinsGrouppedByAddresses can't find any inputs!\n"); + return false; + } + + bool fCreateMixingCollaterals = !pwalletMain->HasCollateralInputs(); + + BOOST_FOREACH(CompactTallyItem& item, vecTally) { + if(!CreateDenominated(item, fCreateMixingCollaterals)) continue; + return true; + } + + LogPrintf("CDarksendPool::CreateDenominated -- failed!\n"); + return false; +} + +// Create denominations +bool CDarksendPool::CreateDenominated(const CompactTallyItem& tallyItem, bool fCreateMixingCollaterals) +{ + std::vector vecSend; + CAmount nValueLeft = tallyItem.nAmount; + nValueLeft -= PRIVATESEND_COLLATERAL; // leave some room for fees + + LogPrintf("CreateDenominated0 nValueLeft: %f\n", (float)nValueLeft/COIN); + // make our collateral address + CReserveKey reservekeyCollateral(pwalletMain); + + CScript scriptCollateral; + CPubKey vchPubKey; + assert(reservekeyCollateral.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked + scriptCollateral = GetScriptForDestination(vchPubKey.GetID()); + + // ****** Add collateral outputs ************ / + + if(fCreateMixingCollaterals) { + vecSend.push_back((CRecipient){scriptCollateral, PRIVATESEND_COLLATERAL*4, false}); + nValueLeft -= PRIVATESEND_COLLATERAL*4; + } + + // ****** Add denoms ************ / + + // make our denom addresses + CReserveKey reservekeyDenom(pwalletMain); + + // try few times - skipping smallest denoms first if there are too much already, if failed - use them + int nOutputsTotal = 0; + bool fSkip = true; + do { + + BOOST_REVERSE_FOREACH(CAmount nDenomValue, vecPrivateSendDenominations) { + + if(fSkip) { + // Note: denoms are skipped if there are already DENOMS_COUNT_MAX of them + // and there are still larger denoms which can be used for mixing + + // check skipped denoms + if(IsDenomSkipped(nDenomValue)) continue; + + // find new denoms to skip if any (ignore the largest one) + if(nDenomValue != vecPrivateSendDenominations[0] && pwalletMain->CountInputsWithAmount(nDenomValue) > DENOMS_COUNT_MAX) { + strAutoDenomResult = strprintf(_("Too many %f denominations, removing."), (float)nDenomValue/COIN); + LogPrintf("CDarksendPool::CreateDenominated -- %s\n", strAutoDenomResult); + vecDenominationsSkipped.push_back(nDenomValue); + continue; + } + } + + int nOutputs = 0; + + // add each output up to 10 times until it can't be added again + while(nValueLeft - nDenomValue >= 0 && nOutputs <= 10) { + CScript scriptDenom; + CPubKey vchPubKey; + //use a unique change address + assert(reservekeyDenom.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked + scriptDenom = GetScriptForDestination(vchPubKey.GetID()); + // TODO: do not keep reservekeyDenom here + reservekeyDenom.KeepKey(); + + vecSend.push_back((CRecipient){ scriptDenom, nDenomValue, false }); + + //increment outputs and subtract denomination amount + nOutputs++; + nValueLeft -= nDenomValue; + LogPrintf("CreateDenominated1: nOutputsTotal: %d, nOutputs: %d, nValueLeft: %f\n", nOutputsTotal, nOutputs, (float)nValueLeft/COIN); + } + + nOutputsTotal += nOutputs; + if(nValueLeft == 0) break; + } + LogPrintf("CreateDenominated2: nOutputsTotal: %d, nValueLeft: %f\n", nOutputsTotal, (float)nValueLeft/COIN); + // if there were no outputs added, start over without skipping + fSkip = !fSkip; + } while (nOutputsTotal == 0 && !fSkip); + LogPrintf("CreateDenominated3: nOutputsTotal: %d, nValueLeft: %f\n", nOutputsTotal, (float)nValueLeft/COIN); + + // if we have anything left over, it will be automatically send back as change - there is no need to send it manually + + CCoinControl coinControl; + coinControl.fAllowOtherInputs = false; + coinControl.fAllowWatchOnly = false; + // send change to the same address so that we were able create more denoms out of it later + coinControl.destChange = tallyItem.address.Get(); + BOOST_FOREACH(const CTxIn& txin, tallyItem.vecTxIn) + coinControl.Select(txin.prevout); + + CWalletTx wtx; + CAmount nFeeRet = 0; + int nChangePosRet = -1; + std::string strFail = ""; + // make our change address + CReserveKey reservekeyChange(pwalletMain); + //TODO +// bool fSuccess = pwalletMain->CreateTransaction(vecSend, wtx, reservekeyChange, nFeeRet, nChangePosRet, strFail, &coinControl, true, ONLY_NONDENOMINATED_NOT1000IFMN); + bool fSuccess = false; + if(!fSuccess) { + LogPrintf("CDarksendPool::CreateDenominated -- Error: %s\n", strFail); + // TODO: return reservekeyDenom here + reservekeyCollateral.ReturnKey(); + return false; + } + + // TODO: keep reservekeyDenom here + reservekeyCollateral.KeepKey(); + + if(!pwalletMain->CommitTransaction(wtx, reservekeyChange)) { + LogPrintf("CDarksendPool::CreateDenominated -- CommitTransaction failed!\n"); + return false; + } + + // use the same nCachedLastSuccessBlock as for DS mixing to prevent race + nCachedLastSuccessBlock = pCurrentBlockIndex->nHeight; + LogPrintf("CDarksendPool::CreateDenominated -- txid=%s\n", wtx.GetHash().GetHex()); + + return true; +} + +bool CDarksendPool::IsOutputsCompatibleWithSessionDenom(const std::vector& vecTxDSOut) +{ + if(GetDenominations(vecTxDSOut) == 0) return false; + + BOOST_FOREACH(const CDarkSendEntry entry, vecEntries) { + LogPrintf("CDarksendPool::IsOutputsCompatibleWithSessionDenom -- vecTxDSOut denom %d, entry.vecTxDSOut denom %d\n", GetDenominations(vecTxDSOut), GetDenominations(entry.vecTxDSOut)); + if(GetDenominations(vecTxDSOut) != GetDenominations(entry.vecTxDSOut)) return false; + } + + return true; +} + +bool CDarksendPool::IsAcceptableDenomAndCollateral(int nDenom, CTransaction txCollateral, PoolMessage& nMessageIDRet) +{ + if(!fZNode) return false; + + // is denom even smth legit? + std::vector vecBits; + if(!GetDenominationsBits(nDenom, vecBits)) { + LogPrint("privatesend", "CDarksendPool::IsAcceptableDenomAndCollateral -- denom not valid!\n"); + nMessageIDRet = ERR_DENOM; + return false; + } + + // check collateral + if(!fUnitTest && !IsCollateralValid(txCollateral)) { + LogPrint("privatesend", "CDarksendPool::IsAcceptableDenomAndCollateral -- collateral not valid!\n"); + nMessageIDRet = ERR_INVALID_COLLATERAL; + return false; + } + + return true; +} + +bool CDarksendPool::CreateNewSession(int nDenom, CTransaction txCollateral, PoolMessage& nMessageIDRet) +{ + if(!fZNode || nSessionID != 0) return false; + + // new session can only be started in idle mode + if(nState != POOL_STATE_IDLE) { + nMessageIDRet = ERR_MODE; + LogPrintf("CDarksendPool::CreateNewSession -- incompatible mode: nState=%d\n", nState); + return false; + } + + if(!IsAcceptableDenomAndCollateral(nDenom, txCollateral, nMessageIDRet)) { + return false; + } + + // start new session + nMessageIDRet = MSG_NOERR; + nSessionID = GetRandInt(999999)+1; + nSessionDenom = nDenom; + + SetState(POOL_STATE_QUEUE); + nTimeLastSuccessfulStep = GetTimeMillis(); + + if(!fUnitTest) { + //broadcast that I'm accepting entries, only if it's the first entry through + CDarksendQueue dsq(nDenom, activeZnode.vin, GetTime(), false); + LogPrint("privatesend", "CDarksendPool::CreateNewSession -- signing and relaying new queue: %s\n", dsq.ToString()); + dsq.Sign(); + dsq.Relay(); + vecDarksendQueue.push_back(dsq); + } + + vecSessionCollaterals.push_back(txCollateral); + LogPrintf("CDarksendPool::CreateNewSession -- new session created, nSessionID: %d nSessionDenom: %d (%s) vecSessionCollaterals.size(): %d\n", + nSessionID, nSessionDenom, GetDenominationsToString(nSessionDenom), vecSessionCollaterals.size()); + + return true; +} + +bool CDarksendPool::AddUserToExistingSession(int nDenom, CTransaction txCollateral, PoolMessage& nMessageIDRet) +{ + if(!fZNode || nSessionID == 0 || IsSessionReady()) return false; + + if(!IsAcceptableDenomAndCollateral(nDenom, txCollateral, nMessageIDRet)) { + return false; + } + + // we only add new users to an existing session when we are in queue mode + if(nState != POOL_STATE_QUEUE) { + nMessageIDRet = ERR_MODE; + LogPrintf("CDarksendPool::AddUserToExistingSession -- incompatible mode: nState=%d\n", nState); + return false; + } + + if(nDenom != nSessionDenom) { + LogPrintf("CDarksendPool::AddUserToExistingSession -- incompatible denom %d (%s) != nSessionDenom %d (%s)\n", + nDenom, GetDenominationsToString(nDenom), nSessionDenom, GetDenominationsToString(nSessionDenom)); + nMessageIDRet = ERR_DENOM; + return false; + } + + // count new user as accepted to an existing session + + nMessageIDRet = MSG_NOERR; + nTimeLastSuccessfulStep = GetTimeMillis(); + vecSessionCollaterals.push_back(txCollateral); + + LogPrintf("CDarksendPool::AddUserToExistingSession -- new user accepted, nSessionID: %d nSessionDenom: %d (%s) vecSessionCollaterals.size(): %d\n", + nSessionID, nSessionDenom, GetDenominationsToString(nSessionDenom), vecSessionCollaterals.size()); + + return true; +} + +/* Create a nice string to show the denominations + Function returns as follows (for 4 denominations): + ( bit on if present ) + bit 0 - 100 + bit 1 - 10 + bit 2 - 1 + bit 3 - .1 + bit 4 and so on - out-of-bounds + none of above - non-denom +*/ +std::string CDarksendPool::GetDenominationsToString(int nDenom) +{ + std::string strDenom = ""; + int nMaxDenoms = vecPrivateSendDenominations.size(); + + if(nDenom >= (1 << nMaxDenoms)) { + return "out-of-bounds"; + } + + for (int i = 0; i < nMaxDenoms; ++i) { + if(nDenom & (1 << i)) { + strDenom += (strDenom.empty() ? "" : "+") + FormatMoney(vecPrivateSendDenominations[i]); + } + } + + if(strDenom.empty()) { + return "non-denom"; + } + + return strDenom; +} + +int CDarksendPool::GetDenominations(const std::vector& vecTxDSOut) +{ + std::vector vecTxOut; + + BOOST_FOREACH(CTxDSOut out, vecTxDSOut) + vecTxOut.push_back(out); + + return GetDenominations(vecTxOut); +} + +/* Return a bitshifted integer representing the denominations in this list + Function returns as follows (for 4 denominations): + ( bit on if present ) + 100 - bit 0 + 10 - bit 1 + 1 - bit 2 + .1 - bit 3 + non-denom - 0, all bits off +*/ +int CDarksendPool::GetDenominations(const std::vector& vecTxOut, bool fSingleRandomDenom) +{ + std::vector > vecDenomUsed; + + // make a list of denominations, with zero uses + BOOST_FOREACH(CAmount nDenomValue, vecPrivateSendDenominations) + vecDenomUsed.push_back(std::make_pair(nDenomValue, 0)); + + // look for denominations and update uses to 1 + BOOST_FOREACH(CTxOut txout, vecTxOut) { + bool found = false; + BOOST_FOREACH (PAIRTYPE(CAmount, int)& s, vecDenomUsed) { + if(txout.nValue == s.first) { + s.second = 1; + found = true; + } + } + if(!found) return 0; + } + + int nDenom = 0; + int c = 0; + // if the denomination is used, shift the bit on + BOOST_FOREACH (PAIRTYPE(CAmount, int)& s, vecDenomUsed) { + int bit = (fSingleRandomDenom ? GetRandInt(2) : 1) & s.second; + nDenom |= bit << c++; + if(fSingleRandomDenom && bit) break; // use just one random denomination + } + + return nDenom; +} + +bool CDarksendPool::GetDenominationsBits(int nDenom, std::vector &vecBitsRet) +{ + // ( bit on if present, 4 denominations example ) + // bit 0 - 100DASH+1 + // bit 1 - 10DASH+1 + // bit 2 - 1DASH+1 + // bit 3 - .1DASH+1 + + int nMaxDenoms = vecPrivateSendDenominations.size(); + + if(nDenom >= (1 << nMaxDenoms)) return false; + + vecBitsRet.clear(); + + for (int i = 0; i < nMaxDenoms; ++i) { + if(nDenom & (1 << i)) { + vecBitsRet.push_back(i); + } + } + + return !vecBitsRet.empty(); +} + +int CDarksendPool::GetDenominationsByAmounts(const std::vector& vecAmount) +{ + CScript scriptTmp = CScript(); + std::vector vecTxOut; + + BOOST_REVERSE_FOREACH(CAmount nAmount, vecAmount) { + CTxOut txout(nAmount, scriptTmp); + vecTxOut.push_back(txout); + } + + return GetDenominations(vecTxOut, true); +} + +std::string CDarksendPool::GetMessageByID(PoolMessage nMessageID) +{ + switch (nMessageID) { + case ERR_ALREADY_HAVE: return _("Already have that input."); + case ERR_DENOM: return _("No matching denominations found for mixing."); + case ERR_ENTRIES_FULL: return _("Entries are full."); + case ERR_EXISTING_TX: return _("Not compatible with existing transactions."); + case ERR_FEES: return _("Transaction fees are too high."); + case ERR_INVALID_COLLATERAL: return _("Collateral not valid."); + case ERR_INVALID_INPUT: return _("Input is not valid."); + case ERR_INVALID_SCRIPT: return _("Invalid script detected."); + case ERR_INVALID_TX: return _("Transaction not valid."); + case ERR_MAXIMUM: return _("Value more than PrivateSend pool maximum allows."); + case ERR_MN_LIST: return _("Not in the Znode list."); + case ERR_MODE: return _("Incompatible mode."); + case ERR_NON_STANDARD_PUBKEY: return _("Non-standard public key detected."); + case ERR_NOT_A_MN: return _("This is not a Znode."); + case ERR_QUEUE_FULL: return _("Znode queue is full."); + case ERR_RECENT: return _("Last PrivateSend was too recent."); + case ERR_SESSION: return _("Session not complete!"); + case ERR_MISSING_TX: return _("Missing input transaction information."); + case ERR_VERSION: return _("Incompatible version."); + case MSG_NOERR: return _("No errors detected."); + case MSG_SUCCESS: return _("Transaction created successfully."); + case MSG_ENTRIES_ADDED: return _("Your entries added successfully."); + default: return _("Unknown response."); + } +} + +bool CDarkSendSigner::IsVinAssociatedWithPubkey(const CTxIn& txin, const CPubKey& pubkey) +{ + CScript payee; + payee = GetScriptForDestination(pubkey.GetID()); + + CTransaction tx; + uint256 hash; + if(GetTransaction(txin.prevout.hash, tx, Params().GetConsensus(), hash, true)) { + BOOST_FOREACH(CTxOut out, tx.vout) + if(out.nValue == 1000*COIN && out.scriptPubKey == payee) return true; + } + + return false; +} + +bool CDarkSendSigner::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 CDarkSendSigner::SignMessage(std::string strMessage, std::vector& vchSigRet, CKey key) +{ + CHashWriter ss(SER_GETHASH, 0); + ss << strMessageMagic; + ss << strMessage; + + return key.SignCompact(ss.GetHash(), vchSigRet); +} + +bool CDarkSendSigner::VerifyMessage(CPubKey pubkey, const std::vector& vchSig, std::string strMessage, std::string& strErrorRet) +{ + CHashWriter ss(SER_GETHASH, 0); + ss << strMessageMagic; + ss << strMessage; + + CPubKey pubkeyFromSig; + if(!pubkeyFromSig.RecoverCompact(ss.GetHash(), vchSig)) { + strErrorRet = "Error recovering public key."; + return false; + } + + if(pubkeyFromSig.GetID() != pubkey.GetID()) { + strErrorRet = strprintf("Keys don't match: pubkey=%s, pubkeyFromSig=%s, strMessage=%s, vchSig=%s", + pubkey.GetID().ToString(), pubkeyFromSig.GetID().ToString(), strMessage, + EncodeBase64(&vchSig[0], vchSig.size())); + return false; + } + + return true; +} + +bool CDarkSendEntry::AddScriptSig(const CTxIn& txin) +{ + BOOST_FOREACH(CTxDSIn& txdsin, vecTxDSIn) { + if(txdsin.prevout == txin.prevout && txdsin.nSequence == txin.nSequence) { + if(txdsin.fHasSig) return false; + + txdsin.scriptSig = txin.scriptSig; + txdsin.prevPubKey = txin.prevPubKey; + txdsin.fHasSig = true; + + return true; + } + } + + return false; +} + +bool CDarksendQueue::Sign() +{ + if(!fZNode) return false; + + std::string strMessage = vin.ToString() + boost::lexical_cast(nDenom) + boost::lexical_cast(nTime) + boost::lexical_cast(fReady); + + if(!darkSendSigner.SignMessage(strMessage, vchSig, activeZnode.keyZnode)) { + LogPrintf("CDarksendQueue::Sign -- SignMessage() failed, %s\n", ToString()); + return false; + } + + return CheckSignature(activeZnode.pubKeyZnode); +} + +bool CDarksendQueue::CheckSignature(const CPubKey& pubKeyZnode) +{ + std::string strMessage = vin.ToString() + boost::lexical_cast(nDenom) + boost::lexical_cast(nTime) + boost::lexical_cast(fReady); + std::string strError = ""; + + if(!darkSendSigner.VerifyMessage(pubKeyZnode, vchSig, strMessage, strError)) { + LogPrintf("CDarksendQueue::CheckSignature -- Got bad Znode queue signature: %s; error: %s\n", ToString(), strError); + return false; + } + + return true; +} + +bool CDarksendQueue::Relay() +{ + std::vector vNodesCopy = CopyNodeVector(); + BOOST_FOREACH(CNode* pnode, vNodesCopy) + if(pnode->nVersion >= MIN_PRIVATESEND_PEER_PROTO_VERSION) + pnode->PushMessage(NetMsgType::DSQUEUE, (*this)); + + ReleaseNodeVector(vNodesCopy); + return true; +} + +bool CDarksendBroadcastTx::Sign() +{ + if(!fZNode) return false; + + std::string strMessage = tx.GetHash().ToString() + boost::lexical_cast(sigTime); + + if(!darkSendSigner.SignMessage(strMessage, vchSig, activeZnode.keyZnode)) { + LogPrintf("CDarksendBroadcastTx::Sign -- SignMessage() failed\n"); + return false; + } + + return CheckSignature(activeZnode.pubKeyZnode); +} + +bool CDarksendBroadcastTx::CheckSignature(const CPubKey& pubKeyZnode) +{ + std::string strMessage = tx.GetHash().ToString() + boost::lexical_cast(sigTime); + std::string strError = ""; + + if(!darkSendSigner.VerifyMessage(pubKeyZnode, vchSig, strMessage, strError)) { + LogPrintf("CDarksendBroadcastTx::CheckSignature -- Got bad dstx signature, error: %s\n", strError); + return false; + } + + return true; +} + +void CDarksendPool::RelayFinalTransaction(const CTransaction& txFinal) +{ + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + if(pnode->nVersion >= MIN_PRIVATESEND_PEER_PROTO_VERSION) + pnode->PushMessage(NetMsgType::DSFINALTX, nSessionID, txFinal); +} + +void CDarksendPool::RelayIn(const CDarkSendEntry& entry) +{ + if(!pSubmittedToZnode) return; + + CNode* pnode = FindNode(pSubmittedToZnode->addr); + if(pnode != NULL) { + LogPrintf("CDarksendPool::RelayIn -- found master, relaying message to %s\n", pnode->addr.ToString()); + pnode->PushMessage(NetMsgType::DSVIN, entry); + } +} + +void CDarksendPool::PushStatus(CNode* pnode, PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID) +{ + if(!pnode) return; + pnode->PushMessage(NetMsgType::DSSTATUSUPDATE, nSessionID, (int)nState, (int)vecEntries.size(), (int)nStatusUpdate, (int)nMessageID); +} + +void CDarksendPool::RelayStatus(PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID) +{ + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + if(pnode->nVersion >= MIN_PRIVATESEND_PEER_PROTO_VERSION) + PushStatus(pnode, nStatusUpdate, nMessageID); +} + +void CDarksendPool::RelayCompletedTransaction(PoolMessage nMessageID) +{ + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + if(pnode->nVersion >= MIN_PRIVATESEND_PEER_PROTO_VERSION) + pnode->PushMessage(NetMsgType::DSCOMPLETE, nSessionID, (int)nMessageID); +} + +void CDarksendPool::SetState(PoolState nStateNew) +{ + if(fZNode && (nStateNew == POOL_STATE_ERROR || nStateNew == POOL_STATE_SUCCESS)) { + LogPrint("privatesend", "CDarksendPool::SetState -- Can't set state to ERROR or SUCCESS as a Znode. \n"); + return; + } + + LogPrintf("CDarksendPool::SetState -- nState: %d, nStateNew: %d\n", nState, nStateNew); + nState = nStateNew; +} + +void CDarksendPool::UpdatedBlockTip(const CBlockIndex *pindex) +{ + pCurrentBlockIndex = pindex; + LogPrint("privatesend", "CDarksendPool::UpdatedBlockTip -- pCurrentBlockIndex->nHeight: %d\n", pCurrentBlockIndex->nHeight); + + if(!fLiteMode && znodeSync.IsZnodeListSynced()) { + NewBlock(); + } +} + +//TODO: Rename/move to core +void ThreadCheckDarkSendPool() +{ + if(fLiteMode) return; // disable all Dash specific functionality + + static bool fOneThread; + if(fOneThread) return; + fOneThread = true; + + // Make this thread recognisable as the PrivateSend thread + RenameThread("dash-privatesend"); + + unsigned int nTick = 0; + unsigned int nDoAutoNextRun = nTick + PRIVATESEND_AUTO_TIMEOUT_MIN; + + while (true) + { + MilliSleep(1000); + + // try to sync from all available nodes, one step at a time + znodeSync.ProcessTick(); + + if(znodeSync.IsBlockchainSynced() && !ShutdownRequested()) { + + nTick++; + + // make sure to check all znodes first + mnodeman.Check(); + + // check if we should activate or ping every few minutes, + // slightly postpone first run to give net thread a chance to connect to some peers + if(nTick % ZNODE_MIN_MNP_SECONDS == 15) + activeZnode.ManageState(); + + if(nTick % 60 == 0) { + mnodeman.ProcessZnodeConnections(); + mnodeman.CheckAndRemove(); + mnpayments.CheckAndRemove(); + instantsend.CheckAndRemove(); + } + if(fZNode && (nTick % (60 * 5) == 0)) { + mnodeman.DoFullVerificationStep(); + } + +// if(nTick % (60 * 5) == 0) { +// governance.DoMaintenance(); +// } + + darkSendPool.CheckTimeout(); + darkSendPool.CheckForCompleteQueue(); + + if(nDoAutoNextRun == nTick) { + darkSendPool.DoAutomaticDenominating(); + nDoAutoNextRun = nTick + PRIVATESEND_AUTO_TIMEOUT_MIN + GetRandInt(PRIVATESEND_AUTO_TIMEOUT_MAX - PRIVATESEND_AUTO_TIMEOUT_MIN); + } + } + } +} diff --git a/src/darksend.h b/src/darksend.h new file mode 100644 index 0000000000..65cd52360b --- /dev/null +++ b/src/darksend.h @@ -0,0 +1,483 @@ +// Copyright (c) 2014-2017 The Dash Core 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 "znode.h" +#include "wallet/wallet.h" + +//class CDarksendPool; +class CDarkSendSigner; +class CDarksendBroadcastTx; + +// timeouts +static const int PRIVATESEND_AUTO_TIMEOUT_MIN = 5; +static const int PRIVATESEND_AUTO_TIMEOUT_MAX = 15; +static const int PRIVATESEND_QUEUE_TIMEOUT = 30; +static const int PRIVATESEND_SIGNING_TIMEOUT = 15; + +//! minimum peer version accepted by mixing pool +static const int MIN_PRIVATESEND_PEER_PROTO_VERSION = 70206; + +static const CAmount PRIVATESEND_COLLATERAL = 0.001 * COIN; +static const CAmount PRIVATESEND_POOL_MAX = 999.999 * COIN; +static const int DENOMS_COUNT_MAX = 100; + +static const int DEFAULT_PRIVATESEND_ROUNDS = 2; +static const int DEFAULT_PRIVATESEND_AMOUNT = 1000; +static const int DEFAULT_PRIVATESEND_LIQUIDITY = 0; +static const bool DEFAULT_PRIVATESEND_MULTISESSION = false; + +// Warn user if mixing in gui or try to create backup if mixing in daemon mode +// when we have only this many keys left +static const int PRIVATESEND_KEYS_THRESHOLD_WARNING = 100; +// Stop mixing completely, it's too dangerous to continue when we have only this many keys left +static const int PRIVATESEND_KEYS_THRESHOLD_STOP = 50; + +extern int nPrivateSendRounds; +extern int nPrivateSendAmount; +extern int nLiquidityProvider; +extern bool fEnablePrivateSend; +//extern bool fPrivateSendMultiSession; + +extern std::map mapDarksendBroadcastTxes; +extern std::vector vecPrivateSendDenominations; + +/** Holds an mixing input + */ +class CTxDSIn : public CTxIn +{ +public: + bool fHasSig; // flag to indicate if signed + int nSentTimes; //times we've sent this anonymously + + CTxDSIn(const CTxIn& txin) : + CTxIn(txin), + fHasSig(false), + nSentTimes(0) + {} + + CTxDSIn() : + CTxIn(), + fHasSig(false), + nSentTimes(0) + {} +}; + +/** Holds an mixing output + */ +class CTxDSOut : public CTxOut +{ +public: + int nSentTimes; //times we've sent this anonymously + + CTxDSOut(const CTxOut& out) : + CTxOut(out), + nSentTimes(0) + {} + + CTxDSOut() : + CTxOut(), + nSentTimes(0) + {} +}; + +// A clients transaction in the mixing pool +class CDarkSendEntry +{ +public: + std::vector vecTxDSIn; + std::vector vecTxDSOut; + CTransaction txCollateral; + + CDarkSendEntry() : + vecTxDSIn(std::vector()), + vecTxDSOut(std::vector()), + txCollateral(CTransaction()) + {} + + CDarkSendEntry(const std::vector& vecTxIn, const std::vector& vecTxOut, const CTransaction& txCollateral) : + txCollateral(txCollateral) + { + BOOST_FOREACH(CTxIn txin, vecTxIn) + vecTxDSIn.push_back(txin); + BOOST_FOREACH(CTxOut txout, vecTxOut) + vecTxDSOut.push_back(txout); + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(vecTxDSIn); + READWRITE(txCollateral); + READWRITE(vecTxDSOut); + } + + bool AddScriptSig(const CTxIn& txin); +}; + + +/** + * A currently inprogress mixing merge and denomination information + */ +class CDarksendQueue +{ +public: + int nDenom; + CTxIn vin; + int64_t nTime; + bool fReady; //ready for submit + std::vector vchSig; + // memory only + bool fTried; + + CDarksendQueue() : + nDenom(0), + vin(CTxIn()), + nTime(0), + fReady(false), + vchSig(std::vector()), + fTried(false) + {} + + CDarksendQueue(int nDenom, CTxIn vin, int64_t nTime, bool fReady) : + nDenom(nDenom), + vin(vin), + nTime(nTime), + fReady(fReady), + vchSig(std::vector()), + fTried(false) + {} + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(nDenom); + READWRITE(vin); + READWRITE(nTime); + READWRITE(fReady); + READWRITE(vchSig); + } + + /** Sign this mixing transaction + * \return true if all conditions are met: + * 1) we have an active Znode, + * 2) we have a valid Znode private key, + * 3) we signed the message successfully, and + * 4) we verified the message successfully + */ + bool Sign(); + /// Check if we have a valid Znode address + bool CheckSignature(const CPubKey& pubKeyZnode); + + bool Relay(); + + /// Is this queue expired? + bool IsExpired() { return GetTime() - nTime > PRIVATESEND_QUEUE_TIMEOUT; } + + std::string ToString() + { + return strprintf("nDenom=%d, nTime=%lld, fReady=%s, fTried=%s, znode=%s", + nDenom, nTime, fReady ? "true" : "false", fTried ? "true" : "false", vin.prevout.ToStringShort()); + } + + friend bool operator==(const CDarksendQueue& a, const CDarksendQueue& b) + { + return a.nDenom == b.nDenom && a.vin.prevout == b.vin.prevout && a.nTime == b.nTime && a.fReady == b.fReady; + } +}; + +/** Helper class to store mixing transaction (tx) information. + */ +class CDarksendBroadcastTx +{ +public: + CTransaction tx; + CTxIn vin; + std::vector vchSig; + int64_t sigTime; + + CDarksendBroadcastTx() : + tx(CTransaction()), + vin(CTxIn()), + vchSig(std::vector()), + sigTime(0) + {} + + CDarksendBroadcastTx(CTransaction tx, CTxIn vin, int64_t sigTime) : + tx(tx), + vin(vin), + vchSig(std::vector()), + sigTime(sigTime) + {} + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(tx); + READWRITE(vin); + READWRITE(vchSig); + READWRITE(sigTime); + } + + bool Sign(); + bool CheckSignature(const CPubKey& pubKeyZnode); +}; + +/** Helper object for signing and checking signatures + */ +class CDarkSendSigner +{ +public: + /// Is the input associated with this public key? (and there is 1000 DASH - checking if valid znode) + bool IsVinAssociatedWithPubkey(const CTxIn& vin, const CPubKey& pubkey); + /// Set the private/public key values, returns true if successful + bool GetKeysFromSecret(std::string strSecret, CKey& keyRet, CPubKey& pubkeyRet); + /// Sign the message, returns true if successful + bool SignMessage(std::string strMessage, std::vector& vchSigRet, CKey key); + /// Verify the message, returns true if succcessful + bool VerifyMessage(CPubKey pubkey, const std::vector& vchSig, std::string strMessage, std::string& strErrorRet); +}; + + +/** Used to keep track of current status of mixing pool + */ +class CDarksendPool +{ +private: + // pool responses + enum PoolMessage { + ERR_ALREADY_HAVE, + ERR_DENOM, + ERR_ENTRIES_FULL, + ERR_EXISTING_TX, + ERR_FEES, + ERR_INVALID_COLLATERAL, + ERR_INVALID_INPUT, + ERR_INVALID_SCRIPT, + ERR_INVALID_TX, + ERR_MAXIMUM, + ERR_MN_LIST, + ERR_MODE, + ERR_NON_STANDARD_PUBKEY, + ERR_NOT_A_MN, + ERR_QUEUE_FULL, + ERR_RECENT, + ERR_SESSION, + ERR_MISSING_TX, + ERR_VERSION, + MSG_NOERR, + MSG_SUCCESS, + MSG_ENTRIES_ADDED, + MSG_POOL_MIN = ERR_ALREADY_HAVE, + MSG_POOL_MAX = MSG_ENTRIES_ADDED + }; + + // pool states + enum PoolState { + POOL_STATE_IDLE, + POOL_STATE_QUEUE, + POOL_STATE_ACCEPTING_ENTRIES, + POOL_STATE_SIGNING, + POOL_STATE_ERROR, + POOL_STATE_SUCCESS, + POOL_STATE_MIN = POOL_STATE_IDLE, + POOL_STATE_MAX = POOL_STATE_SUCCESS + }; + + // status update message constants + enum PoolStatusUpdate { + STATUS_REJECTED, + STATUS_ACCEPTED + }; + + mutable CCriticalSection cs_darksend; + + // The current mixing sessions in progress on the network + std::vector vecDarksendQueue; + // Keep track of the used Znodes + std::vector vecZnodesUsed; + + std::vector vecDenominationsSkipped; + std::vector vecOutPointLocked; + // Mixing uses collateral transactions to trust parties entering the pool + // to behave honestly. If they don't it takes their money. + std::vector vecSessionCollaterals; + std::vector vecEntries; // Znode/clients entries + + PoolState nState; // should be one of the POOL_STATE_XXX values + int64_t nTimeLastSuccessfulStep; // the time when last successful mixing step was performed, in UTC milliseconds + + int nCachedLastSuccessBlock; + int nMinBlockSpacing; //required blocks between mixes + const CBlockIndex *pCurrentBlockIndex; // Keep track of current block index + + int nSessionID; // 0 if no mixing session is active + + int nEntriesCount; + bool fLastEntryAccepted; + + std::string strLastMessage; + std::string strAutoDenomResult; + + bool fUnitTest; + + CMutableTransaction txMyCollateral; // client side collateral + CMutableTransaction finalMutableTransaction; // the finalized transaction ready for signing + + /// Add a clients entry to the pool + bool AddEntry(const CDarkSendEntry& entryNew, PoolMessage& nMessageIDRet); + /// Add signature to a txin + bool AddScriptSig(const CTxIn& txin); + + /// Charge fees to bad actors (Charge clients a fee if they're abusive) + void ChargeFees(); + /// Rarely charge fees to pay miners + void ChargeRandomFees(); + + /// Check for process + void CheckPool(); + + void CreateFinalTransaction(); + void CommitFinalTransaction(); + + void CompletedTransaction(PoolMessage nMessageID); + + /// Get the denominations for a specific amount of dash. + int GetDenominationsByAmounts(const std::vector& vecAmount); + + std::string GetMessageByID(PoolMessage nMessageID); + + /// Get the maximum number of transactions for the pool + int GetMaxPoolTransactions() { return Params().PoolMaxTransactions(); } + + /// Is this nDenom and txCollateral acceptable? + bool IsAcceptableDenomAndCollateral(int nDenom, CTransaction txCollateral, PoolMessage &nMessageIDRet); + bool CreateNewSession(int nDenom, CTransaction txCollateral, PoolMessage &nMessageIDRet); + bool AddUserToExistingSession(int nDenom, CTransaction txCollateral, PoolMessage &nMessageIDRet); + /// Do we have enough users to take entries? + bool IsSessionReady() { return (int)vecSessionCollaterals.size() >= GetMaxPoolTransactions(); } + + /// If the collateral is valid given by a client + bool IsCollateralValid(const CTransaction& txCollateral); + /// Check that all inputs are signed. (Are all inputs signed?) + bool IsSignaturesComplete(); + /// Check to make sure a given input matches an input in the pool and its scriptSig is valid + bool IsInputScriptSigValid(const CTxIn& txin); + /// Are these outputs compatible with other client in the pool? + bool IsOutputsCompatibleWithSessionDenom(const std::vector& vecTxDSOut); + + bool IsDenomSkipped(CAmount nDenomValue) { + return std::find(vecDenominationsSkipped.begin(), vecDenominationsSkipped.end(), nDenomValue) != vecDenominationsSkipped.end(); + } + + /// Create denominations + bool CreateDenominated(); + bool CreateDenominated(const CompactTallyItem& tallyItem, bool fCreateMixingCollaterals); + + /// Split up large inputs or make fee sized inputs + bool MakeCollateralAmounts(); + bool MakeCollateralAmounts(const CompactTallyItem& tallyItem); + + /// As a client, submit part of a future mixing transaction to a Znode to start the process + bool SubmitDenominate(); + /// step 1: prepare denominated inputs and outputs + bool PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, std::vector& vecTxInRet, std::vector& vecTxOutRet); + /// step 2: send denominated inputs and outputs prepared in step 1 + bool SendDenominate(const std::vector& vecTxIn, const std::vector& vecTxOut); + + /// Get Znode updates about the progress of mixing + bool CheckPoolStateUpdate(PoolState nStateNew, int nEntriesCountNew, PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID, int nSessionIDNew=0); + // Set the 'state' value, with some logging and capturing when the state changed + void SetState(PoolState nStateNew); + + /// As a client, check and sign the final transaction + bool SignFinalTransaction(const CTransaction& finalTransactionNew, CNode* pnode); + + /// Relay mixing Messages + void RelayFinalTransaction(const CTransaction& txFinal); + void RelaySignaturesAnon(std::vector& vin); + void RelayInAnon(std::vector& vin, std::vector& vout); + void RelayIn(const CDarkSendEntry& entry); + void PushStatus(CNode* pnode, PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID); + void RelayStatus(PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID = MSG_NOERR); + void RelayCompletedTransaction(PoolMessage nMessageID); + + void SetNull(); + +public: + CZnode* pSubmittedToZnode; + int nSessionDenom; //Users must submit an denom matching this + int nCachedNumBlocks; //used for the overview screen + bool fCreateAutoBackups; //builtin support for automatic backups + CDarksendPool() : + nCachedLastSuccessBlock(0), + nMinBlockSpacing(0), + fUnitTest(false), + txMyCollateral(CMutableTransaction()), + nCachedNumBlocks(std::numeric_limits::max()), + fCreateAutoBackups(true) { SetNull(); } + + /** Process a mixing message using the protocol below + * \param pfrom + * \param strCommand lower case command string; valid values are: + * Command | Description + * -------- | ----------------- + * dsa | Acceptable + * dsc | Complete + * dsf | Final tx + * dsi | Vector of CTxIn + * dsq | Queue + * dss | Signal Final Tx + * dssu | status update + * \param vRecv + */ + void ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); + + void InitDenominations(); + void ClearSkippedDenominations() { vecDenominationsSkipped.clear(); } + + /// Get the denominations for a list of outputs (returns a bitshifted integer) + int GetDenominations(const std::vector& vecTxOut, bool fSingleRandomDenom = false); + int GetDenominations(const std::vector& vecTxDSOut); + std::string GetDenominationsToString(int nDenom); + bool GetDenominationsBits(int nDenom, std::vector &vecBitsRet); + + void SetMinBlockSpacing(int nMinBlockSpacingIn) { nMinBlockSpacing = nMinBlockSpacingIn; } + + void ResetPool(); + + void UnlockCoins(); + + int GetQueueSize() const { return vecDarksendQueue.size(); } + int GetState() const { return nState; } + std::string GetStateString() const; + std::string GetStatus(); + + int GetEntriesCount() const { return vecEntries.size(); } + + /// Passively run mixing in the background according to the configuration in settings + bool DoAutomaticDenominating(bool fDryRun=false); + + void CheckTimeout(); + void CheckForCompleteQueue(); + + /// Process a new block + void NewBlock(); + + void UpdatedBlockTip(const CBlockIndex *pindex); +}; + +// The main object for accessing mixing +extern CDarksendPool darkSendPool; +// A helper object for signing messages from Znodes +extern CDarkSendSigner darkSendSigner; + +void ThreadCheckDarkSendPool(); + +#endif \ No newline at end of file diff --git a/src/init.cpp b/src/init.cpp index 12f270f25b..f766667136 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -62,6 +62,14 @@ #include #include #include +#include "activeznode.h" +#include "darksend.h" +#include "znode-payments.h" +#include "znode-sync.h" +#include "znodeman.h" +#include "znodeconfig.h" +#include "netfulfilledman.h" +#include "spork.h" #if ENABLE_ZMQ #include "zmq/zmqnotificationinterface.h" @@ -1619,8 +1627,7 @@ bool AppInit2(boost::thread_group &threadGroup, CScheduler &scheduler) { std::vector vImportFiles; if (mapArgs.count("-loadblock")) { - BOOST_FOREACH( - const std::string &strFile, mapMultiArgs["-loadblock"]) + BOOST_FOREACH(const std::string &strFile, mapMultiArgs["-loadblock"]) vImportFiles.push_back(strFile); } @@ -1656,7 +1663,35 @@ bool AppInit2(boost::thread_group &threadGroup, CScheduler &scheduler) { // Generate coins in the background GenerateBitcoins(GetBoolArg("-gen", DEFAULT_GENERATE), GetArg("-genproclimit", DEFAULT_GENERATE_THREADS), chainparams); +// ********************************************************* Step 11a: setup PrivateSend + fZNode = GetBoolArg("-znode", false); + + if((fZNode || znodeConfig.getCount() > -1) && fTxIndex == false) { + return InitError("Enabling Znode support requires turning on transaction indexing." + "Please add txindex=1 to your configuration and start with -reindex"); + } + + if(fZNode) { + LogPrintf("ZNODE:\n"); + + if(!GetArg("-znodeaddr", "").empty()) { + // Hot znode (either local or remote) should get its address in + // CActiveZnode::ManageState() automatically and no longer relies on znodeaddr. + return InitError(_("znodeaddr option is deprecated. Please use znode.conf to manage your remote znodes.")); + } + + std::string strZNodePrivKey = GetArg("-znodeprivkey", ""); + if(!strZNodePrivKey.empty()) { + if(!darkSendSigner.GetKeysFromSecret(strZNodePrivKey, activeZnode.keyZnode, activeZnode.pubKeyZnode)) + return InitError(_("Invalid znodeprivkey. Please see documenation.")); + LogPrintf(" pubKeyZnode: %s\n", CBitcoinAddress(activeZnode.pubKeyZnode.GetID()).ToString()); + } else { + return InitError(_("You must specify a znodeprivkey in the configuration. Please see documentation for help.")); + } + } + LogPrintf("Using znode config file %s\n", GetZnodeConfigFile().string()); +// fPrivateSendMultiSession = GetBoolArg("-privatesendmultisession", DEFAULT_PRIVATESEND_MULTISESSION); // ********************************************************* Step 12: finished diff --git a/src/instantx.cpp b/src/instantx.cpp new file mode 100644 index 0000000000..47430403d5 --- /dev/null +++ b/src/instantx.cpp @@ -0,0 +1,1161 @@ +// Copyright (c) 2014-2017 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "activeznode.h" +#include "darksend.h" +#include "instantx.h" +#include "key.h" +#include "main.h" +#include "znode-sync.h" +#include "znodeman.h" +#include "net.h" +#include "protocol.h" +#include "spork.h" +#include "sync.h" +#include "txmempool.h" +#include "util.h" +#include "consensus/validation.h" + +#include +#include + +extern CWallet* pwalletMain; +extern CTxMemPool mempool; + +bool fEnableInstantSend = true; +int nInstantSendDepth = DEFAULT_INSTANTSEND_DEPTH; +int nCompleteTXLocks; + +CInstantSend instantsend; + +// Transaction Locks +// +// step 1) Some node announces intention to lock transaction inputs via "txlreg" message +// step 2) Top COutPointLock::SIGNATURES_TOTAL znodes per each spent outpoint push "txvote" message +// step 3) Once there are COutPointLock::SIGNATURES_REQUIRED valid "txvote" messages per each spent outpoint +// for a corresponding "txlreg" message, all outpoints from that tx are treated as locked + +// +// CInstantSend +// + +void CInstantSend::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) +{ + if(fLiteMode) return; // disable all Dash specific functionality +// if(!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return; + + // Ignore any InstantSend messages until znode list is synced + if(!znodeSync.IsZnodeListSynced()) return; + + // NOTE: NetMsgType::TXLOCKREQUEST is handled via ProcessMessage() in main.cpp + +// if (strCommand == NetMsgType::TXLOCKVOTE) // InstantSend Transaction Lock Consensus Votes +// { +// if(pfrom->nVersion < MIN_INSTANTSEND_PROTO_VERSION) return; +// +// CTxLockVote vote; +// vRecv >> vote; +// +// LOCK2(cs_main, cs_instantsend); +// +// uint256 nVoteHash = vote.GetHash(); +// +// if(mapTxLockVotes.count(nVoteHash)) return; +// mapTxLockVotes.insert(std::make_pair(nVoteHash, vote)); +// +// ProcessTxLockVote(pfrom, vote); +// +// return; +// } +} + +bool CInstantSend::ProcessTxLockRequest(const CTxLockRequest& txLockRequest) +{ + LOCK2(cs_main, cs_instantsend); + + uint256 txHash = txLockRequest.GetHash(); + + // Check to see if we conflict with existing completed lock, + // fail if so, there can't be 2 completed locks for the same outpoint + BOOST_FOREACH(const CTxIn& txin, txLockRequest.vin) { + std::map::iterator it = mapLockedOutpoints.find(txin.prevout); + if(it != mapLockedOutpoints.end()) { + // Conflicting with complete lock, ignore this one + // (this could be the one we have but we don't want to try to lock it twice anyway) + LogPrintf("CInstantSend::ProcessTxLockRequest -- WARNING: Found conflicting completed Transaction Lock, skipping current one, txid=%s, completed lock txid=%s\n", + txLockRequest.GetHash().ToString(), it->second.ToString()); + return false; + } + } + + // Check to see if there are votes for conflicting request, + // if so - do not fail, just warn user + BOOST_FOREACH(const CTxIn& txin, txLockRequest.vin) { + std::map >::iterator it = mapVotedOutpoints.find(txin.prevout); + if(it != mapVotedOutpoints.end()) { + BOOST_FOREACH(const uint256& hash, it->second) { + if(hash != txLockRequest.GetHash()) { + LogPrint("instantsend", "CInstantSend::ProcessTxLockRequest -- Double spend attempt! %s\n", txin.prevout.ToStringShort()); + // do not fail here, let it go and see which one will get the votes to be locked + } + } + } + } + + if(!CreateTxLockCandidate(txLockRequest)) { + // smth is not right + LogPrintf("CInstantSend::ProcessTxLockRequest -- CreateTxLockCandidate failed, txid=%s\n", txHash.ToString()); + return false; + } + LogPrintf("CInstantSend::ProcessTxLockRequest -- accepted, txid=%s\n", txHash.ToString()); + + std::map::iterator itLockCandidate = mapTxLockCandidates.find(txHash); + CTxLockCandidate& txLockCandidate = itLockCandidate->second; + Vote(txLockCandidate); + ProcessOrphanTxLockVotes(); + + // Znodes will sometimes propagate votes before the transaction is known to the client. + // If this just happened - lock inputs, resolve conflicting locks, update transaction status + // forcing external script notification. + TryToFinalizeLockCandidate(txLockCandidate); + + return true; +} + +bool CInstantSend::CreateTxLockCandidate(const CTxLockRequest& txLockRequest) +{ + // Normally we should require all outpoints to be unspent, but in case we are reprocessing + // because of a lot of legit orphan votes we should also check already spent outpoints. + uint256 txHash = txLockRequest.GetHash(); + if(!txLockRequest.IsValid(!IsEnoughOrphanVotesForTx(txLockRequest))) return false; + + LOCK(cs_instantsend); + + std::map::iterator itLockCandidate = mapTxLockCandidates.find(txHash); + if(itLockCandidate == mapTxLockCandidates.end()) { + LogPrintf("CInstantSend::CreateTxLockCandidate -- new, txid=%s\n", txHash.ToString()); + + CTxLockCandidate txLockCandidate(txLockRequest); + // all inputs should already be checked by txLockRequest.IsValid() above, just use them now + BOOST_REVERSE_FOREACH(const CTxIn& txin, txLockRequest.vin) { + txLockCandidate.AddOutPointLock(txin.prevout); + } + mapTxLockCandidates.insert(std::make_pair(txHash, txLockCandidate)); + } else { + LogPrint("instantsend", "CInstantSend::CreateTxLockCandidate -- seen, txid=%s\n", txHash.ToString()); + } + + return true; +} + +void CInstantSend::Vote(CTxLockCandidate& txLockCandidate) +{ + if(!fZNode) return; + + LOCK2(cs_main, cs_instantsend); + + uint256 txHash = txLockCandidate.GetHash(); + // check if we need to vote on this candidate's outpoints, + // it's possible that we need to vote for several of them + std::map::iterator itOutpointLock = txLockCandidate.mapOutPointLocks.begin(); + while(itOutpointLock != txLockCandidate.mapOutPointLocks.end()) { + + int nPrevoutHeight = GetUTXOHeight(itOutpointLock->first); + if(nPrevoutHeight == -1) { + LogPrint("instantsend", "CInstantSend::Vote -- Failed to find UTXO %s\n", itOutpointLock->first.ToStringShort()); + return; + } + + int nLockInputHeight = nPrevoutHeight + 4; + + int n = mnodeman.GetZnodeRank(activeZnode.vin, nLockInputHeight, MIN_INSTANTSEND_PROTO_VERSION); + + if(n == -1) { + LogPrint("instantsend", "CInstantSend::Vote -- Unknown Znode %s\n", activeZnode.vin.prevout.ToStringShort()); + ++itOutpointLock; + continue; + } + + int nSignaturesTotal = COutPointLock::SIGNATURES_TOTAL; + if(n > nSignaturesTotal) { + LogPrint("instantsend", "CInstantSend::Vote -- Znode not in the top %d (%d)\n", nSignaturesTotal, n); + ++itOutpointLock; + continue; + } + + LogPrint("instantsend", "CInstantSend::Vote -- In the top %d (%d)\n", nSignaturesTotal, n); + + std::map >::iterator itVoted = mapVotedOutpoints.find(itOutpointLock->first); + + // Check to see if we already voted for this outpoint, + // refuse to vote twice or to include the same outpoint in another tx + bool fAlreadyVoted = false; + if(itVoted != mapVotedOutpoints.end()) { + BOOST_FOREACH(const uint256& hash, itVoted->second) { + std::map::iterator it2 = mapTxLockCandidates.find(hash); + if(it2->second.HasZnodeVoted(itOutpointLock->first, activeZnode.vin.prevout)) { + // we already voted for this outpoint to be included either in the same tx or in a competing one, + // skip it anyway + fAlreadyVoted = true; + LogPrintf("CInstantSend::Vote -- WARNING: We already voted for this outpoint, skipping: txHash=%s, outpoint=%s\n", + txHash.ToString(), itOutpointLock->first.ToStringShort()); + break; + } + } + } + if(fAlreadyVoted) { + ++itOutpointLock; + continue; // skip to the next outpoint + } + + // we haven't voted for this outpoint yet, let's try to do this now + CTxLockVote vote(txHash, itOutpointLock->first, activeZnode.vin.prevout); + + if(!vote.Sign()) { + LogPrintf("CInstantSend::Vote -- Failed to sign consensus vote\n"); + return; + } + if(!vote.CheckSignature()) { + LogPrintf("CInstantSend::Vote -- Signature invalid\n"); + return; + } + + // vote constructed sucessfully, let's store and relay it + uint256 nVoteHash = vote.GetHash(); + mapTxLockVotes.insert(std::make_pair(nVoteHash, vote)); + if(itOutpointLock->second.AddVote(vote)) { + LogPrintf("CInstantSend::Vote -- Vote created successfully, relaying: txHash=%s, outpoint=%s, vote=%s\n", + txHash.ToString(), itOutpointLock->first.ToStringShort(), nVoteHash.ToString()); + + if(itVoted == mapVotedOutpoints.end()) { + std::set setHashes; + setHashes.insert(txHash); + mapVotedOutpoints.insert(std::make_pair(itOutpointLock->first, setHashes)); + } else { + mapVotedOutpoints[itOutpointLock->first].insert(txHash); + if(mapVotedOutpoints[itOutpointLock->first].size() > 1) { + // it's ok to continue, just warn user + LogPrintf("CInstantSend::Vote -- WARNING: Vote conflicts with some existing votes: txHash=%s, outpoint=%s, vote=%s\n", + txHash.ToString(), itOutpointLock->first.ToStringShort(), nVoteHash.ToString()); + } + } + + vote.Relay(); + } + + ++itOutpointLock; + } +} + +//received a consensus vote +bool CInstantSend::ProcessTxLockVote(CNode* pfrom, CTxLockVote& vote) +{ + LOCK2(cs_main, cs_instantsend); + + uint256 txHash = vote.GetTxHash(); + + if(!vote.IsValid(pfrom)) { + // could be because of missing MN + LogPrint("instantsend", "CInstantSend::ProcessTxLockVote -- Vote is invalid, txid=%s\n", txHash.ToString()); + return false; + } + + // Znodes will sometimes propagate votes before the transaction is known to the client, + // will actually process only after the lock request itself has arrived + + std::map::iterator it = mapTxLockCandidates.find(txHash); + if(it == mapTxLockCandidates.end()) { + if(!mapTxLockVotesOrphan.count(vote.GetHash())) { + mapTxLockVotesOrphan[vote.GetHash()] = vote; + LogPrint("instantsend", "CInstantSend::ProcessTxLockVote -- Orphan vote: txid=%s znode=%s new\n", + txHash.ToString(), vote.GetZnodeOutpoint().ToStringShort()); + bool fReprocess = true; + std::map::iterator itLockRequest = mapLockRequestAccepted.find(txHash); + if(itLockRequest == mapLockRequestAccepted.end()) { + itLockRequest = mapLockRequestRejected.find(txHash); + if(itLockRequest == mapLockRequestRejected.end()) { + // still too early, wait for tx lock request + fReprocess = false; + } + } + if(fReprocess && IsEnoughOrphanVotesForTx(itLockRequest->second)) { + // We have enough votes for corresponding lock to complete, + // tx lock request should already be received at this stage. + LogPrint("instantsend", "CInstantSend::ProcessTxLockVote -- Found enough orphan votes, reprocessing Transaction Lock Request: txid=%s\n", txHash.ToString()); + ProcessTxLockRequest(itLockRequest->second); + return true; + } + } else { + LogPrint("instantsend", "CInstantSend::ProcessTxLockVote -- Orphan vote: txid=%s znode=%s seen\n", + txHash.ToString(), vote.GetZnodeOutpoint().ToStringShort()); + } + + // This tracks those messages and allows only the same rate as of the rest of the network + // TODO: make sure this works good enough for multi-quorum + + int nZnodeOrphanExpireTime = GetTime() + 60*10; // keep time data for 10 minutes + if(!mapZnodeOrphanVotes.count(vote.GetZnodeOutpoint())) { + mapZnodeOrphanVotes[vote.GetZnodeOutpoint()] = nZnodeOrphanExpireTime; + } else { + int64_t nPrevOrphanVote = mapZnodeOrphanVotes[vote.GetZnodeOutpoint()]; + if(nPrevOrphanVote > GetTime() && nPrevOrphanVote > GetAverageZnodeOrphanVoteTime()) { + LogPrint("instantsend", "CInstantSend::ProcessTxLockVote -- znode is spamming orphan Transaction Lock Votes: txid=%s znode=%s\n", + txHash.ToString(), vote.GetZnodeOutpoint().ToStringShort()); + // Misbehaving(pfrom->id, 1); + return false; + } + // not spamming, refresh + mapZnodeOrphanVotes[vote.GetZnodeOutpoint()] = nZnodeOrphanExpireTime; + } + + return true; + } + + LogPrint("instantsend", "CInstantSend::ProcessTxLockVote -- Transaction Lock Vote, txid=%s\n", txHash.ToString()); + + std::map >::iterator it1 = mapVotedOutpoints.find(vote.GetOutpoint()); + if(it1 != mapVotedOutpoints.end()) { + BOOST_FOREACH(const uint256& hash, it1->second) { + if(hash != txHash) { + // same outpoint was already voted to be locked by another tx lock request, + // find out if the same mn voted on this outpoint before + std::map::iterator it2 = mapTxLockCandidates.find(hash); + if(it2->second.HasZnodeVoted(vote.GetOutpoint(), vote.GetZnodeOutpoint())) { + // yes, it did, refuse to accept a vote to include the same outpoint in another tx + // from the same znode. + // TODO: apply pose ban score to this znode? + // NOTE: if we decide to apply pose ban score here, this vote must be relayed further + // to let all other nodes know about this node's misbehaviour and let them apply + // pose ban score too. + LogPrintf("CInstantSend::ProcessTxLockVote -- znode sent conflicting votes! %s\n", vote.GetZnodeOutpoint().ToStringShort()); + return false; + } + } + } + // we have votes by other znodes only (so far), let's continue and see who will win + it1->second.insert(txHash); + } else { + std::set setHashes; + setHashes.insert(txHash); + mapVotedOutpoints.insert(std::make_pair(vote.GetOutpoint(), setHashes)); + } + + CTxLockCandidate& txLockCandidate = it->second; + + if(!txLockCandidate.AddVote(vote)) { + // this should never happen + return false; + } + + int nSignatures = txLockCandidate.CountVotes(); + int nSignaturesMax = txLockCandidate.txLockRequest.GetMaxSignatures(); + LogPrint("instantsend", "CInstantSend::ProcessTxLockVote -- Transaction Lock signatures count: %d/%d, vote hash=%s\n", + nSignatures, nSignaturesMax, vote.GetHash().ToString()); + + TryToFinalizeLockCandidate(txLockCandidate); + + vote.Relay(); + + return true; +} + +void CInstantSend::ProcessOrphanTxLockVotes() +{ + LOCK2(cs_main, cs_instantsend); + std::map::iterator it = mapTxLockVotesOrphan.begin(); + while(it != mapTxLockVotesOrphan.end()) { + if(ProcessTxLockVote(NULL, it->second)) { + mapTxLockVotesOrphan.erase(it++); + } else { + ++it; + } + } +} + +bool CInstantSend::IsEnoughOrphanVotesForTx(const CTxLockRequest& txLockRequest) +{ + // There could be a situation when we already have quite a lot of votes + // but tx lock request still wasn't received. Let's scan through + // orphan votes to check if this is the case. + BOOST_FOREACH(const CTxIn& txin, txLockRequest.vin) { + if(!IsEnoughOrphanVotesForTxAndOutPoint(txLockRequest.GetHash(), txin.prevout)) { + return false; + } + } + return true; +} + +bool CInstantSend::IsEnoughOrphanVotesForTxAndOutPoint(const uint256& txHash, const COutPoint& outpoint) +{ + // Scan orphan votes to check if this outpoint has enough orphan votes to be locked in some tx. + LOCK2(cs_main, cs_instantsend); + int nCountVotes = 0; + std::map::iterator it = mapTxLockVotesOrphan.begin(); + while(it != mapTxLockVotesOrphan.end()) { + if(it->second.GetTxHash() == txHash && it->second.GetOutpoint() == outpoint) { + nCountVotes++; + if(nCountVotes >= COutPointLock::SIGNATURES_REQUIRED) { + return true; + } + } + ++it; + } + return false; +} + +void CInstantSend::TryToFinalizeLockCandidate(const CTxLockCandidate& txLockCandidate) +{ + LOCK2(cs_main, cs_instantsend); + + uint256 txHash = txLockCandidate.txLockRequest.GetHash(); + if(txLockCandidate.IsAllOutPointsReady() && !IsLockedInstantSendTransaction(txHash)) { + // we have enough votes now + LogPrint("instantsend", "CInstantSend::TryToFinalizeLockCandidate -- Transaction Lock is ready to complete, txid=%s\n", txHash.ToString()); + if(ResolveConflicts(txLockCandidate, Params().GetConsensus().nInstantSendKeepLock)) { + LockTransactionInputs(txLockCandidate); + UpdateLockedTransaction(txLockCandidate); + } + } +} + +void CInstantSend::UpdateLockedTransaction(const CTxLockCandidate& txLockCandidate) +{ + LOCK(cs_instantsend); + + uint256 txHash = txLockCandidate.GetHash(); + + if(!IsLockedInstantSendTransaction(txHash)) return; // not a locked tx, do not update/notify + +#ifdef ENABLE_WALLET + if(pwalletMain) { + pwalletMain->UpdatedTransaction(txHash); + // bumping this to update UI + nCompleteTXLocks++; + // notify an external script once threshold is reached + std::string strCmd = GetArg("-instantsendnotify", ""); + if(!strCmd.empty()) { + boost::replace_all(strCmd, "%s", txHash.GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } + } +#endif + +// GetMainSignals().NotifyTransactionLock(txLockCandidate.txLockRequest); + + LogPrint("instantsend", "CInstantSend::UpdateLockedTransaction -- done, txid=%s\n", txHash.ToString()); +} + +void CInstantSend::LockTransactionInputs(const CTxLockCandidate& txLockCandidate) +{ + LOCK(cs_instantsend); + + uint256 txHash = txLockCandidate.GetHash(); + + if(!txLockCandidate.IsAllOutPointsReady()) return; + + std::map::const_iterator it = txLockCandidate.mapOutPointLocks.begin(); + + while(it != txLockCandidate.mapOutPointLocks.end()) { + mapLockedOutpoints.insert(std::make_pair(it->first, txHash)); + ++it; + } + LogPrint("instantsend", "CInstantSend::LockTransactionInputs -- done, txid=%s\n", txHash.ToString()); +} + +bool CInstantSend::GetLockedOutPointTxHash(const COutPoint& outpoint, uint256& hashRet) +{ + LOCK(cs_instantsend); + std::map::iterator it = mapLockedOutpoints.find(outpoint); + if(it == mapLockedOutpoints.end()) return false; + hashRet = it->second; + return true; +} + +bool CInstantSend::ResolveConflicts(const CTxLockCandidate& txLockCandidate, int nMaxBlocks) +{ + if(nMaxBlocks < 1) return false; + + LOCK2(cs_main, cs_instantsend); + + uint256 txHash = txLockCandidate.GetHash(); + + // make sure the lock is ready + if(!txLockCandidate.IsAllOutPointsReady()) return true; // not an error + + LOCK(mempool.cs); // protect mempool.mapNextTx, mempool.mapTx + + bool fMempoolConflict = false; + + BOOST_FOREACH(const CTxIn& txin, txLockCandidate.txLockRequest.vin) { + uint256 hashConflicting; + if(GetLockedOutPointTxHash(txin.prevout, hashConflicting) && txHash != hashConflicting) { + // conflicting with complete lock, ignore current one + LogPrintf("CInstantSend::ResolveConflicts -- WARNING: Found conflicting completed Transaction Lock, skipping current one, txid=%s, conflicting txid=%s\n", + txHash.ToString(), hashConflicting.ToString()); + return false; // can't/shouldn't do anything + } else if (mempool.mapNextTx.count(txin.prevout)) { + // check if it's in mempool +// hashConflicting = mempool.mapNextTx[txin.prevout].ptx->GetHash(); + if(txHash == hashConflicting) continue; // matches current, not a conflict, skip to next txin + // conflicting with tx in mempool + fMempoolConflict = true; + if(HasTxLockRequest(hashConflicting)) { + // There can be only one completed lock, the other lock request should never complete + LogPrintf("CInstantSend::ResolveConflicts -- WARNING: Found conflicting Transaction Lock Request, replacing by completed Transaction Lock, txid=%s, conflicting txid=%s\n", + txHash.ToString(), hashConflicting.ToString()); + } else { + // If this lock is completed, we don't really care about normal conflicting txes. + LogPrintf("CInstantSend::ResolveConflicts -- WARNING: Found conflicting transaction, replacing by completed Transaction Lock, txid=%s, conflicting txid=%s\n", + txHash.ToString(), hashConflicting.ToString()); + } + } + } // FOREACH + if(fMempoolConflict) { + std::list removed; + // remove every tx conflicting with current Transaction Lock Request + mempool.removeConflicts(txLockCandidate.txLockRequest, removed); + // and try to accept it in mempool again + CValidationState state; + bool fMissingInputs = false; + if(!AcceptToMemoryPool(mempool, state, txLockCandidate.txLockRequest, true, true, &fMissingInputs)) { + LogPrintf("CInstantSend::ResolveConflicts -- ERROR: Failed to accept completed Transaction Lock to mempool, txid=%s\n", txHash.ToString()); + return false; + } + LogPrintf("CInstantSend::ResolveConflicts -- Accepted completed Transaction Lock, txid=%s\n", txHash.ToString()); + return true; + } + // No conflicts were found so far, check to see if it was already included in block + CTransaction txTmp; + uint256 hashBlock; + if(GetTransaction(txHash, txTmp, Params().GetConsensus(), hashBlock, true) && hashBlock != uint256()) { + LogPrint("instantsend", "CInstantSend::ResolveConflicts -- Done, %s is included in block %s\n", txHash.ToString(), hashBlock.ToString()); + return true; + } + // Not in block yet, make sure all its inputs are still unspent + BOOST_FOREACH(const CTxIn& txin, txLockCandidate.txLockRequest.vin) { + CCoins coins; + if(!pcoinsTip->GetCoins(txin.prevout.hash, coins) || + (unsigned int)txin.prevout.n>=coins.vout.size() || + coins.vout[txin.prevout.n].IsNull()) { + // Not in UTXO anymore? A conflicting tx was mined while we were waiting for votes. + // Reprocess tip to make sure tx for this lock is included. + LogPrintf("CTxLockRequest::ResolveConflicts -- Failed to find UTXO %s - disconnecting tip...\n", txin.prevout.ToStringShort()); + if(!DisconnectBlocks(1)) { + return false; + } + // Recursively check at "new" old height. Conflicting tx should be rejected by AcceptToMemoryPool. + ResolveConflicts(txLockCandidate, nMaxBlocks - 1); + LogPrintf("CTxLockRequest::ResolveConflicts -- Failed to find UTXO %s - activating best chain...\n", txin.prevout.ToStringShort()); + // Activate best chain, block which includes conflicting tx should be rejected by ConnectBlock. + CValidationState state; + if(!ActivateBestChain(state, Params()) || !state.IsValid()) { + LogPrintf("CTxLockRequest::ResolveConflicts -- ActivateBestChain failed, txid=%s\n", txin.prevout.ToStringShort()); + return false; + } + LogPrintf("CTxLockRequest::ResolveConflicts -- Failed to find UTXO %s - fixed!\n", txin.prevout.ToStringShort()); + } + } + LogPrint("instantsend", "CInstantSend::ResolveConflicts -- Done, txid=%s\n", txHash.ToString()); + + return true; +} + +int64_t CInstantSend::GetAverageZnodeOrphanVoteTime() +{ + LOCK(cs_instantsend); + // NOTE: should never actually call this function when mapZnodeOrphanVotes is empty + if(mapZnodeOrphanVotes.empty()) return 0; + + std::map::iterator it = mapZnodeOrphanVotes.begin(); + int64_t total = 0; + + while(it != mapZnodeOrphanVotes.end()) { + total+= it->second; + ++it; + } + + return total / mapZnodeOrphanVotes.size(); +} + +void CInstantSend::CheckAndRemove() +{ + if(!pCurrentBlockIndex) return; + + LOCK(cs_instantsend); + + std::map::iterator itLockCandidate = mapTxLockCandidates.begin(); + + // remove expired candidates + while(itLockCandidate != mapTxLockCandidates.end()) { + CTxLockCandidate &txLockCandidate = itLockCandidate->second; + uint256 txHash = txLockCandidate.GetHash(); + if(txLockCandidate.IsExpired(pCurrentBlockIndex->nHeight)) { + LogPrintf("CInstantSend::CheckAndRemove -- Removing expired Transaction Lock Candidate: txid=%s\n", txHash.ToString()); + std::map::iterator itOutpointLock = txLockCandidate.mapOutPointLocks.begin(); + while(itOutpointLock != txLockCandidate.mapOutPointLocks.end()) { + mapLockedOutpoints.erase(itOutpointLock->first); + mapVotedOutpoints.erase(itOutpointLock->first); + ++itOutpointLock; + } + mapLockRequestAccepted.erase(txHash); + mapLockRequestRejected.erase(txHash); + mapTxLockCandidates.erase(itLockCandidate++); + } else { + ++itLockCandidate; + } + } + + // remove expired votes + std::map::iterator itVote = mapTxLockVotes.begin(); + while(itVote != mapTxLockVotes.end()) { + if(itVote->second.IsExpired(pCurrentBlockIndex->nHeight)) { + LogPrint("instantsend", "CInstantSend::CheckAndRemove -- Removing expired vote: txid=%s znode=%s\n", + itVote->second.GetTxHash().ToString(), itVote->second.GetZnodeOutpoint().ToStringShort()); + mapTxLockVotes.erase(itVote++); + } else { + ++itVote; + } + } + + // remove expired orphan votes + std::map::iterator itOrphanVote = mapTxLockVotesOrphan.begin(); + while(itOrphanVote != mapTxLockVotesOrphan.end()) { + if(GetTime() - itOrphanVote->second.GetTimeCreated() > ORPHAN_VOTE_SECONDS) { + LogPrint("instantsend", "CInstantSend::CheckAndRemove -- Removing expired orphan vote: txid=%s znode=%s\n", + itOrphanVote->second.GetTxHash().ToString(), itOrphanVote->second.GetZnodeOutpoint().ToStringShort()); + mapTxLockVotes.erase(itOrphanVote->first); + mapTxLockVotesOrphan.erase(itOrphanVote++); + } else { + ++itOrphanVote; + } + } + + // remove expired znode orphan votes (DOS protection) + std::map::iterator itZnodeOrphan = mapZnodeOrphanVotes.begin(); + while(itZnodeOrphan != mapZnodeOrphanVotes.end()) { + if(itZnodeOrphan->second < GetTime()) { + LogPrint("instantsend", "CInstantSend::CheckAndRemove -- Removing expired orphan znode vote: znode=%s\n", + itZnodeOrphan->first.ToStringShort()); + mapZnodeOrphanVotes.erase(itZnodeOrphan++); + } else { + ++itZnodeOrphan; + } + } +} + +bool CInstantSend::AlreadyHave(const uint256& hash) +{ + LOCK(cs_instantsend); + return mapLockRequestAccepted.count(hash) || + mapLockRequestRejected.count(hash) || + mapTxLockVotes.count(hash); +} + +void CInstantSend::AcceptLockRequest(const CTxLockRequest& txLockRequest) +{ + LOCK(cs_instantsend); + mapLockRequestAccepted.insert(make_pair(txLockRequest.GetHash(), txLockRequest)); +} + +void CInstantSend::RejectLockRequest(const CTxLockRequest& txLockRequest) +{ + LOCK(cs_instantsend); + mapLockRequestRejected.insert(make_pair(txLockRequest.GetHash(), txLockRequest)); +} + +bool CInstantSend::HasTxLockRequest(const uint256& txHash) +{ + CTxLockRequest txLockRequestTmp; + return GetTxLockRequest(txHash, txLockRequestTmp); +} + +bool CInstantSend::GetTxLockRequest(const uint256& txHash, CTxLockRequest& txLockRequestRet) +{ + LOCK(cs_instantsend); + + std::map::iterator it = mapTxLockCandidates.find(txHash); + if(it == mapTxLockCandidates.end()) return false; + txLockRequestRet = it->second.txLockRequest; + + return true; +} + +bool CInstantSend::GetTxLockVote(const uint256& hash, CTxLockVote& txLockVoteRet) +{ + LOCK(cs_instantsend); + + std::map::iterator it = mapTxLockVotes.find(hash); + if(it == mapTxLockVotes.end()) return false; + txLockVoteRet = it->second; + + return true; +} + +bool CInstantSend::IsInstantSendReadyToLock(const uint256& txHash) +{ +// if(!fEnableInstantSend || fLargeWorkForkFound || fLargeWorkInvalidChainFound || +// !sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return false; + + LOCK(cs_instantsend); + // There must be a successfully verified lock request + // and all outputs must be locked (i.e. have enough signatures) + std::map::iterator it = mapTxLockCandidates.find(txHash); + return it != mapTxLockCandidates.end() && it->second.IsAllOutPointsReady(); +} + +bool CInstantSend::IsLockedInstantSendTransaction(const uint256& txHash) +{ +// if(!fEnableInstantSend || fLargeWorkForkFound || fLargeWorkInvalidChainFound || +// !sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return false; + + LOCK(cs_instantsend); + + // there must be a lock candidate + std::map::iterator itLockCandidate = mapTxLockCandidates.find(txHash); + if(itLockCandidate == mapTxLockCandidates.end()) return false; + + // which should have outpoints + if(itLockCandidate->second.mapOutPointLocks.empty()) return false; + + // and all of these outputs must be included in mapLockedOutpoints with correct hash + std::map::iterator itOutpointLock = itLockCandidate->second.mapOutPointLocks.begin(); + while(itOutpointLock != itLockCandidate->second.mapOutPointLocks.end()) { + uint256 hashLocked; + if(!GetLockedOutPointTxHash(itOutpointLock->first, hashLocked) || hashLocked != txHash) return false; + ++itOutpointLock; + } + + return true; +} + +int CInstantSend::GetTransactionLockSignatures(const uint256& txHash) +{ + if(!fEnableInstantSend) return -1; +// if(fLargeWorkForkFound || fLargeWorkInvalidChainFound) return -2; +// if(!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return -3; + + LOCK(cs_instantsend); + + std::map::iterator itLockCandidate = mapTxLockCandidates.find(txHash); + if(itLockCandidate != mapTxLockCandidates.end()) { + return itLockCandidate->second.CountVotes(); + } + + return -1; +} + +bool CInstantSend::IsTxLockRequestTimedOut(const uint256& txHash) +{ + if(!fEnableInstantSend) return false; + + LOCK(cs_instantsend); + + std::map::iterator itLockCandidate = mapTxLockCandidates.find(txHash); + if (itLockCandidate != mapTxLockCandidates.end()) { + return !itLockCandidate->second.IsAllOutPointsReady() && + itLockCandidate->second.txLockRequest.IsTimedOut(); + } + + return false; +} + +void CInstantSend::Relay(const uint256& txHash) +{ + LOCK(cs_instantsend); + + std::map::const_iterator itLockCandidate = mapTxLockCandidates.find(txHash); + if (itLockCandidate != mapTxLockCandidates.end()) { + itLockCandidate->second.Relay(); + } +} + +void CInstantSend::UpdatedBlockTip(const CBlockIndex *pindex) +{ + pCurrentBlockIndex = pindex; +} + +void CInstantSend::SyncTransaction(const CTransaction& tx, const CBlock* pblock) +{ + // Update lock candidates and votes if corresponding tx confirmed + // or went from confirmed to 0-confirmed or conflicted. + + if (tx.IsCoinBase()) return; + + LOCK2(cs_main, cs_instantsend); + + uint256 txHash = tx.GetHash(); + + // When tx is 0-confirmed or conflicted, pblock is NULL and nHeightNew should be set to -1 + CBlockIndex* pblockindex = pblock ? mapBlockIndex[pblock->GetHash()] : NULL; + int nHeightNew = pblockindex ? pblockindex->nHeight : -1; + + LogPrint("instantsend", "CInstantSend::SyncTransaction -- txid=%s nHeightNew=%d\n", txHash.ToString(), nHeightNew); + + // Check lock candidates + std::map::iterator itLockCandidate = mapTxLockCandidates.find(txHash); + if(itLockCandidate != mapTxLockCandidates.end()) { + LogPrint("instantsend", "CInstantSend::SyncTransaction -- txid=%s nHeightNew=%d lock candidate updated\n", + txHash.ToString(), nHeightNew); + itLockCandidate->second.SetConfirmedHeight(nHeightNew); + // Loop through outpoint locks + std::map::iterator itOutpointLock = itLockCandidate->second.mapOutPointLocks.begin(); + while(itOutpointLock != itLockCandidate->second.mapOutPointLocks.end()) { + // Check corresponding lock votes + std::vector vVotes = itOutpointLock->second.GetVotes(); + std::vector::iterator itVote = vVotes.begin(); + std::map::iterator it; + while(itVote != vVotes.end()) { + uint256 nVoteHash = itVote->GetHash(); + LogPrint("instantsend", "CInstantSend::SyncTransaction -- txid=%s nHeightNew=%d vote %s updated\n", + txHash.ToString(), nHeightNew, nVoteHash.ToString()); + it = mapTxLockVotes.find(nVoteHash); + if(it != mapTxLockVotes.end()) { + it->second.SetConfirmedHeight(nHeightNew); + } + ++itVote; + } + ++itOutpointLock; + } + } + + // check orphan votes + std::map::iterator itOrphanVote = mapTxLockVotesOrphan.begin(); + while(itOrphanVote != mapTxLockVotesOrphan.end()) { + if(itOrphanVote->second.GetTxHash() == txHash) { + LogPrint("instantsend", "CInstantSend::SyncTransaction -- txid=%s nHeightNew=%d vote %s updated\n", + txHash.ToString(), nHeightNew, itOrphanVote->first.ToString()); + mapTxLockVotes[itOrphanVote->first].SetConfirmedHeight(nHeightNew); + } + ++itOrphanVote; + } +} + +// +// CTxLockRequest +// + +bool CTxLockRequest::IsValid(bool fRequireUnspent) const +{ + if(vout.size() < 1) return false; + + if(vin.size() > WARN_MANY_INPUTS) { + LogPrint("instantsend", "CTxLockRequest::IsValid -- WARNING: Too many inputs: tx=%s", ToString()); + } + + LOCK(cs_main); + if(!CheckFinalTx(*this)) { + LogPrint("instantsend", "CTxLockRequest::IsValid -- Transaction is not final: tx=%s", ToString()); + return false; + } + + CAmount nValueIn = 0; + CAmount nValueOut = 0; + + BOOST_FOREACH(const CTxOut& txout, vout) { + // InstantSend supports normal scripts and unspendable (i.e. data) scripts. + // TODO: Look into other script types that are normal and can be included + if(!txout.scriptPubKey.IsNormalPaymentScript() && !txout.scriptPubKey.IsUnspendable()) { + LogPrint("instantsend", "CTxLockRequest::IsValid -- Invalid Script %s", ToString()); + return false; + } + nValueOut += txout.nValue; + } + + BOOST_FOREACH(const CTxIn& txin, vin) { + + CCoins coins; + int nPrevoutHeight = 0; + CAmount nValue = 0; + + if(!pcoinsTip->GetCoins(txin.prevout.hash, coins) || + (unsigned int)txin.prevout.n>=coins.vout.size() || + coins.vout[txin.prevout.n].IsNull()) { + LogPrint("instantsend", "CTxLockRequest::IsValid -- Failed to find UTXO %s\n", txin.prevout.ToStringShort()); + // Normally above sould be enough, but in case we are reprocessing this because of + // a lot of legit orphan votes we should also check already spent outpoints. + if(fRequireUnspent) return false; + CTransaction txOutpointCreated; + uint256 nHashOutpointConfirmed; + if(!GetTransaction(txin.prevout.hash, txOutpointCreated, Params().GetConsensus(), nHashOutpointConfirmed, true) || nHashOutpointConfirmed == uint256()) { + LogPrint("instantsend", "CTxLockRequest::IsValid -- Failed to find outpoint %s\n", txin.prevout.ToStringShort()); + return false; + } + if(txin.prevout.n >= txOutpointCreated.vout.size()) { + LogPrint("instantsend", "CTxLockRequest::IsValid -- Outpoint %s is out of bounds, size() = %lld\n", + txin.prevout.ToStringShort(), txOutpointCreated.vout.size()); + return false; + } + BlockMap::iterator mi = mapBlockIndex.find(nHashOutpointConfirmed); + if(mi == mapBlockIndex.end() || !mi->second) { + // shouldn't happen + LogPrint("instantsend", "CTxLockRequest::IsValid -- Failed to find block %s for outpoint %s\n", + nHashOutpointConfirmed.ToString(), txin.prevout.ToStringShort()); + return false; + } + nPrevoutHeight = mi->second->nHeight; + nValue = txOutpointCreated.vout[txin.prevout.n].nValue; + } else { + nPrevoutHeight = coins.nHeight; + nValue = coins.vout[txin.prevout.n].nValue; + } + + int nTxAge = chainActive.Height() - nPrevoutHeight + 1; + // 1 less than the "send IX" gui requires, in case of a block propagating the network at the time + int nConfirmationsRequired = INSTANTSEND_CONFIRMATIONS_REQUIRED - 1; + + if(nTxAge < nConfirmationsRequired) { + LogPrint("instantsend", "CTxLockRequest::IsValid -- outpoint %s too new: nTxAge=%d, nConfirmationsRequired=%d, txid=%s\n", + txin.prevout.ToStringShort(), nTxAge, nConfirmationsRequired, GetHash().ToString()); + return false; + } + + nValueIn += nValue; + } + +// if(nValueOut > sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)*COIN) { +// LogPrint("instantsend", "CTxLockRequest::IsValid -- Transaction value too high: nValueOut=%d, tx=%s", nValueOut, ToString()); +// return false; +// } + + if(nValueIn - nValueOut < GetMinFee()) { + LogPrint("instantsend", "CTxLockRequest::IsValid -- did not include enough fees in transaction: fees=%d, tx=%s", nValueOut - nValueIn, ToString()); + return false; + } + + return true; +} + +CAmount CTxLockRequest::GetMinFee() const +{ + CAmount nMinFee = MIN_FEE; + return std::max(nMinFee, CAmount(vin.size() * nMinFee)); +} + +int CTxLockRequest::GetMaxSignatures() const +{ + return vin.size() * COutPointLock::SIGNATURES_TOTAL; +} + +bool CTxLockRequest::IsTimedOut() const +{ + return GetTime() - nTimeCreated > TIMEOUT_SECONDS; +} + +// +// CTxLockVote +// + +bool CTxLockVote::IsValid(CNode* pnode) const +{ + if(!mnodeman.Has(CTxIn(outpointZnode))) { + LogPrint("instantsend", "CTxLockVote::IsValid -- Unknown znode %s\n", outpointZnode.ToStringShort()); + mnodeman.AskForMN(pnode, CTxIn(outpointZnode)); + return false; + } + + int nPrevoutHeight = GetUTXOHeight(outpoint); + if(nPrevoutHeight == -1) { + LogPrint("instantsend", "CTxLockVote::IsValid -- Failed to find UTXO %s\n", outpoint.ToStringShort()); + // Validating utxo set is not enough, votes can arrive after outpoint was already spent, + // if lock request was mined. We should process them too to count them later if they are legit. + CTransaction txOutpointCreated; + uint256 nHashOutpointConfirmed; + if(!GetTransaction(outpoint.hash, txOutpointCreated, Params().GetConsensus(), nHashOutpointConfirmed, true) || nHashOutpointConfirmed == uint256()) { + LogPrint("instantsend", "CTxLockVote::IsValid -- Failed to find outpoint %s\n", outpoint.ToStringShort()); + return false; + } + LOCK(cs_main); + BlockMap::iterator mi = mapBlockIndex.find(nHashOutpointConfirmed); + if(mi == mapBlockIndex.end() || !mi->second) { + // not on this chain? + LogPrint("instantsend", "CTxLockVote::IsValid -- Failed to find block %s for outpoint %s\n", nHashOutpointConfirmed.ToString(), outpoint.ToStringShort()); + return false; + } + nPrevoutHeight = mi->second->nHeight; + } + + int nLockInputHeight = nPrevoutHeight + 4; + + int n = mnodeman.GetZnodeRank(CTxIn(outpointZnode), nLockInputHeight, MIN_INSTANTSEND_PROTO_VERSION); + + if(n == -1) { + //can be caused by past versions trying to vote with an invalid protocol + LogPrint("instantsend", "CTxLockVote::IsValid -- Outdated znode %s\n", outpointZnode.ToStringShort()); + return false; + } + LogPrint("instantsend", "CTxLockVote::IsValid -- Znode %s, rank=%d\n", outpointZnode.ToStringShort(), n); + + int nSignaturesTotal = COutPointLock::SIGNATURES_TOTAL; + if(n > nSignaturesTotal) { + LogPrint("instantsend", "CTxLockVote::IsValid -- Znode %s is not in the top %d (%d), vote hash=%s\n", + outpointZnode.ToStringShort(), nSignaturesTotal, n, GetHash().ToString()); + return false; + } + + if(!CheckSignature()) { + LogPrintf("CTxLockVote::IsValid -- Signature invalid\n"); + return false; + } + + return true; +} + +uint256 CTxLockVote::GetHash() const +{ + CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); + ss << txHash; + ss << outpoint; + ss << outpointZnode; + return ss.GetHash(); +} + +bool CTxLockVote::CheckSignature() const +{ + std::string strError; + std::string strMessage = txHash.ToString() + outpoint.ToStringShort(); + + znode_info_t infoMn = mnodeman.GetZnodeInfo(CTxIn(outpointZnode)); + + if(!infoMn.fInfoValid) { + LogPrintf("CTxLockVote::CheckSignature -- Unknown Znode: znode=%s\n", outpointZnode.ToString()); + return false; + } + + if(!darkSendSigner.VerifyMessage(infoMn.pubKeyZnode, vchZnodeSignature, strMessage, strError)) { + LogPrintf("CTxLockVote::CheckSignature -- VerifyMessage() failed, error: %s\n", strError); + return false; + } + + return true; +} + +bool CTxLockVote::Sign() +{ + std::string strError; + std::string strMessage = txHash.ToString() + outpoint.ToStringShort(); + + if(!darkSendSigner.SignMessage(strMessage, vchZnodeSignature, activeZnode.keyZnode)) { + LogPrintf("CTxLockVote::Sign -- SignMessage() failed\n"); + return false; + } + + if(!darkSendSigner.VerifyMessage(activeZnode.pubKeyZnode, vchZnodeSignature, strMessage, strError)) { + LogPrintf("CTxLockVote::Sign -- VerifyMessage() failed, error: %s\n", strError); + return false; + } + + return true; +} + +void CTxLockVote::Relay() const +{ +// CInv inv(MSG_TXLOCK_VOTE, GetHash()); +// RelayInv(inv); +} + +bool CTxLockVote::IsExpired(int nHeight) const +{ + // Locks and votes expire nInstantSendKeepLock blocks after the block corresponding tx was included into. + return (nConfirmedHeight != -1) && (nHeight - nConfirmedHeight > Params().GetConsensus().nInstantSendKeepLock); +} + +// +// COutPointLock +// + +bool COutPointLock::AddVote(const CTxLockVote& vote) +{ + if(mapZnodeVotes.count(vote.GetZnodeOutpoint())) + return false; + mapZnodeVotes.insert(std::make_pair(vote.GetZnodeOutpoint(), vote)); + return true; +} + +std::vector COutPointLock::GetVotes() const +{ + std::vector vRet; + std::map::const_iterator itVote = mapZnodeVotes.begin(); + while(itVote != mapZnodeVotes.end()) { + vRet.push_back(itVote->second); + ++itVote; + } + return vRet; +} + +bool COutPointLock::HasZnodeVoted(const COutPoint& outpointZnodeIn) const +{ + return mapZnodeVotes.count(outpointZnodeIn); +} + +void COutPointLock::Relay() const +{ + std::map::const_iterator itVote = mapZnodeVotes.begin(); + while(itVote != mapZnodeVotes.end()) { + itVote->second.Relay(); + ++itVote; + } +} + +// +// CTxLockCandidate +// + +void CTxLockCandidate::AddOutPointLock(const COutPoint& outpoint) +{ + mapOutPointLocks.insert(make_pair(outpoint, COutPointLock(outpoint))); +} + + +bool CTxLockCandidate::AddVote(const CTxLockVote& vote) +{ + std::map::iterator it = mapOutPointLocks.find(vote.GetOutpoint()); + if(it == mapOutPointLocks.end()) return false; + return it->second.AddVote(vote); +} + +bool CTxLockCandidate::IsAllOutPointsReady() const +{ + if(mapOutPointLocks.empty()) return false; + + std::map::const_iterator it = mapOutPointLocks.begin(); + while(it != mapOutPointLocks.end()) { + if(!it->second.IsReady()) return false; + ++it; + } + return true; +} + +bool CTxLockCandidate::HasZnodeVoted(const COutPoint& outpointIn, const COutPoint& outpointZnodeIn) +{ + std::map::iterator it = mapOutPointLocks.find(outpointIn); + return it !=mapOutPointLocks.end() && it->second.HasZnodeVoted(outpointZnodeIn); +} + +int CTxLockCandidate::CountVotes() const +{ + // Note: do NOT use vote count to figure out if tx is locked, use IsAllOutPointsReady() instead + int nCountVotes = 0; + std::map::const_iterator it = mapOutPointLocks.begin(); + while(it != mapOutPointLocks.end()) { + nCountVotes += it->second.CountVotes(); + ++it; + } + return nCountVotes; +} + +bool CTxLockCandidate::IsExpired(int nHeight) const +{ + // Locks and votes expire nInstantSendKeepLock blocks after the block corresponding tx was included into. + return (nConfirmedHeight != -1) && (nHeight - nConfirmedHeight > Params().GetConsensus().nInstantSendKeepLock); +} + +void CTxLockCandidate::Relay() const +{ + RelayTransaction(txLockRequest); + std::map::const_iterator itOutpointLock = mapOutPointLocks.begin(); + while(itOutpointLock != mapOutPointLocks.end()) { + itOutpointLock->second.Relay(); + ++itOutpointLock; + } +} diff --git a/src/instantx.h b/src/instantx.h new file mode 100644 index 0000000000..886c65b4fc --- /dev/null +++ b/src/instantx.h @@ -0,0 +1,249 @@ +// Copyright (c) 2014-2017 The Dash Core 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 "net.h" +#include "primitives/transaction.h" + +class CTxLockVote; +class COutPointLock; +class CTxLockRequest; +class CTxLockCandidate; +class CInstantSend; + +extern CInstantSend instantsend; + +/* + At 15 signatures, 1/2 of the znode network can be owned by + one party without comprimising the security of InstantSend + (1000/2150.0)**10 = 0.00047382219560689856 + (1000/2900.0)**10 = 2.3769498616783657e-05 + + ### getting 5 of 10 signatures w/ 1000 nodes of 2900 + (1000/2900.0)**5 = 0.004875397277841433 +*/ +static const int INSTANTSEND_CONFIRMATIONS_REQUIRED = 6; +static const int DEFAULT_INSTANTSEND_DEPTH = 5; + +static const int MIN_INSTANTSEND_PROTO_VERSION = 70206; + +extern bool fEnableInstantSend; +extern int nInstantSendDepth; +extern int nCompleteTXLocks; + +class CInstantSend +{ +private: + static const int ORPHAN_VOTE_SECONDS = 60; + + // Keep track of current block index + const CBlockIndex *pCurrentBlockIndex; + + // maps for AlreadyHave + std::map mapLockRequestAccepted; // tx hash - tx + std::map mapLockRequestRejected; // tx hash - tx + std::map mapTxLockVotes; // vote hash - vote + std::map mapTxLockVotesOrphan; // vote hash - vote + + std::map mapTxLockCandidates; // tx hash - lock candidate + + std::map > mapVotedOutpoints; // utxo - tx hash set + std::map mapLockedOutpoints; // utxo - tx hash + + //track znodes who voted with no txreq (for DOS protection) + std::map mapZnodeOrphanVotes; // mn outpoint - time + + bool CreateTxLockCandidate(const CTxLockRequest& txLockRequest); + void Vote(CTxLockCandidate& txLockCandidate); + + //process consensus vote message + bool ProcessTxLockVote(CNode* pfrom, CTxLockVote& vote); + void ProcessOrphanTxLockVotes(); + bool IsEnoughOrphanVotesForTx(const CTxLockRequest& txLockRequest); + bool IsEnoughOrphanVotesForTxAndOutPoint(const uint256& txHash, const COutPoint& outpoint); + int64_t GetAverageZnodeOrphanVoteTime(); + + void TryToFinalizeLockCandidate(const CTxLockCandidate& txLockCandidate); + void LockTransactionInputs(const CTxLockCandidate& txLockCandidate); + //update UI and notify external script if any + void UpdateLockedTransaction(const CTxLockCandidate& txLockCandidate); + bool ResolveConflicts(const CTxLockCandidate& txLockCandidate, int nMaxBlocks); + + bool IsInstantSendReadyToLock(const uint256 &txHash); + +public: + CCriticalSection cs_instantsend; + + void ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); + + bool ProcessTxLockRequest(const CTxLockRequest& txLockRequest); + + bool AlreadyHave(const uint256& hash); + + void AcceptLockRequest(const CTxLockRequest& txLockRequest); + void RejectLockRequest(const CTxLockRequest& txLockRequest); + bool HasTxLockRequest(const uint256& txHash); + bool GetTxLockRequest(const uint256& txHash, CTxLockRequest& txLockRequestRet); + + bool GetTxLockVote(const uint256& hash, CTxLockVote& txLockVoteRet); + + bool GetLockedOutPointTxHash(const COutPoint& outpoint, uint256& hashRet); + + // verify if transaction is currently locked + bool IsLockedInstantSendTransaction(const uint256& txHash); + // get the actual uber og accepted lock signatures + int GetTransactionLockSignatures(const uint256& txHash); + + // remove expired entries from maps + void CheckAndRemove(); + // verify if transaction lock timed out + bool IsTxLockRequestTimedOut(const uint256& txHash); + + void Relay(const uint256& txHash); + + void UpdatedBlockTip(const CBlockIndex *pindex); + void SyncTransaction(const CTransaction& tx, const CBlock* pblock); +}; + +class CTxLockRequest : public CTransaction +{ +private: + static const int TIMEOUT_SECONDS = 5 * 60; + static const CAmount MIN_FEE = 0.001 * COIN; + + int64_t nTimeCreated; + +public: + static const int WARN_MANY_INPUTS = 100; + + CTxLockRequest() : + CTransaction(), + nTimeCreated(GetTime()) + {} + CTxLockRequest(const CTransaction& tx) : + CTransaction(tx), + nTimeCreated(GetTime()) + {} + + bool IsValid(bool fRequireUnspent = true) const; + CAmount GetMinFee() const; + int GetMaxSignatures() const; + bool IsTimedOut() const; +}; + +class CTxLockVote +{ +private: + uint256 txHash; + COutPoint outpoint; + COutPoint outpointZnode; + std::vector vchZnodeSignature; + // local memory only + int nConfirmedHeight; // when corresponding tx is 0-confirmed or conflicted, nConfirmedHeight is -1 + int64_t nTimeCreated; + +public: + CTxLockVote() : + txHash(), + outpoint(), + outpointZnode(), + vchZnodeSignature(), + nConfirmedHeight(-1), + nTimeCreated(GetTime()) + {} + + CTxLockVote(const uint256& txHashIn, const COutPoint& outpointIn, const COutPoint& outpointZnodeIn) : + txHash(txHashIn), + outpoint(outpointIn), + outpointZnode(outpointZnodeIn), + vchZnodeSignature(), + nConfirmedHeight(-1), + nTimeCreated(GetTime()) + {} + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(txHash); + READWRITE(outpoint); + READWRITE(outpointZnode); + READWRITE(vchZnodeSignature); + } + + uint256 GetHash() const; + + uint256 GetTxHash() const { return txHash; } + COutPoint GetOutpoint() const { return outpoint; } + COutPoint GetZnodeOutpoint() const { return outpointZnode; } + int64_t GetTimeCreated() const { return nTimeCreated; } + + bool IsValid(CNode* pnode) const; + void SetConfirmedHeight(int nConfirmedHeightIn) { nConfirmedHeight = nConfirmedHeightIn; } + bool IsExpired(int nHeight) const; + + bool Sign(); + bool CheckSignature() const; + + void Relay() const; +}; + +class COutPointLock +{ +private: + COutPoint outpoint; // utxo + std::map mapZnodeVotes; // znode outpoint - vote + +public: + static const int SIGNATURES_REQUIRED = 6; + static const int SIGNATURES_TOTAL = 10; + + COutPointLock(const COutPoint& outpointIn) : + outpoint(outpointIn), + mapZnodeVotes() + {} + + COutPoint GetOutpoint() const { return outpoint; } + + bool AddVote(const CTxLockVote& vote); + std::vector GetVotes() const; + bool HasZnodeVoted(const COutPoint& outpointZnodeIn) const; + int CountVotes() const { return mapZnodeVotes.size(); } + bool IsReady() const { return CountVotes() >= SIGNATURES_REQUIRED; } + + void Relay() const; +}; + +class CTxLockCandidate +{ +private: + int nConfirmedHeight; // when corresponding tx is 0-confirmed or conflicted, nConfirmedHeight is -1 + +public: + CTxLockCandidate(const CTxLockRequest& txLockRequestIn) : + nConfirmedHeight(-1), + txLockRequest(txLockRequestIn), + mapOutPointLocks() + {} + + CTxLockRequest txLockRequest; + std::map mapOutPointLocks; + + uint256 GetHash() const { return txLockRequest.GetHash(); } + + void AddOutPointLock(const COutPoint& outpoint); + bool AddVote(const CTxLockVote& vote); + bool IsAllOutPointsReady() const; + + bool HasZnodeVoted(const COutPoint& outpointIn, const COutPoint& outpointZnodeIn); + int CountVotes() const; + + void SetConfirmedHeight(int nConfirmedHeightIn) { nConfirmedHeight = nConfirmedHeightIn; } + bool IsExpired(int nHeight) const; + + void Relay() const; +}; + +#endif diff --git a/src/libzerocoin/ParamGeneration.cpp b/src/libzerocoin/ParamGeneration.cpp index eac0551018..f71c61a738 100755 --- a/src/libzerocoin/ParamGeneration.cpp +++ b/src/libzerocoin/ParamGeneration.cpp @@ -249,8 +249,8 @@ namespace libzerocoin { // Calculate "p" and "q" and "domain_parameter_seed" from the // "seed" buffer above, using the procedure described in NIST // FIPS 186-3, Appendix A.1.2. - calculateGroupModulusAndOrder(seed, pLen, qLen, result.modulus, - result.groupOrder, &pSeed, &qSeed); + calculateGroupModulusAndOrder(seed, pLen, qLen, &(result.modulus), + &(result.groupOrder), &pSeed, &qSeed); // Calculate the generators "g", "h" using the process described in // NIST FIPS 186-3, Appendix A.2.3. This algorithm takes ("p", "q", @@ -348,7 +348,7 @@ namespace libzerocoin { /// primes "p" and "q". void calculateGroupModulusAndOrder(arith_uint256 seed, uint32_t pLen, uint32_t qLen, - Bignum &resultModulus, Bignum &resultGroupOrder, + Bignum *resultModulus, Bignum *resultGroupOrder, arith_uint256 *resultPseed, arith_uint256 *resultQseed) { // Verify that the seed length is >= qLen if (qLen > (sizeof(seed)) * 8) { @@ -365,7 +365,7 @@ namespace libzerocoin { // Result is the value "resultGroupOrder", "qseed" and "qgen_counter". arith_uint256 qseed; uint32_t qgen_counter; - resultGroupOrder = generateRandomPrime(qLen, seed, &qseed, &qgen_counter); + *resultGroupOrder = generateRandomPrime(qLen, seed, &qseed, &qgen_counter); // Using ⎡pLen / 2 + 1⎤ as the length and qseed as the input_seed, use the random prime // routine to obtain p0 , pseed, and pgen_counter. We pass exceptions upward. @@ -388,7 +388,7 @@ namespace libzerocoin { // t = ⎡x / (2 * resultGroupOrder * p0)⎤. // TODO: we don't have a ceiling function - Bignum t = x / (Bignum(2) * resultGroupOrder * p0); + Bignum t = x / (Bignum(2) * (*resultGroupOrder) * p0); // Now loop until we find a valid prime "p" or we fail due to // pgen_counter exceeding ((4*pLen) + old_counter). @@ -396,29 +396,29 @@ namespace libzerocoin { // If (2 * t * resultGroupOrder * p0 + 1) > 2^{pLen}, then // t = ⎡2^{pLen−1} / (2 * resultGroupOrder * p0)⎤. powerOfTwo = Bignum(2).pow(pLen); - Bignum prod = (Bignum(2) * t * resultGroupOrder * p0) + Bignum(1); + Bignum prod = (Bignum(2) * t * (*resultGroupOrder) * p0) + Bignum(1); if (prod > powerOfTwo) { // TODO: implement a ceil function - t = Bignum(2).pow(pLen - 1) / (Bignum(2) * resultGroupOrder * p0); + t = Bignum(2).pow(pLen - 1) / (Bignum(2) * (*resultGroupOrder) * p0); } // Compute a candidate prime resultModulus = 2tqp0 + 1. - resultModulus = (Bignum(2) * t * resultGroupOrder * p0) + Bignum(1); + *resultModulus = (Bignum(2) * t * (*resultGroupOrder) * p0) + Bignum(1); // Verify that resultModulus is prime. First generate a pseudorandom integer "a". Bignum a = generateIntegerFromSeed(pLen, pseed, &iterations); pseed += iterations + 1; // Set a = 2 + (a mod (resultModulus–3)). - a = Bignum(2) + (a % (resultModulus - Bignum(3))); + a = Bignum(2) + (a % ((*resultModulus) - Bignum(3))); // Set z = a^{2 * t * resultGroupOrder} mod resultModulus - Bignum z = a.pow_mod(Bignum(2) * t * resultGroupOrder, resultModulus); + Bignum z = a.pow_mod(Bignum(2) * t * (*resultGroupOrder), (*resultModulus)); // If GCD(z–1, resultModulus) == 1 AND (z^{p0} mod resultModulus == 1) // then we have found our result. Return. - if ((resultModulus.gcd(z - Bignum(1))).isOne() && - (z.pow_mod(p0, resultModulus)).isOne()) { + if ((resultModulus->gcd(z - Bignum(1))).isOne() && + (z.pow_mod(p0, (*resultModulus))).isOne()) { // Success! Return the seeds and primes. *resultPseed = pseed; *resultQseed = qseed; diff --git a/src/libzerocoin/ParamGeneration.h b/src/libzerocoin/ParamGeneration.h index d72b5a2a9b..b64bac0c13 100755 --- a/src/libzerocoin/ParamGeneration.h +++ b/src/libzerocoin/ParamGeneration.h @@ -37,7 +37,7 @@ arith_uint256 calculateGeneratorSeed(arith_uint256 seed, arith_uint256 pSeed, ar arith_uint256 calculateHash(arith_uint256 input); IntegerGroupParams deriveIntegerGroupParams(arith_uint256 seed, uint32_t pLen, uint32_t qLen); IntegerGroupParams deriveIntegerGroupFromOrder(Bignum &groupOrder); -void calculateGroupModulusAndOrder(arith_uint256 seed, uint32_t pLen, uint32_t qLen, Bignum &resultModulus, Bignum &resultGroupOrder, arith_uint256 *resultPseed, arith_uint256 *resultQseed); +void calculateGroupModulusAndOrder(arith_uint256 seed, uint32_t pLen, uint32_t qLen, Bignum *resultModulus, Bignum *resultGroupOrder, arith_uint256 *resultPseed, arith_uint256 *resultQseed); Bignum calculateGroupGenerator(arith_uint256 seed, arith_uint256 pSeed, arith_uint256 qSeed, Bignum modulus, Bignum groupOrder, uint32_t index); Bignum generateRandomPrime(uint32_t primeBitLen, arith_uint256 in_seed, arith_uint256 *out_seed, uint32_t *prime_gen_counter); Bignum generateIntegerFromSeed(uint32_t numBits, arith_uint256 seed, uint32_t *numIterations); diff --git a/src/libzerocoin/bitcoin_bignum/bignum.h b/src/libzerocoin/bitcoin_bignum/bignum.h index 94221085b9..2154681cf6 100755 --- a/src/libzerocoin/bitcoin_bignum/bignum.h +++ b/src/libzerocoin/bitcoin_bignum/bignum.h @@ -20,6 +20,7 @@ class bignum_error : public std::runtime_error explicit bignum_error(const std::string& str) : std::runtime_error(str) {} }; + /** RAII encapsulated BN_CTX (OpenSSL bignum context) */ class CAutoBN_CTX { @@ -49,66 +50,53 @@ class CAutoBN_CTX /** C++ wrapper for BIGNUM (OpenSSL bignum) */ -class CBigNum +class CBigNum : public BIGNUM { -protected: - BIGNUM *bn; - - void init() - { - bn = BN_new(); - } - public: CBigNum() { - init(); + BN_init(this); } CBigNum(const CBigNum& b) { - init(); - if (!BN_copy(bn, &b)) + BN_init(this); + if (!BN_copy(this, &b)) { - BN_clear_free(bn); + BN_clear_free(this); throw bignum_error("CBigNum::CBigNum(const CBigNum&) : BN_copy failed"); } } CBigNum& operator=(const CBigNum& b) { - if (!BN_copy(bn, &b)) + if (!BN_copy(this, &b)) throw bignum_error("CBigNum::operator= : BN_copy failed"); return (*this); } ~CBigNum() { - BN_clear_free(bn); - } - - BIGNUM *operator &() const - { - return bn; + BN_clear_free(this); } //CBigNum(char n) is not portable. Use 'signed char' or 'unsigned char'. - CBigNum(signed char n) { init(); if (n >= 0) setulong(n); else setint64(n); } - CBigNum(short n) { init(); if (n >= 0) setulong(n); else setint64(n); } - CBigNum(int n) { init(); if (n >= 0) setulong(n); else setint64(n); } -// CBigNum(long n) { init(); if (n >= 0) setulong(n); else setint64(n); } - CBigNum(int64_t n) { init(); setint64(n); } - CBigNum(unsigned char n) { init(); setulong(n); } - CBigNum(unsigned short n) { init(); setulong(n); } - CBigNum(unsigned int n) { init(); setulong(n); } -// CBigNum(unsigned long n) { init(); setulong(n); } - CBigNum(uint64_t n) { init(); setuint64(n); } - explicit CBigNum(arith_uint256 n) { init(); setuint256(n); } - explicit CBigNum(uint256 n) { arith_uint256 m = UintToArith256(n); init(); setuint256(m); } + CBigNum(signed char n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(short n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(int n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } +// CBigNum(long n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(int64_t n) { BN_init(this); setint64(n); } + CBigNum(unsigned char n) { BN_init(this); setulong(n); } + CBigNum(unsigned short n) { BN_init(this); setulong(n); } + CBigNum(unsigned int n) { BN_init(this); setulong(n); } +// CBigNum(unsigned long n) { BN_init(this); setulong(n); } + CBigNum(uint64_t n) { BN_init(this); setuint64(n); } + explicit CBigNum(arith_uint256 n) { BN_init(this); setuint256(n); } + explicit CBigNum(uint256 n) { arith_uint256 m = UintToArith256(n); BN_init(this); setuint256(m); } explicit CBigNum(const std::vector& vch) { - init(); + BN_init(this); setvch(vch); } @@ -142,29 +130,29 @@ class CBigNum * @return the size */ int bitSize() const{ - return BN_num_bits(bn); + return BN_num_bits(this); } void setulong(unsigned long n) { - if (!BN_set_word(bn, n)) + if (!BN_set_word(this, n)) throw bignum_error("CBigNum conversion from unsigned long : BN_set_word failed"); } unsigned long getulong() const { - return BN_get_word(bn); + return BN_get_word(this); } unsigned int getuint() const { - return BN_get_word(bn); + return BN_get_word(this); } int getint() const { - unsigned long n = BN_get_word(bn); - if (!BN_is_negative(bn)) + unsigned long n = BN_get_word(this); + if (!BN_is_negative(this)) return (n > (unsigned long)std::numeric_limits::max() ? std::numeric_limits::max() : n); else return (n > (unsigned long)std::numeric_limits::max() ? std::numeric_limits::min() : -(int)n); @@ -212,7 +200,7 @@ class CBigNum pch[1] = (nSize >> 16) & 0xff; pch[2] = (nSize >> 8) & 0xff; pch[3] = (nSize) & 0xff; - BN_mpi2bn(pch, p - pch, bn); + BN_mpi2bn(pch, p - pch, this); } void setuint64(uint64_t n) @@ -239,7 +227,7 @@ class CBigNum pch[1] = (nSize >> 16) & 0xff; pch[2] = (nSize >> 8) & 0xff; pch[3] = (nSize) & 0xff; - BN_mpi2bn(pch, p - pch, bn); + BN_mpi2bn(pch, p - pch, this); } void setuint256(arith_uint256 n) @@ -267,18 +255,18 @@ class CBigNum pch[1] = (nSize >> 16) & 0xff; pch[2] = (nSize >> 8) & 0xff; pch[3] = (nSize >> 0) & 0xff; - BN_mpi2bn(pch, p - pch, bn); + BN_mpi2bn(pch, p - pch, this); } // uint256 getuint256() const // { // uint64_t x = 0; // uint256 n = x; -// unsigned int nSize = BN_bn2mpi(bn, NULL); +// unsigned int nSize = BN_bn2mpi(this, NULL); // if (nSize < 4) // return n; // std::vector vch(nSize); -// BN_bn2mpi(bn, &vch[0]); +// BN_bn2mpi(this, &vch[0]); // if (vch.size() > 4) // vch[4] &= 0x7f; // for (unsigned int i = 0, j = vch.size()-1; i < sizeof(n) && j >= 4; i++, j--) @@ -298,16 +286,16 @@ class CBigNum vch2[3] = (nSize >> 0) & 0xff; // swap data to big endian reverse_copy(vch.begin(), vch.end(), vch2.begin() + 4); - BN_mpi2bn(&vch2[0], vch2.size(), bn); + BN_mpi2bn(&vch2[0], vch2.size(), this); } std::vector getvch() const { - unsigned int nSize = BN_bn2mpi(bn, NULL); + unsigned int nSize = BN_bn2mpi(this, NULL); if (nSize <= 4) return std::vector(); std::vector vch(nSize); - BN_bn2mpi(bn, &vch[0]); + BN_bn2mpi(this, &vch[0]); vch.erase(vch.begin(), vch.begin() + 4); reverse(vch.begin(), vch.end()); return vch; @@ -343,28 +331,28 @@ class CBigNum if (nSize <= 3) { nWord >>= 8*(3-nSize); - BN_set_word(bn, nWord); + BN_set_word(this, nWord); } else { - BN_set_word(bn, nWord); - BN_lshift(bn, bn, 8*(nSize-3)); + BN_set_word(this, nWord); + BN_lshift(this, this, 8*(nSize-3)); } - BN_set_negative(bn, fNegative); + BN_set_negative(this, fNegative); return *this; } unsigned int GetCompact() const { - unsigned int nSize = BN_num_bytes(bn); + unsigned int nSize = BN_num_bytes(this); unsigned int nCompact = 0; if (nSize <= 3) - nCompact = BN_get_word(bn) << 8*(3-nSize); + nCompact = BN_get_word(this) << 8*(3-nSize); else { - CBigNum bn1; - BN_rshift(&bn1, bn, 8*(nSize-3)); - nCompact = BN_get_word(&bn1); + CBigNum bn; + BN_rshift(&bn, this, 8*(nSize-3)); + nCompact = BN_get_word(&bn); } // The 0x00800000 bit denotes the sign. // Thus, if it is already set, divide the mantissa by 256 and increase the exponent. @@ -374,7 +362,7 @@ class CBigNum nSize++; } nCompact |= nSize << 24; - nCompact |= (BN_is_negative(bn) ? 0x00800000 : 0); + nCompact |= (BN_is_negative(this) ? 0x00800000 : 0); return nCompact; } @@ -447,21 +435,21 @@ class CBigNum CBigNum bnBase = nBase; CBigNum bn0 = 0; std::string str; - CBigNum bn1 = *this; - BN_set_negative(&bn1, false); + CBigNum bn = *this; + BN_set_negative(&bn, false); CBigNum dv; CBigNum rem; - if (BN_cmp(&bn1, &bn0) == 0) + if (BN_cmp(&bn, &bn0) == 0) return "0"; - while (BN_cmp(&bn1, &bn0) > 0) + while (BN_cmp(&bn, &bn0) > 0) { - if (!BN_div(&dv, &rem, &bn1, &bnBase, pctx)) + if (!BN_div(&dv, &rem, &bn, &bnBase, pctx)) throw bignum_error("CBigNum::ToString() : BN_div failed"); - bn1 = dv; + bn = dv; unsigned int c = rem.getulong(); str += "0123456789abcdef"[c]; } - if (BN_is_negative(bn)) + if (BN_is_negative(this)) str += "-"; reverse(str.begin(), str.end()); return str; @@ -508,7 +496,7 @@ class CBigNum CBigNum pow(const CBigNum& e) const { CAutoBN_CTX pctx; CBigNum ret; - if (!BN_exp(&ret, bn, &e, pctx)) + if (!BN_exp(&ret, this, &e, pctx)) throw bignum_error("CBigNum::pow : BN_exp failed"); return ret; } @@ -521,7 +509,7 @@ class CBigNum CBigNum mul_mod(const CBigNum& b, const CBigNum& m) const { CAutoBN_CTX pctx; CBigNum ret; - if (!BN_mod_mul(&ret, bn, &b, &m, pctx)) + if (!BN_mod_mul(&ret, this, &b, &m, pctx)) throw bignum_error("CBigNum::mul_mod : BN_mod_mul failed"); return ret; @@ -542,7 +530,7 @@ class CBigNum if (!BN_mod_exp(&ret, &inv, &posE, &m, pctx)) throw bignum_error("CBigNum::pow_mod: BN_mod_exp failed on negative exponent"); }else - if (!BN_mod_exp(&ret, bn, &e, &m, pctx)) + if (!BN_mod_exp(&ret, this, &e, &m, pctx)) throw bignum_error("CBigNum::pow_mod : BN_mod_exp failed"); return ret; @@ -557,7 +545,7 @@ class CBigNum CBigNum inverse(const CBigNum& m) const { CAutoBN_CTX pctx; CBigNum ret; - if (!BN_mod_inverse(&ret, bn, &m, pctx)) + if (!BN_mod_inverse(&ret, this, &m, pctx)) throw bignum_error("CBigNum::inverse*= :BN_mod_inverse"); return ret; } @@ -583,7 +571,7 @@ class CBigNum CBigNum gcd( const CBigNum& b) const{ CAutoBN_CTX pctx; CBigNum ret; - if (!BN_gcd(&ret, bn, &b, pctx)) + if (!BN_gcd(&ret, this, &b, pctx)) throw bignum_error("CBigNum::gcd*= :BN_gcd"); return ret; } @@ -596,7 +584,7 @@ class CBigNum */ bool isPrime(const int checks=BN_prime_checks) const { CAutoBN_CTX pctx; - int ret = BN_is_prime_ex(bn, checks, pctx, NULL); + int ret = BN_is_prime(this, checks, NULL, pctx, NULL); if(ret < 0){ throw bignum_error("CBigNum::isPrime :BN_is_prime"); } @@ -604,19 +592,19 @@ class CBigNum } bool isOne() const { - return BN_is_one(bn); + return BN_is_one(this); } bool operator!() const { - return BN_is_zero(bn); + return BN_is_zero(this); } CBigNum& operator+=(const CBigNum& b) { - if (!BN_add(bn, bn, &b)) + if (!BN_add(this, this, &b)) throw bignum_error("CBigNum::operator+= : BN_add failed"); return *this; } @@ -630,7 +618,7 @@ class CBigNum CBigNum& operator*=(const CBigNum& b) { CAutoBN_CTX pctx; - if (!BN_mul(bn, bn, &b, pctx)) + if (!BN_mul(this, this, &b, pctx)) throw bignum_error("CBigNum::operator*= : BN_mul failed"); return *this; } @@ -649,7 +637,7 @@ class CBigNum CBigNum& operator<<=(unsigned int shift) { - if (!BN_lshift(bn, bn, shift)) + if (!BN_lshift(this, this, shift)) throw bignum_error("CBigNum:operator<<= : BN_lshift failed"); return *this; } @@ -660,13 +648,13 @@ class CBigNum // if built on ubuntu 9.04 or 9.10, probably depends on version of OpenSSL CBigNum a = 1; a <<= shift; - if (BN_cmp(&a, bn) > 0) + if (BN_cmp(&a, this) > 0) { *this = 0; return *this; } - if (!BN_rshift(bn, bn, shift)) + if (!BN_rshift(this, this, shift)) throw bignum_error("CBigNum:operator>>= : BN_rshift failed"); return *this; } @@ -675,7 +663,7 @@ class CBigNum CBigNum& operator++() { // prefix operator - if (!BN_add(bn, bn, BN_value_one())) + if (!BN_add(this, this, BN_value_one())) throw bignum_error("CBigNum::operator++ : BN_add failed"); return *this; } @@ -692,7 +680,7 @@ class CBigNum { // prefix operator CBigNum r; - if (!BN_sub(&r, bn, BN_value_one())) + if (!BN_sub(&r, this, BN_value_one())) throw bignum_error("CBigNum::operator-- : BN_sub failed"); *this = r; return *this; diff --git a/src/main.cpp b/src/main.cpp index f34db130d4..01a5c29366 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -677,14 +677,23 @@ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { stats.nMisbehavior = state->nMisbehavior; stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1; stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1; - BOOST_FOREACH( - const QueuedBlock &queue, state->vBlocksInFlight) { + BOOST_FOREACH(const QueuedBlock &queue, state->vBlocksInFlight) { if (queue.pindex) stats.vHeightInFlight.push_back(queue.pindex->nHeight); } return true; } +bool GetBlockHash(uint256& hashRet, int nBlockHeight) +{ + LOCK(cs_main); + if(chainActive.Tip() == NULL) return false; + if(nBlockHeight < -1 || nBlockHeight > chainActive.Height()) return false; + if(nBlockHeight == -1) nBlockHeight = chainActive.Height(); + hashRet = chainActive[nBlockHeight]->GetBlockHash(); + return true; +} + void RegisterNodeSignals(CNodeSignals &nodeSignals) { nodeSignals.GetHeight.connect(&GetHeight); nodeSignals.ProcessMessages.connect(&ProcessMessages); @@ -1315,7 +1324,7 @@ bool CheckTransaction(const CTransaction &tx, CValidationState &state, uint256 h return state.DoS(100, false, REJECT_INVALID, "bad-cb-length"); //BTZC: add ZCOIN code // Check for founders inputs - if ((nHeight > 0) && (nHeight < 210000)) { + if ((nHeight > 0) && (nHeight < 210000) && (nHeight != 57157)) { bool found_1 = false; bool found_2 = false; bool found_3 = false; @@ -3417,6 +3426,60 @@ bool static ConnectTip(CValidationState &state, const CChainParams &chainparams, return true; } + +int GetUTXOHeight(const COutPoint& outpoint) +{ + LOCK(cs_main); + CCoins coins; + if(!pcoinsTip->GetCoins(outpoint.hash, coins) || + (unsigned int)outpoint.n>=coins.vout.size() || + coins.vout[outpoint.n].IsNull()) { + return -1; + } + return coins.nHeight; +} + +int GetInputAge(const CTxIn &txin) +{ + CCoinsView viewDummy; + CCoinsViewCache view(&viewDummy); + { + LOCK(mempool.cs); + CCoinsViewMemPool viewMempool(pcoinsTip, mempool); + view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view + + const CCoins* coins = view.AccessCoins(txin.prevout.hash); + + if (coins) { + if(coins->nHeight < 0) return 0; + return chainActive.Height() - coins->nHeight + 1; + } else { + return -1; + } + } +} + +CAmount GetZnodePayment(int nHeight, CAmount blockValue) +{ + CAmount ret = blockValue/5; // start at 20% + + int nMNPIBlock = Params().GetConsensus().nZnodePaymentsIncreaseBlock; + int nMNPIPeriod = Params().GetConsensus().nZnodePaymentsIncreasePeriod; + + // mainnet: + if(nHeight > nMNPIBlock) ret += blockValue / 20; // 158000 - 25.0% - 2014-10-24 + if(nHeight > nMNPIBlock+(nMNPIPeriod* 1)) ret += blockValue / 20; // 175280 - 30.0% - 2014-11-25 + if(nHeight > nMNPIBlock+(nMNPIPeriod* 2)) ret += blockValue / 20; // 192560 - 35.0% - 2014-12-26 + if(nHeight > nMNPIBlock+(nMNPIPeriod* 3)) ret += blockValue / 40; // 209840 - 37.5% - 2015-01-26 + if(nHeight > nMNPIBlock+(nMNPIPeriod* 4)) ret += blockValue / 40; // 227120 - 40.0% - 2015-02-27 + if(nHeight > nMNPIBlock+(nMNPIPeriod* 5)) ret += blockValue / 40; // 244400 - 42.5% - 2015-03-30 + if(nHeight > nMNPIBlock+(nMNPIPeriod* 6)) ret += blockValue / 40; // 261680 - 45.0% - 2015-05-01 + if(nHeight > nMNPIBlock+(nMNPIPeriod* 7)) ret += blockValue / 40; // 278960 - 47.5% - 2015-06-01 + if(nHeight > nMNPIBlock+(nMNPIPeriod* 9)) ret += blockValue / 40; // 313520 - 50.0% - 2015-08-03 + + return ret; +} + /** * Connect a new ZCblock to chainActive. pblock is either NULL or a pointer to a CBlock * corresponding to pindexNew, to bypass loading it again from disk. @@ -3480,6 +3543,51 @@ bool static ConnectTipZC(CValidationState &state, const CChainParams &chainparam return true; } +bool DisconnectBlocks(int blocks) +{ +// LOCK(cs_main); +// +// CValidationState state; +// const CChainParams& chainparams = Params(); +// +// LogPrintf("DisconnectBlocks -- Got command to replay %d blocks\n", blocks); +// for(int i = 0; i < blocks; i++) { +// if(!DisconnectTip(state, chainparams.GetConsensus()) || !state.IsValid()) { +// return false; +// } +// } +// +// return true; +} + +void ReprocessBlocks(int nBlocks) +{ +// LOCK(cs_main); +// +// std::map::iterator it = mapRejectedBlocks.begin(); +// while(it != mapRejectedBlocks.end()){ +// //use a window twice as large as is usual for the nBlocks we want to reset +// if((*it).second > GetTime() - (nBlocks*60*5)) { +// BlockMap::iterator mi = mapBlockIndex.find((*it).first); +// if (mi != mapBlockIndex.end() && (*mi).second) { +// +// CBlockIndex* pindex = (*mi).second; +// LogPrintf("ReprocessBlocks -- %s\n", (*it).first.ToString()); +// +// CValidationState state; +// ReconsiderBlock(state, pindex); +// } +// } +// ++it; +// } +// +// DisconnectBlocks(nBlocks); +// +// CValidationState state; +// ActivateBestChain(state, Params()); +} + + /** * Connect a new ZCblock to chainActive. pblock is either NULL or a pointer to a CBlock * corresponding to pindexNew, to bypass loading it again from disk. @@ -4465,7 +4573,7 @@ static bool AcceptBlock(const CBlock &block, CValidationState &state, const CCha if (fTooFarAhead) return true; // Block height is too high } if (fNewBlock) *fNewBlock = true; - if ((!CheckBlock(block, state, chainparams.GetConsensus(), GetAdjustedTime(), true, INT_MAX, false)) || + if ((!CheckBlock(block, state, chainparams.GetConsensus(), GetAdjustedTime(), true, pindex->nHeight, false)) || !ContextualCheckBlock(block, state, pindex->pprev)) { if (state.IsInvalid() && !state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; diff --git a/src/main.h b/src/main.h index f182704b4d..16b4923c30 100644 --- a/src/main.h +++ b/src/main.h @@ -423,6 +423,12 @@ bool SequenceLocks(const CTransaction &tx, int flags, std::vector* prevHeig */ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp = NULL, bool useExistingLockPoints = false); +/** + * Return true if hash can be found in chainActive at nBlockHeight height. + * Fills hashRet with found hash, if no nBlockHeight is specified - chainActive.Height() is used. + */ +bool GetBlockHash(uint256& hashRet, int nBlockHeight = -1); + /** * Closure representing one script verification * Note that this stores references to the spending transaction @@ -491,6 +497,16 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin * of problems. Note that in any case, coins may be modified. */ bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockIndex* pindex, CCoinsViewCache& coins, bool* pfClean = NULL); +/** Reprocess a number of blocks to try and get on the correct chain again **/ +bool DisconnectBlocks(int blocks); +void ReprocessBlocks(int nBlocks); + +int GetUTXOHeight(const COutPoint& outpoint); +int GetInputAge(const CTxIn &txin); +int GetInputAgeIX(const uint256 &nTXHash, const CTxIn &txin); +int GetIXConfirmations(const uint256 &nTXHash); +CAmount GetZnodePayment(int nHeight, CAmount blockValue); + /** Check a block is completely valid from start to finish (only works on top of our current best block, with cs_main held) */ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true); diff --git a/src/net.cpp b/src/net.cpp index 7f30330f66..504de361c9 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -458,6 +458,72 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure return NULL; } +CNode* ConnectNodeDash(CAddress addrConnect, const char *pszDest, bool fConnectToZnode) +{ + if (pszDest == NULL) { + // we clean znode connections in CZnodeMan::ProcessZnodeConnections() + // so should be safe to skip this and connect to local Hot MN on CActiveZnode::ManageState() + if (IsLocal(addrConnect) && !fConnectToZnode) + return NULL; + + LOCK(cs_vNodes); + // Look for an existing connection + CNode* pnode = FindNode((CService)addrConnect); + if (pnode) + { + // we have existing connection to this node but it was not a connection to znode, + // change flag and add reference so that we can correctly clear it later + if(fConnectToZnode && !pnode->fZnode) { + pnode->AddRef(); + pnode->fZnode = true; + } + return pnode; + } + } + + /// debug print + LogPrint("net", "trying connection %s lastseen=%.1fhrs\n", + pszDest ? pszDest : addrConnect.ToString(), + pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0); + + // Connect + SOCKET hSocket; + bool proxyConnectionFailed = false; + if (pszDest ? ConnectSocketByName(addrConnect, hSocket, pszDest, Params().GetDefaultPort(), nConnectTimeout, &proxyConnectionFailed) : + ConnectSocket(addrConnect, hSocket, nConnectTimeout, &proxyConnectionFailed)) + { + if (!IsSelectableSocket(hSocket)) { + LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n"); + CloseSocket(hSocket); + return NULL; + } + + addrman.Attempt(addrConnect, fConnectToZnode); + + // Add node +// CNode* pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false, true); + CNode* pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false); + + pnode->nTimeConnected = GetTime(); + if(fConnectToZnode) { + pnode->AddRef(); + pnode->fZnode = true; + } + + LOCK(cs_vNodes); + vNodes.push_back(pnode); + + return pnode; + } else if (!proxyConnectionFailed) { + // If connecting to the node failed, and failure is not caused by a problem connecting to + // the proxy, mark this as an attempt. + addrman.Attempt(addrConnect, fConnectToZnode); + } + + return NULL; +} + + static void DumpBanlist() { CNode::SweepBanned(); // clean unused entries (if bantime has expired) @@ -2204,6 +2270,15 @@ void RelayTransaction(const CTransaction& tx) } } +void RelayInv(CInv &inv, const int minProtoVersion) { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + if (pnode->nVersion >= minProtoVersion) + pnode->PushInventory(inv); + } +} + void CNode::RecordBytesRecv(uint64_t bytes) { LOCK(cs_totalBytesRecv); @@ -2511,6 +2586,8 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa minFeeFilter = 0; lastSentFeeFilter = 0; nextSendTimeFeeFilter = 0; + // znode + fZnode = false; BOOST_FOREACH(const std::string &msg, getAllNetMessageTypes()) mapRecvBytesPerMsgCmd[msg] = 0; @@ -2754,3 +2831,23 @@ int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds) { return CSipHasher(k0, k1).Write(&vchNetGroup[0], vchNetGroup.size()).Finalize(); } + +std::vector CopyNodeVector() +{ + std::vector vecNodesCopy; + LOCK(cs_vNodes); + for(size_t i = 0; i < vNodes.size(); ++i) { + CNode* pnode = vNodes[i]; + pnode->AddRef(); + vecNodesCopy.push_back(pnode); + } + return vecNodesCopy; +} + +void ReleaseNodeVector(const std::vector& vecNodes) +{ + for(size_t i = 0; i < vecNodes.size(); ++i) { + CNode* pnode = vecNodes[i]; + pnode->Release(); + } +} \ No newline at end of file diff --git a/src/net.h b/src/net.h index 63bb2c2ecf..9a190f0657 100644 --- a/src/net.h +++ b/src/net.h @@ -91,6 +91,9 @@ CNode* FindNode(const CSubNet& subNet); CNode* FindNode(const std::string& addrName); CNode* FindNode(const CService& ip); CNode* FindNode(const NodeId id); //TODO: Remove this + +CNode* ConnectNodeDash(CAddress addrConnect, const char *pszDest = NULL, bool fConnectToZnode = false); + bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false, bool fFeeler = false); void MapPort(bool fUseUPnP); unsigned short GetListenPort(); @@ -370,6 +373,8 @@ class CNode CBloomFilter* pfilter; int nRefCount; NodeId id; + // znode from dash + bool fZnode; const uint64_t nKeyedNetGroup; protected: @@ -447,6 +452,7 @@ class CNode int64_t nextSendTimeFeeFilter; CNode(SOCKET hSocketIn, const CAddress &addrIn, const std::string &addrNameIn = "", bool fInboundIn = false); +// CNode(SOCKET hSocketIn, const CAddress &addrIn, const std::string &addrNameIn = "", bool fInboundIn = false, bool fNetworkNodeIn = false); ~CNode(); private: @@ -818,6 +824,7 @@ class CNode class CTransaction; void RelayTransaction(const CTransaction& tx); +void RelayInv(CInv &inv, const int minProtoVersion = MIN_PEER_PROTO_VERSION); /** Access to the (IP) address database (peers.dat) */ class CAddrDB @@ -853,6 +860,9 @@ struct AddedNodeInfo bool fInbound; }; +std::vector CopyNodeVector(); +void ReleaseNodeVector(const std::vector& vecNodes); + std::vector GetAddedNodeInfo(); #endif // BITCOIN_NET_H diff --git a/src/netfulfilledman.cpp b/src/netfulfilledman.cpp new file mode 100644 index 0000000000..e512d2938b --- /dev/null +++ b/src/netfulfilledman.cpp @@ -0,0 +1,72 @@ +// Copyright (c) 2014-2017 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "chainparams.h" +#include "netfulfilledman.h" +#include "util.h" + +CNetFulfilledRequestManager netfulfilledman; + +void CNetFulfilledRequestManager::AddFulfilledRequest(CAddress addr, std::string strRequest) +{ + LOCK(cs_mapFulfilledRequests); + mapFulfilledRequests[addr][strRequest] = GetTime() + Params().FulfilledRequestExpireTime(); +} + +bool CNetFulfilledRequestManager::HasFulfilledRequest(CAddress addr, std::string strRequest) +{ + LOCK(cs_mapFulfilledRequests); + fulfilledreqmap_t::iterator it = mapFulfilledRequests.find(addr); + + return it != mapFulfilledRequests.end() && + it->second.find(strRequest) != it->second.end() && + it->second[strRequest] > GetTime(); +} + +void CNetFulfilledRequestManager::RemoveFulfilledRequest(CAddress addr, std::string strRequest) +{ + LOCK(cs_mapFulfilledRequests); + fulfilledreqmap_t::iterator it = mapFulfilledRequests.find(addr); + + if (it != mapFulfilledRequests.end()) { + it->second.erase(strRequest); + } +} + +void CNetFulfilledRequestManager::CheckAndRemove() +{ + LOCK(cs_mapFulfilledRequests); + + int64_t now = GetTime(); + fulfilledreqmap_t::iterator it = mapFulfilledRequests.begin(); + + while(it != mapFulfilledRequests.end()) { + fulfilledreqmapentry_t::iterator it_entry = it->second.begin(); + while(it_entry != it->second.end()) { + if(now > it_entry->second) { + it->second.erase(it_entry++); + } else { + ++it_entry; + } + } + if(it->second.size() == 0) { + mapFulfilledRequests.erase(it++); + } else { + ++it; + } + } +} + +void CNetFulfilledRequestManager::Clear() +{ + LOCK(cs_mapFulfilledRequests); + mapFulfilledRequests.clear(); +} + +std::string CNetFulfilledRequestManager::ToString() const +{ + std::ostringstream info; + info << "Nodes with fulfilled requests: " << (int)mapFulfilledRequests.size(); + return info.str(); +} diff --git a/src/netfulfilledman.h b/src/netfulfilledman.h new file mode 100644 index 0000000000..d447261964 --- /dev/null +++ b/src/netfulfilledman.h @@ -0,0 +1,49 @@ +// Copyright (c) 2014-2017 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef NETFULFILLEDMAN_H +#define NETFULFILLEDMAN_H + +#include "netbase.h" +#include "protocol.h" +#include "serialize.h" +#include "sync.h" + +// Fulfilled requests are used to prevent nodes from asking for the same data on sync +// and from being banned for doing so too often. +class CNetFulfilledRequestManager; +extern CNetFulfilledRequestManager netfulfilledman; + +class CNetFulfilledRequestManager +{ +private: + typedef std::map fulfilledreqmapentry_t; + typedef std::map fulfilledreqmap_t; + + //keep track of what node has/was asked for and when + fulfilledreqmap_t mapFulfilledRequests; + CCriticalSection cs_mapFulfilledRequests; + +public: + CNetFulfilledRequestManager() {} + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + LOCK(cs_mapFulfilledRequests); + READWRITE(mapFulfilledRequests); + } + + void AddFulfilledRequest(CAddress addr, std::string strRequest); // expire after 1 hour by default + bool HasFulfilledRequest(CAddress addr, std::string strRequest); + void RemoveFulfilledRequest(CAddress addr, std::string strRequest); + + void CheckAndRemove(); + void Clear(); + + std::string ToString() const; +}; + +#endif diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 93ec1227e8..6742f20a38 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -28,6 +28,11 @@ std::string COutPoint::ToString() const return strprintf("COutPoint(%s, %u)", hash.ToString(), n); } +std::string COutPoint::ToStringShort() const +{ + return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,64), n); +} + CTxIn::CTxIn(COutPoint prevoutIn, CScript scriptSigIn, uint32_t nSequenceIn) { prevout = prevoutIn; @@ -81,6 +86,22 @@ uint256 CMutableTransaction::GetHash() const return SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS); } +std::string CMutableTransaction::ToString() const +{ + std::string str; + str += strprintf("CMutableTransaction(hash=%s, ver=%d, vin.size=%u, vout.size=%u, nLockTime=%u)\n", + GetHash().ToString().substr(0,10), + nVersion, + vin.size(), + vout.size(), + nLockTime); + for (unsigned int i = 0; i < vin.size(); i++) + str += " " + vin[i].ToString() + "\n"; + for (unsigned int i = 0; i < vout.size(); i++) + str += " " + vout[i].ToString() + "\n"; + return str; +} + void CTransaction::UpdateHash() const { *const_cast(&hash) = SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS); diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 59d23fd917..089f3b1426 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -52,6 +52,7 @@ class COutPoint } std::string ToString() const; + std::string ToStringShort() const; }; /** An input of a transaction. It contains the location of the previous @@ -64,6 +65,7 @@ class CTxIn COutPoint prevout; CScript scriptSig; uint32_t nSequence; + CScript prevPubKey; /* Setting nSequence to this value for every input in a transaction * disables nLockTime. */ @@ -121,6 +123,11 @@ class CTxIn return !(a == b); } + friend bool operator<(const CTxIn& a, const CTxIn& b) + { + return a.prevout allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes)); diff --git a/src/protocol.h b/src/protocol.h index 15f27e2d2f..05cb7ea1e1 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -243,6 +243,26 @@ extern const char *GETBLOCKTXN; * @since protocol version 70014 as described by BIP 152 */ extern const char *BLOCKTXN; + +extern const char *SPORK; +extern const char *GETSPORKS; +extern const char *ZNODEPAYMENTVOTE; +extern const char *ZNODEPAYMENTSYNC; +extern const char *SYNCSTATUSCOUNT; +extern const char *DSEG; +extern const char *MNVERIFY; +extern const char *MNPING; +extern const char *MNANNOUNCE; +extern const char *DSACCEPT; +extern const char *DSQUEUE; +extern const char *DSVIN; +extern const char *DSSTATUSUPDATE; +extern const char *DSSIGNFINALTX; +extern const char *DSCOMPLETE; +extern const char *DSFINALTX; +extern const char *TXLOCKVOTE; + + }; /* Get a vector of all valid message types (see above) */ @@ -327,6 +347,15 @@ enum GetDataMsg MSG_WITNESS_BLOCK = MSG_BLOCK | MSG_WITNESS_FLAG, MSG_WITNESS_TX = MSG_TX | MSG_WITNESS_FLAG, MSG_FILTERED_WITNESS_BLOCK = MSG_FILTERED_BLOCK | MSG_WITNESS_FLAG, + MSG_SPORK, + MSG_ZNODE_PAYMENT_VOTE, + MSG_ZNODE_PAYMENT_BLOCK, + MSG_ZNODE_QUORUM, // not implemented + MSG_ZNODE_ANNOUNCE, + MSG_ZNODE_PING, + MSG_ZNODE_VERIFY, + MSG_DSTX, + DSQUEUE, }; /** inv message data */ diff --git a/src/qt/res/icons/zcoin.svg b/src/qt/res/icons/zcoin.svg new file mode 100644 index 0000000000..757e230318 --- /dev/null +++ b/src/qt/res/icons/zcoin.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/random.h b/src/random.h index 31b80bd565..671ee80e6d 100644 --- a/src/random.h +++ b/src/random.h @@ -49,4 +49,32 @@ static inline uint32_t insecure_rand(void) return (insecure_rand_Rw << 16) + insecure_rand_Rz; } +/** + * PRNG initialized from secure entropy based RNG + */ +class InsecureRand +{ +private: + uint32_t nRz; + uint32_t nRw; + bool fDeterministic; + +public: + InsecureRand(bool _fDeterministic = false); + + /** + * MWC RNG of George Marsaglia + * This is intended to be fast. It has a period of 2^59.3, though the + * least significant 16 bits only have a period of about 2^30.1. + * + * @return random value < nMax + */ + int64_t operator()(int64_t nMax) + { + nRz = 36969 * (nRz & 65535) + (nRz >> 16); + nRw = 18000 * (nRw & 65535) + (nRw >> 16); + return ((nRw << 16) + nRz) % nMax; + } +}; + #endif // BITCOIN_RANDOM_H diff --git a/src/rpc/rpcznode.cpp b/src/rpc/rpcznode.cpp new file mode 100644 index 0000000000..5ac3a49ba8 --- /dev/null +++ b/src/rpc/rpcznode.cpp @@ -0,0 +1,808 @@ +// Copyright (c) 2014-2017 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "activeznode.h" +#include "darksend.h" +#include "init.h" +#include "main.h" +#include "znode-payments.h" +#include "znode-sync.h" +#include "znodeconfig.h" +#include "znodeman.h" +#include "rpc/server.h" +#include "util.h" +#include "utilmoneystr.h" +#include "net.h" + +#include +#include +#include + +void EnsureWalletIsUnlocked(); + +UniValue privatesend(const UniValue ¶ms, bool fHelp) { + if (fHelp || params.size() != 1) + throw std::runtime_error( + "privatesend \"command\"\n" + "\nArguments:\n" + "1. \"command\" (string or set of strings, required) The command to execute\n" + "\nAvailable commands:\n" + " start - Start mixing\n" + " stop - Stop mixing\n" + " reset - Reset mixing\n" + ); + + if (params[0].get_str() == "start") { + { + LOCK(pwalletMain->cs_wallet); + EnsureWalletIsUnlocked(); + } + + if (fZNode) + return "Mixing is not supported from znodes"; + + fEnablePrivateSend = true; + bool result = darkSendPool.DoAutomaticDenominating(); + return "Mixing " + + (result ? "started successfully" : ("start failed: " + darkSendPool.GetStatus() + ", will retry")); + } + + if (params[0].get_str() == "stop") { + fEnablePrivateSend = false; + return "Mixing was stopped"; + } + + if (params[0].get_str() == "reset") { + darkSendPool.ResetPool(); + return "Mixing was reset"; + } + + return "Unknown command, please see \"help privatesend\""; +} + +UniValue getpoolinfo(const UniValue ¶ms, bool fHelp) { + if (fHelp || params.size() != 0) + throw std::runtime_error( + "getpoolinfo\n" + "Returns an object containing mixing pool related information.\n"); + + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("state", darkSendPool.GetStateString())); +// obj.push_back(Pair("mixing_mode", fPrivateSendMultiSession ? "multi-session" : "normal")); + obj.push_back(Pair("queue", darkSendPool.GetQueueSize())); + obj.push_back(Pair("entries", darkSendPool.GetEntriesCount())); + obj.push_back(Pair("status", darkSendPool.GetStatus())); + + if (darkSendPool.pSubmittedToZnode) { + obj.push_back(Pair("outpoint", darkSendPool.pSubmittedToZnode->vin.prevout.ToStringShort())); + obj.push_back(Pair("addr", darkSendPool.pSubmittedToZnode->addr.ToString())); + } + + if (pwalletMain) { + obj.push_back(Pair("keys_left", pwalletMain->nKeysLeftSinceAutoBackup)); + obj.push_back(Pair("warnings", pwalletMain->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_WARNING + ? "WARNING: keypool is almost depleted!" : "")); + } + + return obj; +} + + +UniValue znode(const UniValue ¶ms, bool fHelp) { + std::string strCommand; + if (params.size() >= 1) { + strCommand = params[0].get_str(); + } + + if (strCommand == "start-many") + throw JSONRPCError(RPC_INVALID_PARAMETER, "DEPRECATED, please use start-all instead"); + + if (fHelp || + (strCommand != "start" && strCommand != "start-alias" && strCommand != "start-all" && + strCommand != "start-missing" && + strCommand != "start-disabled" && strCommand != "list" && strCommand != "list-conf" && strCommand != "count" && + strCommand != "debug" && strCommand != "current" && strCommand != "winner" && strCommand != "winners" && + strCommand != "genkey" && + strCommand != "connect" && strCommand != "outputs" && strCommand != "status")) + throw std::runtime_error( + "znode \"command\"...\n" + "Set of commands to execute znode related actions\n" + "\nArguments:\n" + "1. \"command\" (string or set of strings, required) The command to execute\n" + "\nAvailable commands:\n" + " count - Print number of all known znodes (optional: 'ps', 'enabled', 'all', 'qualify')\n" + " current - Print info on current znode winner to be paid the next block (calculated locally)\n" + " debug - Print znode status\n" + " genkey - Generate new znodeprivkey\n" + " outputs - Print znode compatible outputs\n" + " start - Start local Hot znode configured in dash.conf\n" + " start-alias - Start single remote znode by assigned alias configured in znode.conf\n" + " start- - Start remote znodes configured in znode.conf (: 'all', 'missing', 'disabled')\n" + " status - Print znode status information\n" + " list - Print list of all known znodes (see znodelist for more info)\n" + " list-conf - Print znode.conf in JSON format\n" + " winner - Print info on next znode winner to vote for\n" + " winners - Print list of znode winners\n" + ); + + if (strCommand == "list") { + UniValue newParams(UniValue::VARR); + // forward params but skip "list" + for (unsigned int i = 1; i < params.size(); i++) { + newParams.push_back(params[i]); + } + return znodelist(newParams, fHelp); + } + + if (strCommand == "connect") { + if (params.size() < 2) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Znode address required"); + + std::string strAddress = params[1].get_str(); + + CService addr = CService(strAddress); + +// CNode *pnode = ConnectNodeDash(CAddress(addr, NODE_NETWORK), NULL); +// if (!pnode) + throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Couldn't connect to znode %s", strAddress)); + + return "successfully connected"; + } + + if (strCommand == "count") { + if (params.size() > 2) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Too many parameters"); + + if (params.size() == 1) + return mnodeman.size(); + + std::string strMode = params[1].get_str(); + + if (strMode == "ps") + return mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION); + + if (strMode == "enabled") + return mnodeman.CountEnabled(); + + int nCount; + mnodeman.GetNextZnodeInQueueForPayment(true, nCount); + + if (strMode == "qualify") + return nCount; + + if (strMode == "all") + return strprintf("Total: %d (PS Compatible: %d / Enabled: %d / Qualify: %d)", + mnodeman.size(), mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION), + mnodeman.CountEnabled(), nCount); + } + + if (strCommand == "current" || strCommand == "winner") { + int nCount; + int nHeight; + CZnode *winner = NULL; + { + LOCK(cs_main); + nHeight = chainActive.Height() + (strCommand == "current" ? 1 : 10); + } + mnodeman.UpdateLastPaid(); + winner = mnodeman.GetNextZnodeInQueueForPayment(nHeight, true, nCount); + if (!winner) return "unknown"; + + UniValue obj(UniValue::VOBJ); + + obj.push_back(Pair("height", nHeight)); + obj.push_back(Pair("IP:port", winner->addr.ToString())); + obj.push_back(Pair("protocol", (int64_t) winner->nProtocolVersion)); + obj.push_back(Pair("vin", winner->vin.prevout.ToStringShort())); + obj.push_back(Pair("payee", CBitcoinAddress(winner->pubKeyCollateralAddress.GetID()).ToString())); + obj.push_back(Pair("lastseen", (winner->lastPing == CZnodePing()) ? winner->sigTime : + winner->lastPing.sigTime)); + obj.push_back(Pair("activeseconds", (winner->lastPing == CZnodePing()) ? 0 : + (winner->lastPing.sigTime - winner->sigTime))); + return obj; + } + + if (strCommand == "debug") { + if (activeZnode.nState != ACTIVE_ZNODE_INITIAL || !znodeSync.IsBlockchainSynced()) + return activeZnode.GetStatus(); + + CTxIn vin; + CPubKey pubkey; + CKey key; + + if (!pwalletMain || !pwalletMain->GetZnodeVinAndKeys(vin, pubkey, key)) + throw JSONRPCError(RPC_INVALID_PARAMETER, + "Missing znode input, please look at the documentation for instructions on znode creation"); + + return activeZnode.GetStatus(); + } + + if (strCommand == "start") { + if (!fZNode) + throw JSONRPCError(RPC_INTERNAL_ERROR, "You must set znode=1 in the configuration"); + + { + LOCK(pwalletMain->cs_wallet); + EnsureWalletIsUnlocked(); + } + + if (activeZnode.nState != ACTIVE_ZNODE_STARTED) { + activeZnode.nState = ACTIVE_ZNODE_INITIAL; // TODO: consider better way + activeZnode.ManageState(); + } + + return activeZnode.GetStatus(); + } + + if (strCommand == "start-alias") { + if (params.size() < 2) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Please specify an alias"); + + { + LOCK(pwalletMain->cs_wallet); + EnsureWalletIsUnlocked(); + } + + std::string strAlias = params[1].get_str(); + + bool fFound = false; + + UniValue statusObj(UniValue::VOBJ); + statusObj.push_back(Pair("alias", strAlias)); + + BOOST_FOREACH(CZnodeConfig::CZnodeEntry + mne, znodeConfig.getEntries()) { + if (mne.getAlias() == strAlias) { + fFound = true; + std::string strError; + CZnodeBroadcast mnb; + + bool fResult = CZnodeBroadcast::Create(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), + mne.getOutputIndex(), strError, mnb); + + statusObj.push_back(Pair("result", fResult ? "successful" : "failed")); + if (fResult) { + mnodeman.UpdateZnodeList(mnb); + mnb.Relay(); + } else { + statusObj.push_back(Pair("errorMessage", strError)); + } + mnodeman.NotifyZnodeUpdates(); + break; + } + } + + if (!fFound) { + statusObj.push_back(Pair("result", "failed")); + statusObj.push_back(Pair("errorMessage", "Could not find alias in config. Verify with list-conf.")); + } + + return statusObj; + + } + + if (strCommand == "start-all" || strCommand == "start-missing" || strCommand == "start-disabled") { + { + LOCK(pwalletMain->cs_wallet); + EnsureWalletIsUnlocked(); + } + + if ((strCommand == "start-missing" || strCommand == "start-disabled") && + !znodeSync.IsZnodeListSynced()) { + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, + "You can't use this command until znode list is synced"); + } + + int nSuccessful = 0; + int nFailed = 0; + + UniValue resultsObj(UniValue::VOBJ); + + BOOST_FOREACH(CZnodeConfig::CZnodeEntry + mne, znodeConfig.getEntries()) { + std::string strError; + + CTxIn vin = CTxIn(uint256S(mne.getTxHash()), uint32_t(atoi(mne.getOutputIndex().c_str()))); + CZnode *pmn = mnodeman.Find(vin); + CZnodeBroadcast mnb; + + if (strCommand == "start-missing" && pmn) continue; + if (strCommand == "start-disabled" && pmn && pmn->IsEnabled()) continue; + + bool fResult = CZnodeBroadcast::Create(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), + mne.getOutputIndex(), strError, mnb); + + UniValue statusObj(UniValue::VOBJ); + statusObj.push_back(Pair("alias", mne.getAlias())); + statusObj.push_back(Pair("result", fResult ? "successful" : "failed")); + + if (fResult) { + nSuccessful++; + mnodeman.UpdateZnodeList(mnb); + mnb.Relay(); + } else { + nFailed++; + statusObj.push_back(Pair("errorMessage", strError)); + } + + resultsObj.push_back(Pair("status", statusObj)); + } + mnodeman.NotifyZnodeUpdates(); + + UniValue returnObj(UniValue::VOBJ); + returnObj.push_back(Pair("overall", + strprintf("Successfully started %d znodes, failed to start %d, total %d", + nSuccessful, nFailed, nSuccessful + nFailed))); + returnObj.push_back(Pair("detail", resultsObj)); + + return returnObj; + } + + if (strCommand == "genkey") { + CKey secret; + secret.MakeNewKey(false); + + return CBitcoinSecret(secret).ToString(); + } + + if (strCommand == "list-conf") { + UniValue resultObj(UniValue::VOBJ); + + BOOST_FOREACH(CZnodeConfig::CZnodeEntry + mne, znodeConfig.getEntries()) { + CTxIn vin = CTxIn(uint256S(mne.getTxHash()), uint32_t(atoi(mne.getOutputIndex().c_str()))); + CZnode *pmn = mnodeman.Find(vin); + + std::string strStatus = pmn ? pmn->GetStatus() : "MISSING"; + + UniValue mnObj(UniValue::VOBJ); + mnObj.push_back(Pair("alias", mne.getAlias())); + mnObj.push_back(Pair("address", mne.getIp())); + mnObj.push_back(Pair("privateKey", mne.getPrivKey())); + mnObj.push_back(Pair("txHash", mne.getTxHash())); + mnObj.push_back(Pair("outputIndex", mne.getOutputIndex())); + mnObj.push_back(Pair("status", strStatus)); + resultObj.push_back(Pair("znode", mnObj)); + } + + return resultObj; + } + + if (strCommand == "outputs") { + // Find possible candidates + std::vector vPossibleCoins; + pwalletMain->AvailableCoinsDash(vPossibleCoins, true, NULL, false, ONLY_1000); + + UniValue obj(UniValue::VOBJ); + BOOST_FOREACH(COutput & out, vPossibleCoins) + { + obj.push_back(Pair(out.tx->GetHash().ToString(), strprintf("%d", out.i))); + } + + return obj; + + } + + if (strCommand == "status") { + if (!fZNode) + throw JSONRPCError(RPC_INTERNAL_ERROR, "This is not a znode"); + + UniValue mnObj(UniValue::VOBJ); + + mnObj.push_back(Pair("vin", activeZnode.vin.ToString())); + mnObj.push_back(Pair("service", activeZnode.service.ToString())); + + CZnode mn; + if (mnodeman.Get(activeZnode.vin, mn)) { + mnObj.push_back(Pair("payee", CBitcoinAddress(mn.pubKeyCollateralAddress.GetID()).ToString())); + } + + mnObj.push_back(Pair("status", activeZnode.GetStatus())); + return mnObj; + } + + if (strCommand == "winners") { + int nHeight; + { + LOCK(cs_main); + CBlockIndex *pindex = chainActive.Tip(); + if (!pindex) return NullUniValue; + + nHeight = pindex->nHeight; + } + + int nLast = 10; + std::string strFilter = ""; + + if (params.size() >= 2) { + nLast = atoi(params[1].get_str()); + } + + if (params.size() == 3) { + strFilter = params[2].get_str(); + } + + if (params.size() > 3) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Correct usage is 'znode winners ( \"count\" \"filter\" )'"); + + UniValue obj(UniValue::VOBJ); + + for (int i = nHeight - nLast; i < nHeight + 20; i++) { + std::string strPayment = GetRequiredPaymentsString(i); + if (strFilter != "" && strPayment.find(strFilter) == std::string::npos) continue; + obj.push_back(Pair(strprintf("%d", i), strPayment)); + } + + return obj; + } + + return NullUniValue; +} + +UniValue znodelist(const UniValue ¶ms, bool fHelp) { + std::string strMode = "status"; + std::string strFilter = ""; + + if (params.size() >= 1) strMode = params[0].get_str(); + if (params.size() == 2) strFilter = params[1].get_str(); + + if (fHelp || ( + strMode != "activeseconds" && strMode != "addr" && strMode != "full" && + strMode != "lastseen" && strMode != "lastpaidtime" && strMode != "lastpaidblock" && + strMode != "protocol" && strMode != "payee" && strMode != "rank" && strMode != "status")) { + throw std::runtime_error( + "znodelist ( \"mode\" \"filter\" )\n" + "Get a list of znodes in different modes\n" + "\nArguments:\n" + "1. \"mode\" (string, optional/required to use filter, defaults = status) The mode to run list in\n" + "2. \"filter\" (string, optional) Filter results. Partial match by outpoint by default in all modes,\n" + " additional matches in some modes are also available\n" + "\nAvailable modes:\n" + " activeseconds - Print number of seconds znode recognized by the network as enabled\n" + " (since latest issued \"znode start/start-many/start-alias\")\n" + " addr - Print ip address associated with a znode (can be additionally filtered, partial match)\n" + " full - Print info in format 'status protocol payee lastseen activeseconds lastpaidtime lastpaidblock IP'\n" + " (can be additionally filtered, partial match)\n" + " lastpaidblock - Print the last block height a node was paid on the network\n" + " lastpaidtime - Print the last time a node was paid on the network\n" + " lastseen - Print timestamp of when a znode was last seen on the network\n" + " payee - Print Dash address associated with a znode (can be additionally filtered,\n" + " partial match)\n" + " protocol - Print protocol of a znode (can be additionally filtered, exact match))\n" + " rank - Print rank of a znode based on current block\n" + " status - Print znode status: PRE_ENABLED / ENABLED / EXPIRED / WATCHDOG_EXPIRED / NEW_START_REQUIRED /\n" + " UPDATE_REQUIRED / POSE_BAN / OUTPOINT_SPENT (can be additionally filtered, partial match)\n" + ); + } + + if (strMode == "full" || strMode == "lastpaidtime" || strMode == "lastpaidblock") { + mnodeman.UpdateLastPaid(); + } + + UniValue obj(UniValue::VOBJ); + if (strMode == "rank") { + std::vector > vZnodeRanks = mnodeman.GetZnodeRanks(); + BOOST_FOREACH(PAIRTYPE(int, CZnode) & s, vZnodeRanks) + { + std::string strOutpoint = s.second.vin.prevout.ToStringShort(); + if (strFilter != "" && strOutpoint.find(strFilter) == std::string::npos) continue; + obj.push_back(Pair(strOutpoint, s.first)); + } + } else { + std::vector vZnodes = mnodeman.GetFullZnodeVector(); + BOOST_FOREACH(CZnode & mn, vZnodes) + { + std::string strOutpoint = mn.vin.prevout.ToStringShort(); + if (strMode == "activeseconds") { + if (strFilter != "" && strOutpoint.find(strFilter) == std::string::npos) continue; + obj.push_back(Pair(strOutpoint, (int64_t)(mn.lastPing.sigTime - mn.sigTime))); + } else if (strMode == "addr") { + std::string strAddress = mn.addr.ToString(); + if (strFilter != "" && strAddress.find(strFilter) == std::string::npos && + strOutpoint.find(strFilter) == std::string::npos) + continue; + obj.push_back(Pair(strOutpoint, strAddress)); + } else if (strMode == "full") { + std::ostringstream streamFull; + streamFull << std::setw(18) << + mn.GetStatus() << " " << + mn.nProtocolVersion << " " << + CBitcoinAddress(mn.pubKeyCollateralAddress.GetID()).ToString() << " " << + (int64_t) mn.lastPing.sigTime << " " << std::setw(8) << + (int64_t)(mn.lastPing.sigTime - mn.sigTime) << " " << std::setw(10) << + mn.GetLastPaidTime() << " " << std::setw(6) << + mn.GetLastPaidBlock() << " " << + mn.addr.ToString(); + std::string strFull = streamFull.str(); + if (strFilter != "" && strFull.find(strFilter) == std::string::npos && + strOutpoint.find(strFilter) == std::string::npos) + continue; + obj.push_back(Pair(strOutpoint, strFull)); + } else if (strMode == "lastpaidblock") { + if (strFilter != "" && strOutpoint.find(strFilter) == std::string::npos) continue; + obj.push_back(Pair(strOutpoint, mn.GetLastPaidBlock())); + } else if (strMode == "lastpaidtime") { + if (strFilter != "" && strOutpoint.find(strFilter) == std::string::npos) continue; + obj.push_back(Pair(strOutpoint, mn.GetLastPaidTime())); + } else if (strMode == "lastseen") { + if (strFilter != "" && strOutpoint.find(strFilter) == std::string::npos) continue; + obj.push_back(Pair(strOutpoint, (int64_t) mn.lastPing.sigTime)); + } else if (strMode == "payee") { + CBitcoinAddress address(mn.pubKeyCollateralAddress.GetID()); + std::string strPayee = address.ToString(); + if (strFilter != "" && strPayee.find(strFilter) == std::string::npos && + strOutpoint.find(strFilter) == std::string::npos) + continue; + obj.push_back(Pair(strOutpoint, strPayee)); + } else if (strMode == "protocol") { + if (strFilter != "" && strFilter != strprintf("%d", mn.nProtocolVersion) && + strOutpoint.find(strFilter) == std::string::npos) + continue; + obj.push_back(Pair(strOutpoint, (int64_t) mn.nProtocolVersion)); + } else if (strMode == "status") { + std::string strStatus = mn.GetStatus(); + if (strFilter != "" && strStatus.find(strFilter) == std::string::npos && + strOutpoint.find(strFilter) == std::string::npos) + continue; + obj.push_back(Pair(strOutpoint, strStatus)); + } + } + } + return obj; +} + +bool DecodeHexVecMnb(std::vector &vecMnb, std::string strHexMnb) { + + if (!IsHex(strHexMnb)) + return false; + + std::vector mnbData(ParseHex(strHexMnb)); + CDataStream ssData(mnbData, SER_NETWORK, PROTOCOL_VERSION); + try { + ssData >> vecMnb; + } + catch (const std::exception &) { + return false; + } + + return true; +} + +UniValue znodebroadcast(const UniValue ¶ms, bool fHelp) { + std::string strCommand; + if (params.size() >= 1) + strCommand = params[0].get_str(); + + if (fHelp || + (strCommand != "create-alias" && strCommand != "create-all" && strCommand != "decode" && strCommand != "relay")) + throw std::runtime_error( + "znodebroadcast \"command\"...\n" + "Set of commands to create and relay znode broadcast messages\n" + "\nArguments:\n" + "1. \"command\" (string or set of strings, required) The command to execute\n" + "\nAvailable commands:\n" + " create-alias - Create single remote znode broadcast message by assigned alias configured in znode.conf\n" + " create-all - Create remote znode broadcast messages for all znodes configured in znode.conf\n" + " decode - Decode znode broadcast message\n" + " relay - Relay znode broadcast message to the network\n" + ); + + if (strCommand == "create-alias") { + // wait for reindex and/or import to finish + if (fImporting || fReindex) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Wait for reindex and/or import to finish"); + + if (params.size() < 2) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Please specify an alias"); + + { + LOCK(pwalletMain->cs_wallet); + EnsureWalletIsUnlocked(); + } + + bool fFound = false; + std::string strAlias = params[1].get_str(); + + UniValue statusObj(UniValue::VOBJ); + std::vector vecMnb; + + statusObj.push_back(Pair("alias", strAlias)); + + BOOST_FOREACH(CZnodeConfig::CZnodeEntry + mne, znodeConfig.getEntries()) { + if (mne.getAlias() == strAlias) { + fFound = true; + std::string strError; + CZnodeBroadcast mnb; + + bool fResult = CZnodeBroadcast::Create(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), + mne.getOutputIndex(), strError, mnb, true); + + statusObj.push_back(Pair("result", fResult ? "successful" : "failed")); + if (fResult) { + vecMnb.push_back(mnb); + CDataStream ssVecMnb(SER_NETWORK, PROTOCOL_VERSION); + ssVecMnb << vecMnb; + statusObj.push_back(Pair("hex", HexStr(ssVecMnb.begin(), ssVecMnb.end()))); + } else { + statusObj.push_back(Pair("errorMessage", strError)); + } + break; + } + } + + if (!fFound) { + statusObj.push_back(Pair("result", "not found")); + statusObj.push_back(Pair("errorMessage", "Could not find alias in config. Verify with list-conf.")); + } + + return statusObj; + + } + + if (strCommand == "create-all") { + // wait for reindex and/or import to finish + if (fImporting || fReindex) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Wait for reindex and/or import to finish"); + + { + LOCK(pwalletMain->cs_wallet); + EnsureWalletIsUnlocked(); + } + + std::vector mnEntries; + mnEntries = znodeConfig.getEntries(); + + int nSuccessful = 0; + int nFailed = 0; + + UniValue resultsObj(UniValue::VOBJ); + std::vector vecMnb; + + BOOST_FOREACH(CZnodeConfig::CZnodeEntry + mne, znodeConfig.getEntries()) { + std::string strError; + CZnodeBroadcast mnb; + + bool fResult = CZnodeBroadcast::Create(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), + mne.getOutputIndex(), strError, mnb, true); + + UniValue statusObj(UniValue::VOBJ); + statusObj.push_back(Pair("alias", mne.getAlias())); + statusObj.push_back(Pair("result", fResult ? "successful" : "failed")); + + if (fResult) { + nSuccessful++; + vecMnb.push_back(mnb); + } else { + nFailed++; + statusObj.push_back(Pair("errorMessage", strError)); + } + + resultsObj.push_back(Pair("status", statusObj)); + } + + CDataStream ssVecMnb(SER_NETWORK, PROTOCOL_VERSION); + ssVecMnb << vecMnb; + UniValue returnObj(UniValue::VOBJ); + returnObj.push_back(Pair("overall", strprintf( + "Successfully created broadcast messages for %d znodes, failed to create %d, total %d", + nSuccessful, nFailed, nSuccessful + nFailed))); + returnObj.push_back(Pair("detail", resultsObj)); + returnObj.push_back(Pair("hex", HexStr(ssVecMnb.begin(), ssVecMnb.end()))); + + return returnObj; + } + + if (strCommand == "decode") { + if (params.size() != 2) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Correct usage is 'znodebroadcast decode \"hexstring\"'"); + + std::vector vecMnb; + + if (!DecodeHexVecMnb(vecMnb, params[1].get_str())) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Znode broadcast message decode failed"); + + int nSuccessful = 0; + int nFailed = 0; + int nDos = 0; + UniValue returnObj(UniValue::VOBJ); + + BOOST_FOREACH(CZnodeBroadcast & mnb, vecMnb) + { + UniValue resultObj(UniValue::VOBJ); + + if (mnb.CheckSignature(nDos)) { + nSuccessful++; + resultObj.push_back(Pair("vin", mnb.vin.ToString())); + resultObj.push_back(Pair("addr", mnb.addr.ToString())); + resultObj.push_back(Pair("pubKeyCollateralAddress", + CBitcoinAddress(mnb.pubKeyCollateralAddress.GetID()).ToString())); + resultObj.push_back(Pair("pubKeyZnode", CBitcoinAddress(mnb.pubKeyZnode.GetID()).ToString())); + resultObj.push_back(Pair("vchSig", EncodeBase64(&mnb.vchSig[0], mnb.vchSig.size()))); + resultObj.push_back(Pair("sigTime", mnb.sigTime)); + resultObj.push_back(Pair("protocolVersion", mnb.nProtocolVersion)); + resultObj.push_back(Pair("nLastDsq", mnb.nLastDsq)); + + UniValue lastPingObj(UniValue::VOBJ); + lastPingObj.push_back(Pair("vin", mnb.lastPing.vin.ToString())); + lastPingObj.push_back(Pair("blockHash", mnb.lastPing.blockHash.ToString())); + lastPingObj.push_back(Pair("sigTime", mnb.lastPing.sigTime)); + lastPingObj.push_back( + Pair("vchSig", EncodeBase64(&mnb.lastPing.vchSig[0], mnb.lastPing.vchSig.size()))); + + resultObj.push_back(Pair("lastPing", lastPingObj)); + } else { + nFailed++; + resultObj.push_back(Pair("errorMessage", "Znode broadcast signature verification failed")); + } + + returnObj.push_back(Pair(mnb.GetHash().ToString(), resultObj)); + } + + returnObj.push_back(Pair("overall", strprintf( + "Successfully decoded broadcast messages for %d znodes, failed to decode %d, total %d", + nSuccessful, nFailed, nSuccessful + nFailed))); + + return returnObj; + } + + if (strCommand == "relay") { + if (params.size() < 2 || params.size() > 3) + throw JSONRPCError(RPC_INVALID_PARAMETER, "znodebroadcast relay \"hexstring\" ( fast )\n" + "\nArguments:\n" + "1. \"hex\" (string, required) Broadcast messages hex string\n" + "2. fast (string, optional) If none, using safe method\n"); + + std::vector vecMnb; + + if (!DecodeHexVecMnb(vecMnb, params[1].get_str())) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Znode broadcast message decode failed"); + + int nSuccessful = 0; + int nFailed = 0; + bool fSafe = params.size() == 2; + UniValue returnObj(UniValue::VOBJ); + + // verify all signatures first, bailout if any of them broken + BOOST_FOREACH(CZnodeBroadcast & mnb, vecMnb) + { + UniValue resultObj(UniValue::VOBJ); + + resultObj.push_back(Pair("vin", mnb.vin.ToString())); + resultObj.push_back(Pair("addr", mnb.addr.ToString())); + + int nDos = 0; + bool fResult; + if (mnb.CheckSignature(nDos)) { + if (fSafe) { + fResult = mnodeman.CheckMnbAndUpdateZnodeList(NULL, mnb, nDos); + } else { + mnodeman.UpdateZnodeList(mnb); + mnb.Relay(); + fResult = true; + } + mnodeman.NotifyZnodeUpdates(); + } else fResult = false; + + if (fResult) { + nSuccessful++; + resultObj.push_back(Pair(mnb.GetHash().ToString(), "successful")); + } else { + nFailed++; + resultObj.push_back(Pair("errorMessage", "Znode broadcast signature verification failed")); + } + + returnObj.push_back(Pair(mnb.GetHash().ToString(), resultObj)); + } + + returnObj.push_back(Pair("overall", strprintf( + "Successfully relayed broadcast messages for %d znodes, failed to relay %d, total %d", nSuccessful, + nFailed, nSuccessful + nFailed))); + + return returnObj; + } + + return NullUniValue; +} diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 8ee6b70d10..e910e9d6f9 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -271,6 +271,11 @@ static const CRPCCommand vRPCCommands[] = /* Overall control/query calls */ { "control", "help", &help, true }, { "control", "stop", &stop, true }, + /* Dash features */ + { "dash", "znode", &znode, true }, + { "dash", "znodelist", &znodelist, true }, + { "dash", "znodebroadcast", &znodebroadcast, true }, + { "dash", "getpoolinfo", &getpoolinfo, true }, }; CRPCTable::CRPCTable() diff --git a/src/rpc/server.h b/src/rpc/server.h index 1ed8f2466d..7ba177b492 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -190,6 +190,12 @@ extern std::string HelpRequiringPassphrase(); extern std::string HelpExampleCli(const std::string& methodname, const std::string& args); extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args); +extern UniValue getpoolinfo(const UniValue& params, bool fHelp); +extern UniValue spork(const UniValue& params, bool fHelp); +extern UniValue znode(const UniValue& params, bool fHelp); +extern UniValue znodelist(const UniValue& params, bool fHelp); +extern UniValue znodebroadcast(const UniValue& params, bool fHelp); + extern void EnsureWalletIsUnlocked(); bool StartRPC(); diff --git a/src/script/script.cpp b/src/script/script.cpp index e7905bb713..60003129a9 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -205,6 +205,30 @@ unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const return subscript.GetSigOpCount(true); } +bool CScript::IsNormalPaymentScript() const +{ + if(this->size() != 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; +} + bool CScript::IsPayToScriptHash() const { // Extra-fast test for pay-to-script-hash CScripts: diff --git a/src/script/script.h b/src/script/script.h index 021b602428..49e361b618 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -632,6 +632,7 @@ class CScript : public CScriptBase * pay-to-script-hash transactions: */ unsigned int GetSigOpCount(const CScript& scriptSig) const; + bool IsNormalPaymentScript() const; bool IsPayToScriptHash() const; bool IsPayToWitnessScriptHash() const; diff --git a/src/spork.cpp b/src/spork.cpp new file mode 100644 index 0000000000..e314a10297 --- /dev/null +++ b/src/spork.cpp @@ -0,0 +1,263 @@ +// Copyright (c) 2014-2017 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "darksend.h" +#include "main.h" +#include "spork.h" + +#include + +class CSporkMessage; +class CSporkManager; + +CSporkManager sporkManager; + +std::map mapSporks; + +void CSporkManager::ProcessSpork(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) +{ + if(fLiteMode) return; // disable all Dash specific functionality + + if (strCommand == NetMsgType::SPORK) { + + CDataStream vMsg(vRecv); + CSporkMessage spork; + vRecv >> spork; + + uint256 hash = spork.GetHash(); + + std::string strLogMsg; + { + LOCK(cs_main); + pfrom->setAskFor.erase(hash); + if(!chainActive.Tip()) return; + strLogMsg = strprintf("SPORK -- hash: %s id: %d value: %10d bestHeight: %d peer=%d", hash.ToString(), spork.nSporkID, spork.nValue, chainActive.Height(), pfrom->id); + } + + if(mapSporksActive.count(spork.nSporkID)) { + if (mapSporksActive[spork.nSporkID].nTimeSigned >= spork.nTimeSigned) { + LogPrint("spork", "%s seen\n", strLogMsg); + return; + } else { + LogPrintf("%s updated\n", strLogMsg); + } + } else { + LogPrintf("%s new\n", strLogMsg); + } + + if(!spork.CheckSignature()) { + LogPrintf("CSporkManager::ProcessSpork -- invalid signature\n"); + Misbehaving(pfrom->GetId(), 100); + return; + } + + mapSporks[hash] = spork; + mapSporksActive[spork.nSporkID] = spork; + spork.Relay(); + + //does a task if needed + ExecuteSpork(spork.nSporkID, spork.nValue); + + } else if (strCommand == NetMsgType::GETSPORKS) { + + std::map::iterator it = mapSporksActive.begin(); + + while(it != mapSporksActive.end()) { + pfrom->PushMessage(NetMsgType::SPORK, it->second); + it++; + } + } + +} + +void CSporkManager::ExecuteSpork(int nSporkID, int nValue) +{ + //correct fork via spork technology + if(nSporkID == SPORK_12_RECONSIDER_BLOCKS && nValue > 0) { + // allow to reprocess 24h of blocks max, which should be enough to resolve any issues + int64_t nMaxBlocks = 576; + // this potentially can be a heavy operation, so only allow this to be executed once per 10 minutes + int64_t nTimeout = 10 * 60; + + static int64_t nTimeExecuted = 0; // i.e. it was never executed before + + if(GetTime() - nTimeExecuted < nTimeout) { + LogPrint("spork", "CSporkManager::ExecuteSpork -- ERROR: Trying to reconsider blocks, too soon - %d/%d\n", GetTime() - nTimeExecuted, nTimeout); + return; + } + + if(nValue > nMaxBlocks) { + LogPrintf("CSporkManager::ExecuteSpork -- ERROR: Trying to reconsider too many blocks %d/%d\n", nValue, nMaxBlocks); + return; + } + + + LogPrintf("CSporkManager::ExecuteSpork -- Reconsider Last %d Blocks\n", nValue); + + ReprocessBlocks(nValue); + nTimeExecuted = GetTime(); + } +} + +bool CSporkManager::UpdateSpork(int nSporkID, int64_t nValue) +{ + + CSporkMessage spork = CSporkMessage(nSporkID, nValue, GetTime()); + + if(spork.Sign(strMasterPrivKey)) { + spork.Relay(); + mapSporks[spork.GetHash()] = spork; + mapSporksActive[nSporkID] = spork; + return true; + } + + return false; +} + +// grab the spork, otherwise say it's off +bool CSporkManager::IsSporkActive(int nSporkID) +{ + int64_t r = -1; + + if(mapSporksActive.count(nSporkID)){ + r = mapSporksActive[nSporkID].nValue; + } else { + switch (nSporkID) { + case SPORK_2_INSTANTSEND_ENABLED: r = SPORK_2_INSTANTSEND_ENABLED_DEFAULT; break; + case SPORK_3_INSTANTSEND_BLOCK_FILTERING: r = SPORK_3_INSTANTSEND_BLOCK_FILTERING_DEFAULT; break; + case SPORK_5_INSTANTSEND_MAX_VALUE: r = SPORK_5_INSTANTSEND_MAX_VALUE_DEFAULT; break; + case SPORK_8_ZNODE_PAYMENT_ENFORCEMENT: r = SPORK_8_ZNODE_PAYMENT_ENFORCEMENT_DEFAULT; break; + case SPORK_9_SUPERBLOCKS_ENABLED: r = SPORK_9_SUPERBLOCKS_ENABLED_DEFAULT; break; + case SPORK_10_ZNODE_PAY_UPDATED_NODES: r = SPORK_10_ZNODE_PAY_UPDATED_NODES_DEFAULT; break; + case SPORK_12_RECONSIDER_BLOCKS: r = SPORK_12_RECONSIDER_BLOCKS_DEFAULT; break; + case SPORK_13_OLD_SUPERBLOCK_FLAG: r = SPORK_13_OLD_SUPERBLOCK_FLAG_DEFAULT; break; + case SPORK_14_REQUIRE_SENTINEL_FLAG: r = SPORK_14_REQUIRE_SENTINEL_FLAG_DEFAULT; break; + default: + LogPrint("spork", "CSporkManager::IsSporkActive -- Unknown Spork ID %d\n", nSporkID); + r = 4070908800ULL; // 2099-1-1 i.e. off by default + break; + } + } + + return r < GetTime(); +} + +// grab the value of the spork on the network, or the default +int64_t CSporkManager::GetSporkValue(int nSporkID) +{ + if (mapSporksActive.count(nSporkID)) + return mapSporksActive[nSporkID].nValue; + + switch (nSporkID) { + case SPORK_2_INSTANTSEND_ENABLED: return SPORK_2_INSTANTSEND_ENABLED_DEFAULT; + case SPORK_3_INSTANTSEND_BLOCK_FILTERING: return SPORK_3_INSTANTSEND_BLOCK_FILTERING_DEFAULT; + case SPORK_5_INSTANTSEND_MAX_VALUE: return SPORK_5_INSTANTSEND_MAX_VALUE_DEFAULT; + case SPORK_8_ZNODE_PAYMENT_ENFORCEMENT: return SPORK_8_ZNODE_PAYMENT_ENFORCEMENT_DEFAULT; + case SPORK_9_SUPERBLOCKS_ENABLED: return SPORK_9_SUPERBLOCKS_ENABLED_DEFAULT; + case SPORK_10_ZNODE_PAY_UPDATED_NODES: return SPORK_10_ZNODE_PAY_UPDATED_NODES_DEFAULT; + case SPORK_12_RECONSIDER_BLOCKS: return SPORK_12_RECONSIDER_BLOCKS_DEFAULT; + case SPORK_13_OLD_SUPERBLOCK_FLAG: return SPORK_13_OLD_SUPERBLOCK_FLAG_DEFAULT; + case SPORK_14_REQUIRE_SENTINEL_FLAG: return SPORK_14_REQUIRE_SENTINEL_FLAG_DEFAULT; + default: + LogPrint("spork", "CSporkManager::GetSporkValue -- Unknown Spork ID %d\n", nSporkID); + return -1; + } + +} + +int CSporkManager::GetSporkIDByName(std::string strName) +{ + if (strName == "SPORK_2_INSTANTSEND_ENABLED") return SPORK_2_INSTANTSEND_ENABLED; + if (strName == "SPORK_3_INSTANTSEND_BLOCK_FILTERING") return SPORK_3_INSTANTSEND_BLOCK_FILTERING; + if (strName == "SPORK_5_INSTANTSEND_MAX_VALUE") return SPORK_5_INSTANTSEND_MAX_VALUE; + if (strName == "SPORK_8_ZNODE_PAYMENT_ENFORCEMENT") return SPORK_8_ZNODE_PAYMENT_ENFORCEMENT; + if (strName == "SPORK_9_SUPERBLOCKS_ENABLED") return SPORK_9_SUPERBLOCKS_ENABLED; + if (strName == "SPORK_10_ZNODE_PAY_UPDATED_NODES") return SPORK_10_ZNODE_PAY_UPDATED_NODES; + if (strName == "SPORK_12_RECONSIDER_BLOCKS") return SPORK_12_RECONSIDER_BLOCKS; + if (strName == "SPORK_13_OLD_SUPERBLOCK_FLAG") return SPORK_13_OLD_SUPERBLOCK_FLAG; + if (strName == "SPORK_14_REQUIRE_SENTINEL_FLAG") return SPORK_14_REQUIRE_SENTINEL_FLAG; + + LogPrint("spork", "CSporkManager::GetSporkIDByName -- Unknown Spork name '%s'\n", strName); + return -1; +} + +std::string CSporkManager::GetSporkNameByID(int nSporkID) +{ + switch (nSporkID) { + case SPORK_2_INSTANTSEND_ENABLED: return "SPORK_2_INSTANTSEND_ENABLED"; + case SPORK_3_INSTANTSEND_BLOCK_FILTERING: return "SPORK_3_INSTANTSEND_BLOCK_FILTERING"; + case SPORK_5_INSTANTSEND_MAX_VALUE: return "SPORK_5_INSTANTSEND_MAX_VALUE"; + case SPORK_8_ZNODE_PAYMENT_ENFORCEMENT: return "SPORK_8_ZNODE_PAYMENT_ENFORCEMENT"; + case SPORK_9_SUPERBLOCKS_ENABLED: return "SPORK_9_SUPERBLOCKS_ENABLED"; + case SPORK_10_ZNODE_PAY_UPDATED_NODES: return "SPORK_10_ZNODE_PAY_UPDATED_NODES"; + case SPORK_12_RECONSIDER_BLOCKS: return "SPORK_12_RECONSIDER_BLOCKS"; + case SPORK_13_OLD_SUPERBLOCK_FLAG: return "SPORK_13_OLD_SUPERBLOCK_FLAG"; + case SPORK_14_REQUIRE_SENTINEL_FLAG: return "SPORK_14_REQUIRE_SENTINEL_FLAG"; + default: + LogPrint("spork", "CSporkManager::GetSporkNameByID -- Unknown Spork ID %d\n", nSporkID); + return "Unknown"; + } +} + +bool CSporkManager::SetPrivKey(std::string strPrivKey) +{ + CSporkMessage spork; + + spork.Sign(strPrivKey); + + if(spork.CheckSignature()){ + // Test signing successful, proceed + LogPrintf("CSporkManager::SetPrivKey -- Successfully initialized as spork signer\n"); + strMasterPrivKey = strPrivKey; + return true; + } else { + return false; + } +} + +bool CSporkMessage::Sign(std::string strSignKey) +{ + CKey key; + CPubKey pubkey; + std::string strError = ""; + std::string strMessage = boost::lexical_cast(nSporkID) + boost::lexical_cast(nValue) + boost::lexical_cast(nTimeSigned); + + if(!darkSendSigner.GetKeysFromSecret(strSignKey, key, pubkey)) { + LogPrintf("CSporkMessage::Sign -- GetKeysFromSecret() failed, invalid spork key %s\n", strSignKey); + return false; + } + + if(!darkSendSigner.SignMessage(strMessage, vchSig, key)) { + LogPrintf("CSporkMessage::Sign -- SignMessage() failed\n"); + return false; + } + + if(!darkSendSigner.VerifyMessage(pubkey, vchSig, strMessage, strError)) { + LogPrintf("CSporkMessage::Sign -- VerifyMessage() failed, error: %s\n", strError); + return false; + } + + return true; +} + +bool CSporkMessage::CheckSignature() +{ + //note: need to investigate why this is failing + std::string strError = ""; + std::string strMessage = boost::lexical_cast(nSporkID) + boost::lexical_cast(nValue) + boost::lexical_cast(nTimeSigned); + CPubKey pubkey(ParseHex(Params().SporkPubKey())); + + if(!darkSendSigner.VerifyMessage(pubkey, vchSig, strMessage, strError)) { + LogPrintf("CSporkMessage::CheckSignature -- VerifyMessage() failed, error: %s\n", strError); + return false; + } + + return true; +} + +void CSporkMessage::Relay() +{ + CInv inv(MSG_SPORK, GetHash()); + RelayInv(inv); +} diff --git a/src/spork.h b/src/spork.h new file mode 100644 index 0000000000..637e5afda2 --- /dev/null +++ b/src/spork.h @@ -0,0 +1,122 @@ +// Copyright (c) 2014-2017 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef SPORK_H +#define SPORK_H + +#include "hash.h" +#include "net.h" +#include "utilstrencodings.h" +#include "util.h" + +class CSporkMessage; + +/* + Don't ever reuse these IDs for other sporks + - This would result in old clients getting confused about which spork is for what +*/ +static const int SPORK_START = 10001; +static const int SPORK_END = 10013; + +static const int SPORK_2_INSTANTSEND_ENABLED = 10001; +static const int SPORK_3_INSTANTSEND_BLOCK_FILTERING = 10002; +static const int SPORK_5_INSTANTSEND_MAX_VALUE = 10004; +static const int SPORK_8_ZNODE_PAYMENT_ENFORCEMENT = 10007; +static const int SPORK_9_SUPERBLOCKS_ENABLED = 10008; +static const int SPORK_10_ZNODE_PAY_UPDATED_NODES = 10009; +static const int SPORK_12_RECONSIDER_BLOCKS = 10011; +static const int SPORK_13_OLD_SUPERBLOCK_FLAG = 10012; +static const int SPORK_14_REQUIRE_SENTINEL_FLAG = 10013; + +static const int64_t SPORK_2_INSTANTSEND_ENABLED_DEFAULT = 0; // ON +static const int64_t SPORK_3_INSTANTSEND_BLOCK_FILTERING_DEFAULT = 0; // ON +static const int64_t SPORK_5_INSTANTSEND_MAX_VALUE_DEFAULT = 1000; // 1000 DASH +static const int64_t SPORK_8_ZNODE_PAYMENT_ENFORCEMENT_DEFAULT = 4070908800ULL;// OFF +static const int64_t SPORK_9_SUPERBLOCKS_ENABLED_DEFAULT = 4070908800ULL;// OFF +static const int64_t SPORK_10_ZNODE_PAY_UPDATED_NODES_DEFAULT = 4070908800ULL;// OFF +static const int64_t SPORK_12_RECONSIDER_BLOCKS_DEFAULT = 0; // 0 BLOCKS +static const int64_t SPORK_13_OLD_SUPERBLOCK_FLAG_DEFAULT = 4070908800ULL;// OFF +static const int64_t SPORK_14_REQUIRE_SENTINEL_FLAG_DEFAULT = 4070908800ULL;// OFF + +extern std::map mapSporks; + +// +// Spork classes +// Keep track of all of the network spork settings +// + +class CSporkMessage +{ +private: + std::vector vchSig; + +public: + int nSporkID; + int64_t nValue; + int64_t nTimeSigned; + + CSporkMessage(int nSporkID, int64_t nValue, int64_t nTimeSigned) : + nSporkID(nSporkID), + nValue(nValue), + nTimeSigned(nTimeSigned) + {} + + CSporkMessage() : + nSporkID(0), + nValue(0), + nTimeSigned(0) + {} + + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(nSporkID); + READWRITE(nValue); + READWRITE(nTimeSigned); + READWRITE(vchSig); + } + + uint256 GetHash() const + { + CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); + ss << nSporkID; + ss << nValue; + ss << nTimeSigned; + return ss.GetHash(); + } + + bool Sign(std::string strSignKey); + bool CheckSignature(); + void Relay(); +}; + + +class CSporkManager +{ +private: + std::vector vchSig; + std::string strMasterPrivKey; + std::map mapSporksActive; + +public: + + CSporkManager() {} + + void ProcessSpork(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); + void ExecuteSpork(int nSporkID, int nValue); + bool UpdateSpork(int nSporkID, int64_t nValue); + + bool IsSporkActive(int nSporkID); + int64_t GetSporkValue(int nSporkID); + int GetSporkIDByName(std::string strName); + std::string GetSporkNameByID(int nSporkID); + + bool SetPrivKey(std::string strPrivKey); +}; + +extern CSporkManager sporkManager; + +#endif diff --git a/src/util.cpp b/src/util.cpp index 0fce422271..166e17fd87 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -99,6 +99,11 @@ namespace boost { using namespace std; +// znode fZnode +bool fZNode = false; +bool fLiteMode = false; +int nWalletBackups = 10; + const char * const BITCOIN_CONF_FILENAME = "zcoin.conf"; const char * const BITCOIN_PID_FILENAME = "zcoind.pid"; @@ -485,6 +490,34 @@ static boost::filesystem::path pathCached; static boost::filesystem::path pathCachedNetSpecific; static CCriticalSection csPathCached; +static boost::filesystem::path backupsDirCached; +static CCriticalSection csBackupsDirCached; + +const boost::filesystem::path &GetBackupsDir() +{ + namespace fs = boost::filesystem; + + LOCK(csBackupsDirCached); + + fs::path &backupsDir = backupsDirCached; + + if (!backupsDir.empty()) + return backupsDir; + + if (mapArgs.count("-walletbackupsdir")) { + backupsDir = fs::absolute(mapArgs["-walletbackupsdir"]); + // Path must exist + if (fs::is_directory(backupsDir)) return backupsDir; + // Fallback to default path if it doesn't + LogPrintf("%s: Warning: incorrect parameter -walletbackupsdir, path must exist! Using default path.\n", __func__); + strMiscWarning = _("Warning: incorrect parameter -walletbackupsdir, path must exist! Using default path."); + } + // Default path + backupsDir = GetDataDir() / "backups"; + + return backupsDir; +} + const boost::filesystem::path &GetDataDir(bool fNetSpecific) { namespace fs = boost::filesystem; @@ -530,6 +563,13 @@ boost::filesystem::path GetConfigFile() return pathConfigFile; } +boost::filesystem::path GetZnodeConfigFile() +{ + boost::filesystem::path pathConfigFile(GetArg("-mnconf", "znode.conf")); + if (!pathConfigFile.is_complete()) pathConfigFile = GetDataDir() / pathConfigFile; + return pathConfigFile; +} + void ReadConfigFile(map& mapSettingsRet, map >& mapMultiSettingsRet) { diff --git a/src/util.h b/src/util.h index 5c2e16dea1..3cd7ddcf6b 100644 --- a/src/util.h +++ b/src/util.h @@ -41,6 +41,9 @@ class CTranslationInterface /** Translate a message to the native language of the user. */ boost::signals2::signal Translate; }; +extern bool fZNode; +extern bool fLiteMode; +extern int nWalletBackups; extern std::map mapArgs; extern std::map > mapMultiArgs; @@ -133,8 +136,10 @@ bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest); bool TryCreateDirectory(const boost::filesystem::path& p); boost::filesystem::path GetDefaultDataDir(); const boost::filesystem::path &GetDataDir(bool fNetSpecific = true); +const boost::filesystem::path &GetBackupsDir(); void ClearDatadirCache(); boost::filesystem::path GetConfigFile(); +boost::filesystem::path GetZnodeConfigFile(); #ifndef WIN32 boost::filesystem::path GetPidFile(); void CreatePidFile(const boost::filesystem::path &path, pid_t pid); diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index 5d0a4a3305..122abb5c8d 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -133,6 +133,9 @@ class CCryptoKeyStore : public CBasicKeyStore //! keeps track of whether Unlock has run a thorough check before bool fDecryptionThoroughlyChecked; + //! if fOnlyMixingAllowed is true, only mixing should be allowed in unlocked wallet + bool fOnlyMixingAllowed; + protected: bool SetCrypted(); @@ -151,7 +154,7 @@ class CCryptoKeyStore : public CBasicKeyStore return fUseCrypto; } - bool IsLocked() const + bool IsLocked(bool fForMixing = false) const { if (!IsCrypted()) return false; @@ -160,6 +163,7 @@ class CCryptoKeyStore : public CBasicKeyStore LOCK(cs_KeyStore); result = vMasterKey.empty(); } +// if(!fForMixing && fOnlyMixingAllowed) return true; return result; } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 66f8d8cb1f..6488b46d99 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -25,6 +25,8 @@ #include "ui_interface.h" #include "utilmoneystr.h" #include "validation.h" +#include "darksend.h" +#include "random.h" #include #include @@ -255,6 +257,7 @@ bool CWallet::RemoveWatchOnly(const CScript &dest) { return true; } + bool CWallet::LoadWatchOnly(const CScript &dest) { return CCryptoKeyStore::AddWatchOnly(dest); } @@ -1677,6 +1680,126 @@ CAmount CWallet::GetBalance() const { return nTotal; } +CAmount CWallet::GetAnonymizableBalance(bool fSkipDenominated) const +{ + if(fLiteMode) return 0; + + std::vector vecTally; + if(!SelectCoinsGrouppedByAddresses(vecTally, fSkipDenominated)) return 0; + + CAmount nTotal = 0; + + BOOST_FOREACH(CompactTallyItem& item, vecTally) { + bool fIsDenominated = IsDenominatedAmount(item.nAmount); + if(fSkipDenominated && fIsDenominated) continue; + // assume that the fee to create denoms be PRIVATESEND_COLLATERAL at max + if(item.nAmount >= vecPrivateSendDenominations.back() + (fIsDenominated ? 0 : PRIVATESEND_COLLATERAL)) + nTotal += item.nAmount; + } + + return nTotal; +} + +CAmount CWallet::GetAnonymizedBalance() const +{ + if(fLiteMode) return 0; + + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + + if (pcoin->IsTrusted()) + nTotal += 0; +// nTotal += pcoin->GetAnonymizedCredit(); + } + } + + return nTotal; +} + +CAmount CWalletTx::GetAnonymizedCredit(bool fUseCache) const +{ + if (pwallet == 0) + return 0; + + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + +// if (fUseCache && fAnonymizedCreditCached) +// return nAnonymizedCreditCached; + + CAmount nCredit = 0; + uint256 hashTx = GetHash(); + for (unsigned int i = 0; i < vout.size(); i++) + { + const CTxOut &txout = vout[i]; + const CTxIn txin = CTxIn(hashTx, i); + + if(pwallet->IsSpent(hashTx, i) || !pwallet->IsDenominated(txin)) continue; + +// const int nRounds = pwallet->GetInputPrivateSendRounds(txin); + const int nRounds = 0; + if(nRounds >= nPrivateSendRounds){ + nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetAnonymizedCredit() : value out of range"); + } + } + +// nAnonymizedCreditCached = nCredit; +// fAnonymizedCreditCached = true; + return nCredit; +} + + +CAmount CWallet::GetNeedsToBeAnonymizedBalance(CAmount nMinBalance) const +{ + if(fLiteMode) return 0; + + CAmount nAnonymizedBalance = GetAnonymizedBalance(); + CAmount nNeedsToAnonymizeBalance = nPrivateSendAmount*COIN - nAnonymizedBalance; + + // try to overshoot target DS balance up to nMinBalance + nNeedsToAnonymizeBalance += nMinBalance; + + CAmount nAnonymizableBalance = GetAnonymizableBalance(); + + // anonymizable balance is way too small + if(nAnonymizableBalance < nMinBalance) return 0; + + // not enough funds to anonymze amount we want, try the max we can + if(nNeedsToAnonymizeBalance > nAnonymizableBalance) nNeedsToAnonymizeBalance = nAnonymizableBalance; + + // we should never exceed the pool max + if (nNeedsToAnonymizeBalance > PRIVATESEND_POOL_MAX) nNeedsToAnonymizeBalance = PRIVATESEND_POOL_MAX; + + return nNeedsToAnonymizeBalance; +} + +CAmount CWallet::GetDenominatedBalance(bool unconfirmed) const +{ + if(fLiteMode) return 0; + + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + +// nTotal += pcoin->GetDenominatedCredit(unconfirmed); + } + } + + return nTotal; +} + + + CAmount CWallet::GetUnconfirmedBalance() const { CAmount nTotal = 0; { @@ -1729,6 +1852,74 @@ CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const { return nTotal; } +bool CWallet::IsDenominated(const CTxIn &txin) const +{ + LOCK(cs_wallet); + + map::const_iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) { + const CWalletTx& prev = (*mi).second; + if (txin.prevout.n < prev.vout.size()) { + return IsDenominatedAmount(prev.vout[txin.prevout.n].nValue); + } + } + + return false; +} + +bool CWallet::IsDenominatedAmount(CAmount nInputAmount) const +{ +// BOOST_FOREACH(CAmount d, vecPrivateSendDenominations) +// if(nInputAmount == d) +// return true; + return false; +} + +int CWallet::CountInputsWithAmount(CAmount nInputAmount) +{ + CAmount nTotal = 0; + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsTrusted()){ + int nDepth = pcoin->GetDepthInMainChain(false); + + for (unsigned int i = 0; i < pcoin->vout.size(); i++) { + COutput out = COutput(pcoin, i, nDepth, true, true); + CTxIn txin = CTxIn(out.tx->GetHash(), out.i); + + if(out.tx->vout[out.i].nValue != nInputAmount) continue; + if(!IsDenominatedAmount(pcoin->vout[i].nValue)) continue; + if(IsSpent(out.tx->GetHash(), i) || IsMine(pcoin->vout[i]) != ISMINE_SPENDABLE || !IsDenominated(txin)) continue; + + nTotal++; + } + } + } + } + + return nTotal; +} + +bool CWallet::HasCollateralInputs(bool fOnlyConfirmed) const +{ + vector vCoins; + AvailableCoinsDash(vCoins, fOnlyConfirmed, NULL, false, ONLY_PRIVATESEND_COLLATERAL); + + return !vCoins.empty(); +} + + +bool CWallet::IsCollateralAmount(CAmount nInputAmount) const +{ + // collateral inputs should always be a 2x..4x of PRIVATESEND_COLLATERAL + return nInputAmount >= PRIVATESEND_COLLATERAL * 2 && + nInputAmount <= PRIVATESEND_COLLATERAL * 4 && + nInputAmount % PRIVATESEND_COLLATERAL == 0; +} + CAmount CWallet::GetImmatureWatchOnlyBalance() const { CAmount nTotal = 0; { @@ -1784,6 +1975,168 @@ void CWallet::AvailableCoins(vector &vCoins, bool fOnlyConfirmed, cons } } } + +void CWallet::AvailableCoinsDash(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue, AvailableCoinsType nCoinType, bool fUseInstantSend) const +{ + vCoins.clear(); + + { + LOCK2(cs_main, cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const uint256& wtxid = it->first; + const CWalletTx* pcoin = &(*it).second; + + if (!CheckFinalTx(*pcoin)) + continue; + + if (fOnlyConfirmed && !pcoin->IsTrusted()) + continue; + + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + continue; + + int nDepth = pcoin->GetDepthInMainChain(false); + // do not use IX for inputs that have less then INSTANTSEND_CONFIRMATIONS_REQUIRED blockchain confirmations +// if (fUseInstantSend && nDepth < INSTANTSEND_CONFIRMATIONS_REQUIRED) +// continue; + + // We should not consider coins which aren't at least in our mempool + // It's possible for these to be conflicted via ancestors which we may never be able to detect + if (nDepth == 0 && !pcoin->InMempool()) + continue; + + for (unsigned int i = 0; i < pcoin->vout.size(); i++) { + bool found = false; + if(nCoinType == ONLY_DENOMINATED) { + found = IsDenominatedAmount(pcoin->vout[i].nValue); + } else if(nCoinType == ONLY_NOT1000IFMN) { + found = !(fZNode && pcoin->vout[i].nValue == 1000*COIN); + } else if(nCoinType == ONLY_NONDENOMINATED_NOT1000IFMN) { + if (IsCollateralAmount(pcoin->vout[i].nValue)) continue; // do not use collateral amounts + found = !IsDenominatedAmount(pcoin->vout[i].nValue); + if(found && fZNode) found = pcoin->vout[i].nValue != 1000*COIN; // do not use Hot MN funds + } else if(nCoinType == ONLY_1000) { + found = pcoin->vout[i].nValue == 1000*COIN; + } else if(nCoinType == ONLY_PRIVATESEND_COLLATERAL) { + found = IsCollateralAmount(pcoin->vout[i].nValue); + } else { + found = true; + } + if(!found) continue; + + isminetype mine = IsMine(pcoin->vout[i]); + if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && + (!IsLockedCoin((*it).first, i) || nCoinType == ONLY_1000) && + (pcoin->vout[i].nValue > 0 || fIncludeZeroValue) && + (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i)))) + vCoins.push_back(COutput(pcoin, i, nDepth, + ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || + (coinControl && coinControl->fAllowWatchOnly && + (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO), + (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO)); + } + } + } +} + + +bool CWallet::SelectCoinsDark(CAmount nValueMin, CAmount nValueMax, std::vector& vecTxInRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax) const +{ + CCoinControl *coinControl=NULL; + + vecTxInRet.clear(); + nValueRet = 0; + + vector vCoins; + AvailableCoinsDash(vCoins, true, coinControl, false, nPrivateSendRoundsMin < 0 ? ONLY_NONDENOMINATED_NOT1000IFMN : ONLY_DENOMINATED); + + //order the array so largest nondenom are first, then denominations, then very small inputs. +// sort(vCoins.rbegin(), vCoins.rend(), CompareByPriority()); + + BOOST_FOREACH(const COutput& out, vCoins) + { + //do not allow inputs less than 1/10th of minimum value + if(out.tx->vout[out.i].nValue < nValueMin/10) continue; + //do not allow collaterals to be selected + if(IsCollateralAmount(out.tx->vout[out.i].nValue)) continue; + if(fZNode && out.tx->vout[out.i].nValue == 1000*COIN) continue; //znode input + + if(nValueRet + out.tx->vout[out.i].nValue <= nValueMax){ + CTxIn txin = CTxIn(out.tx->GetHash(),out.i); + +// int nRounds = GetInputPrivateSendRounds(txin); + int nRounds = 0; + if(nRounds >= nPrivateSendRoundsMax) continue; + if(nRounds < nPrivateSendRoundsMin) continue; + + txin.prevPubKey = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey + nValueRet += out.tx->vout[out.i].nValue; + vecTxInRet.push_back(txin); + } + } + + return nValueRet >= nValueMin; +} + +// znode +bool CWallet::GetZnodeVinAndKeys(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; + AvailableCoinsDash(vPossibleCoins, true, NULL, false, ONLY_1000); + if(vPossibleCoins.empty()) { + LogPrintf("CWallet::GetZnodeVinAndKeys -- Could not locate any valid znode 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::GetZnodeVinAndKeys -- Could not locate specified znode 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; +} + //[zcoin] void CWallet::ListAvailableCoinsMintCoins(vector &vCoins, bool fOnlyConfirmed) const { vCoins.clear(); @@ -2112,6 +2465,205 @@ bool CWallet::FundTransaction(CMutableTransaction &tx, CAmount &nFeeRet, bool ov return true; } +bool CWallet::ConvertList(std::vector vecTxIn, std::vector& vecAmounts) +{ + BOOST_FOREACH(CTxIn txin, vecTxIn) { + if (mapWallet.count(txin.prevout.hash)) { + CWalletTx& wtx = mapWallet[txin.prevout.hash]; + if(txin.prevout.n < wtx.vout.size()){ + vecAmounts.push_back(wtx.vout[txin.prevout.n].nValue); + } + } else { + LogPrintf("CWallet::ConvertList -- Couldn't find transaction\n"); + } + } + return true; +} + +bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount nValueMax, std::vector& vecTxInRet, std::vector& vCoinsRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax) +{ + vecTxInRet.clear(); + vCoinsRet.clear(); + nValueRet = 0; + + vector vCoins; + AvailableCoinsDash(vCoins, true, NULL, false, ONLY_DENOMINATED); + + std::random_shuffle(vCoins.rbegin(), vCoins.rend(), GetRandInt); + + // ( bit on if present ) + // bit 0 - 100DASH+1 + // bit 1 - 10DASH+1 + // bit 2 - 1DASH+1 + // bit 3 - .1DASH+1 + + std::vector vecBits; + if (!darkSendPool.GetDenominationsBits(nDenom, vecBits)) { + return false; + } + + int nDenomResult = 0; + +// InsecureRand insecureRand; + BOOST_FOREACH(const COutput& out, vCoins) + { + // znode-like input should not be selected by AvailableCoins now anyway + //if(out.tx->vout[out.i].nValue == 1000*COIN) continue; + if(nValueRet + out.tx->vout[out.i].nValue <= nValueMax){ + + CTxIn txin = CTxIn(out.tx->GetHash(), out.i); + + int nRounds = 0; +// int nRounds = GetInputPrivateSendRounds(txin); + if(nRounds >= nPrivateSendRoundsMax) continue; + if(nRounds < nPrivateSendRoundsMin) continue; + + BOOST_FOREACH(int nBit, vecBits) { + if(out.tx->vout[out.i].nValue == vecPrivateSendDenominations[nBit]) { + if(nValueRet >= nValueMin) { + //randomly reduce the max amount we'll submit (for anonymity) +// nValueMax -= (nValueMax/5); + nValueMax -= nValueMax; + //on average use 50% of the inputs or less +// int r = insecureRand(vCoins.size()); + int r = vCoins.size(); + if((int)vecTxInRet.size() > r) return true; + } + txin.prevPubKey = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey + nValueRet += out.tx->vout[out.i].nValue; + vecTxInRet.push_back(txin); + vCoinsRet.push_back(out); + nDenomResult |= 1 << nBit; + } + } + } + } + + return nValueRet >= nValueMin && nDenom == nDenomResult; +} + +bool CWallet::CreateCollateralTransaction(CMutableTransaction& txCollateral, std::string& strReason) +{ + txCollateral.vin.clear(); + txCollateral.vout.clear(); + + CReserveKey reservekey(this); + CAmount nValue = 0; + CTxIn txinCollateral; + +// if (!GetCollateralTxIn(txinCollateral, nValue)) { +// strReason = "PrivateSend requires a collateral transaction and could not locate an acceptable input!"; +// return false; +// } + + // make our change address + CScript scriptChange; + CPubKey vchPubKey; + assert(reservekey.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked + scriptChange = GetScriptForDestination(vchPubKey.GetID()); + reservekey.KeepKey(); + + txCollateral.vin.push_back(txinCollateral); + + //pay collateral charge in fees + CTxOut txout = CTxOut(nValue - PRIVATESEND_COLLATERAL, scriptChange); + txCollateral.vout.push_back(txout); + + if(!SignSignature(*this, txinCollateral.prevPubKey, txCollateral, 0, NULL, int(SIGHASH_ALL|SIGHASH_ANYONECANPAY))) { + strReason = "Unable to sign collateral transaction!"; + return false; + } + + return true; +} + +bool CWallet::SelectCoinsGrouppedByAddresses(std::vector& vecTallyRet, bool fSkipDenominated, bool fAnonymizable) const +{ + LOCK2(cs_main, cs_wallet); + + isminefilter filter = ISMINE_SPENDABLE; + + // try to use cache + if(fAnonymizable) { +// if(fSkipDenominated && fAnonymizableTallyCachedNonDenom) { +// vecTallyRet = vecAnonymizableTallyCachedNonDenom; +// LogPrint("selectcoins", "SelectCoinsGrouppedByAddresses - using cache for non-denom inputs\n"); +// return vecTallyRet.size() > 0; +// } +// if(!fSkipDenominated && fAnonymizableTallyCached) { +// vecTallyRet = vecAnonymizableTallyCached; +// LogPrint("selectcoins", "SelectCoinsGrouppedByAddresses - using cache for all inputs\n"); +// return vecTallyRet.size() > 0; +// } + } + + // Tally + map mapTally; + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { + const CWalletTx& wtx = (*it).second; + + if(wtx.IsCoinBase() && wtx.GetBlocksToMaturity() > 0) continue; + if(!fAnonymizable && !wtx.IsTrusted()) continue; + + for (unsigned int i = 0; i < wtx.vout.size(); i++) { + CTxDestination address; + if (!ExtractDestination(wtx.vout[i].scriptPubKey, address)) continue; + + isminefilter mine = ::IsMine(*this, address); + if(!(mine & filter)) continue; + + if(IsSpent(wtx.GetHash(), i) || IsLockedCoin(wtx.GetHash(), i)) continue; + + if(fSkipDenominated && IsDenominatedAmount(wtx.vout[i].nValue)) continue; + + if(fAnonymizable) { + // ignore collaterals + if(IsCollateralAmount(wtx.vout[i].nValue)) continue; + if(fZNode && wtx.vout[i].nValue == 1000*COIN) continue; + // ignore outputs that are 10 times smaller then the smallest denomination + // otherwise they will just lead to higher fee / lower priority + if(wtx.vout[i].nValue <= vecPrivateSendDenominations.back()/10) continue; + // ignore anonymized +// if(GetInputPrivateSendRounds(CTxIn(wtx.GetHash(), i)) >= nPrivateSendRounds) continue; + } + + CompactTallyItem& item = mapTally[address]; + item.address = address; + item.nAmount += wtx.vout[i].nValue; + item.vecTxIn.push_back(CTxIn(wtx.GetHash(), i)); + } + } + + // construct resulting vector + vecTallyRet.clear(); + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CompactTallyItem)& item, mapTally) { + if(fAnonymizable && item.second.nAmount < vecPrivateSendDenominations.back()) continue; + vecTallyRet.push_back(item.second); + } + + // order by amounts per address, from smallest to largest +// sort(vecTallyRet.rbegin(), vecTallyRet.rend(), CompareByAmount()); + + // cache anonymizable for later use + if(fAnonymizable) { + if(fSkipDenominated) { +// vecAnonymizableTallyCachedNonDenom = vecTallyRet; +// fAnonymizableTallyCachedNonDenom = true; + } else { +// vecAnonymizableTallyCached = vecTallyRet; +// fAnonymizableTallyCached = true; + } + } + + // debug + std::string strMessage = "SelectCoinsGrouppedByAddresses - vecTallyRet:\n"; + BOOST_FOREACH(CompactTallyItem& item, vecTallyRet) + strMessage += strprintf(" %s %f\n", item.address.ToString().c_str(), float(item.nAmount)/COIN); + LogPrint("selectcoins", "%s", strMessage); + + return vecTallyRet.size() > 0; +} + bool CWallet::CreateTransaction(const vector &vecSend, CWalletTx &wtxNew, CReserveKey &reservekey, CAmount &nFeeRet, int &nChangePosInOut, std::string &strFailReason, const CCoinControl *coinControl, @@ -4637,6 +5189,39 @@ int CMerkleTx::SetMerkleBranch(const CBlock &block) { return chainActive.Height() - pindex->nHeight + 1; } +int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet, bool enableIX) const +{ + int nResult; + + if (hashUnset()) + nResult = 0; + else { + AssertLockHeld(cs_main); + + // Find the block it claims to be in + BlockMap::iterator mi = mapBlockIndex.find(hashBlock); + if (mi == mapBlockIndex.end()) + nResult = 0; + else { + CBlockIndex* pindex = (*mi).second; + if (!pindex || !chainActive.Contains(pindex)) + nResult = 0; + else { + pindexRet = pindex; + nResult = ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1); + + if (nResult == 0 && !mempool.exists(GetHash())) + return -1; // Not in chain, not in mempool + } + } + } + +// if(enableIX && nResult < 6 && instantsend.IsLockedInstantSendTransaction(GetHash())) +// return nInstantSendDepth + nResult; + + return nResult; +} + int CMerkleTx::GetDepthInMainChain(const CBlockIndex *&pindexRet) const { if (hashUnset()) return 0; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index d76e1fe87b..581f3c722d 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -17,6 +17,7 @@ #include "wallet/crypter.h" #include "wallet/walletdb.h" #include "wallet/rpcwallet.h" +#include "../base58.h" #include #include @@ -85,6 +86,27 @@ enum WalletFeature FEATURE_LATEST = FEATURE_COMPRPUBKEY // HD is optional, use FEATURE_COMPRPUBKEY as latest version }; +enum AvailableCoinsType +{ + ALL_COINS = 1, + ONLY_DENOMINATED = 2, + ONLY_NOT1000IFMN = 3, + ONLY_NONDENOMINATED_NOT1000IFMN = 4, + ONLY_1000 = 5, // find znode outputs including locked ones (use with caution) + ONLY_PRIVATESEND_COLLATERAL = 6 +}; + +struct CompactTallyItem +{ + CBitcoinAddress address; + CAmount nAmount; + std::vector vecTxIn; + CompactTallyItem() + { + nAmount = 0; + } +}; + /** A key pool entry */ class CKeyPool @@ -213,6 +235,10 @@ class CMerkleTx : public CTransaction */ int GetDepthInMainChain(const CBlockIndex* &pindexRet) const; int GetDepthInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); } + + int GetDepthInMainChain(const CBlockIndex* &pindexRet, bool enableIX) const; + int GetDepthInMainChain(bool enableIX) const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet, enableIX); } + bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet) > 0; } int GetBlocksToMaturity() const; /** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */ @@ -382,6 +408,7 @@ class CWalletTx : public CMerkleTx CAmount GetAvailableCredit(bool fUseCache=true) const; CAmount GetImmatureWatchOnlyCredit(const bool& fUseCache=true) const; CAmount GetAvailableWatchOnlyCredit(const bool& fUseCache=true) const; + CAmount GetAnonymizedCredit(bool fUseCache=true) const; CAmount GetChange() const; void GetAmounts(std::list& listReceived, @@ -599,6 +626,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface std::set setKeyPool; std::map mapKeyMetadata; + //znode + int64_t nKeysLeftSinceAutoBackup; typedef std::map MasterKeyMap; MasterKeyMap mapMasterKeys; @@ -664,6 +693,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface * populate vCoins with vector of available COutputs. */ void AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl = NULL, bool fIncludeZeroValue=false) const; + void AvailableCoinsDash(std::vector& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl = NULL, bool fIncludeZeroValue=false, AvailableCoinsType nCoinType=ALL_COINS, bool fUseInstantSend = false) const; /** * Shuffle and select coins until nTargetValue is reached while avoiding @@ -672,6 +702,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface * assembled */ bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, uint64_t nMaxAncestors, std::vector vCoins, std::set >& setCoinsRet, CAmount& nValueRet) const; + bool SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount nValueMax, std::vector& vecTxInRet, std::vector& vCoinsRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax); + bool SelectCoinsDark(CAmount nValueMin, CAmount nValueMax, std::vector& vecTxInRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax) const; + bool SelectCoinsGrouppedByAddresses(std::vector& vecTallyRet, bool fSkipDenominated = true, bool fAnonymizable = true) const; bool IsSpent(const uint256& hash, unsigned int n) const; @@ -681,6 +714,14 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface void UnlockAllCoins(); void ListLockedCoins(std::vector& vOutpts); + // znode + /// Get 1000DASH output and keys which can be used for the Znode + bool GetZnodeVinAndKeys(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 HasCollateralInputs(bool fOnlyConfirmed = true) const; + int CountInputsWithAmount(CAmount nInputAmount); + /** * keystore implementation * Generate a new key @@ -745,7 +786,16 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface CAmount GetWatchOnlyBalance() const; CAmount GetUnconfirmedWatchOnlyBalance() const; CAmount GetImmatureWatchOnlyBalance() const; - + // znode + bool IsDenominated(const CTxIn &txin) const; + bool IsDenominatedAmount(CAmount nInputAmount) const; + bool IsCollateralAmount(CAmount nInputAmount) const; + CAmount GetAnonymizableBalance(bool fSkipDenominated = false) const; + CAmount GetAnonymizedBalance() const; +// double GetAverageAnonymizedRounds() const; +// CAmount GetNormalizedAnonymizedBalance() const; + CAmount GetNeedsToBeAnonymizedBalance(CAmount nMinBalance = 0) const; + CAmount GetDenominatedBalance(bool unconfirmed=false) const; /** * Insert additional inputs into the transaction by * calling CreateTransaction(); @@ -782,6 +832,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); + bool CreateCollateralTransaction(CMutableTransaction& txCollateral, std::string& strReason); + bool ConvertList(std::vector vecTxIn, std::vector& vecAmounts); + bool AddAccountingEntry(const CAccountingEntry&, CWalletDB & pwalletdb); static CFeeRate minTxFee; diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index c444c54ef3..5469bf8dc1 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -928,6 +928,125 @@ void ThreadFlushWalletDB(const string &strFile) { } } +// This should be called carefully: +// either supply "wallet" (if already loaded) or "strWalletFile" (if wallet wasn't loaded yet) +bool AutoBackupWallet (CWallet* wallet, std::string strWalletFile, std::string& strBackupWarning, std::string& strBackupError) +{ + namespace fs = boost::filesystem; + + strBackupWarning = strBackupError = ""; + + if(nWalletBackups > 0) + { + fs::path backupsDir = GetBackupsDir(); + + if (!fs::exists(backupsDir)) + { + // Always create backup folder to not confuse the operating system's file browser + LogPrintf("Creating backup folder %s\n", backupsDir.string()); + if(!fs::create_directories(backupsDir)) { + // smth is wrong, we shouldn't continue until it's resolved + strBackupError = strprintf(_("Wasn't able to create wallet backup folder %s!"), backupsDir.string()); + LogPrintf("%s\n", strBackupError); + nWalletBackups = -1; + return false; + } + } + + // Create backup of the ... + std::string dateTimeStr = DateTimeStrFormat(".%Y-%m-%d-%H-%M", GetTime()); + if (wallet) + { + // ... opened wallet + LOCK2(cs_main, wallet->cs_wallet); + strWalletFile = wallet->strWalletFile; + fs::path backupFile = backupsDir / (strWalletFile + dateTimeStr); +// if(!BackupWallet(*wallet, backupFile.string())) { +// strBackupWarning = strprintf(_("Failed to create backup %s!"), backupFile.string()); +// LogPrintf("%s\n", strBackupWarning); +// nWalletBackups = -1; +// return false; +// } + // Update nKeysLeftSinceAutoBackup using current pool size + wallet->nKeysLeftSinceAutoBackup = wallet->GetKeyPoolSize(); + LogPrintf("nKeysLeftSinceAutoBackup: %d\n", wallet->nKeysLeftSinceAutoBackup); + if(wallet->IsLocked(true)) { + strBackupWarning = _("Wallet is locked, can't replenish keypool! Automatic backups and mixing are disabled, please unlock your wallet to replenish keypool."); + LogPrintf("%s\n", strBackupWarning); + nWalletBackups = -2; + return false; + } + } else { + // ... strWalletFile file + fs::path sourceFile = GetDataDir() / strWalletFile; + fs::path backupFile = backupsDir / (strWalletFile + dateTimeStr); + sourceFile.make_preferred(); + backupFile.make_preferred(); + if (fs::exists(backupFile)) + { + strBackupWarning = _("Failed to create backup, file already exists! This could happen if you restarted wallet in less than 60 seconds. You can continue if you are ok with this."); + LogPrintf("%s\n", strBackupWarning); + return false; + } + if(fs::exists(sourceFile)) { + try { + fs::copy_file(sourceFile, backupFile); + LogPrintf("Creating backup of %s -> %s\n", sourceFile.string(), backupFile.string()); + } catch(fs::filesystem_error &error) { + strBackupWarning = strprintf(_("Failed to create backup, error: %s"), error.what()); + LogPrintf("%s\n", strBackupWarning); + nWalletBackups = -1; + return false; + } + } + } + + // Keep only the last 10 backups, including the new one of course + typedef std::multimap folder_set_t; + folder_set_t folder_set; + fs::directory_iterator end_iter; + backupsDir.make_preferred(); + // Build map of backup files for current(!) wallet sorted by last write time + fs::path currentFile; + for (fs::directory_iterator dir_iter(backupsDir); dir_iter != end_iter; ++dir_iter) + { + // Only check regular files + if ( fs::is_regular_file(dir_iter->status())) + { + currentFile = dir_iter->path().filename(); + // Only add the backups for the current wallet, e.g. wallet.dat.* + if(dir_iter->path().stem().string() == strWalletFile) + { + folder_set.insert(folder_set_t::value_type(fs::last_write_time(dir_iter->path()), *dir_iter)); + } + } + } + + // Loop backward through backup files and keep the N newest ones (1 <= N <= 10) + int counter = 0; + BOOST_REVERSE_FOREACH(PAIRTYPE(const std::time_t, fs::path) file, folder_set) + { + counter++; + if (counter > nWalletBackups) + { + // More than nWalletBackups backups: delete oldest one(s) + try { + fs::remove(file.second); + LogPrintf("Old backup deleted: %s\n", file.second); + } catch(fs::filesystem_error &error) { + strBackupWarning = strprintf(_("Failed to delete backup, error: %s"), error.what()); + LogPrintf("%s\n", strBackupWarning); + return false; + } + } + } + return true; + } + + LogPrintf("Automatic wallet backups are disabled!\n"); + return false; +} + // // Try to (very carefully!) recover wallet file if there is a problem. // diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 2cc0aea840..d5eaac14fd 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -206,5 +206,6 @@ class CWalletDB : public CDB }; void ThreadFlushWalletDB(const std::string& strFile); +bool AutoBackupWallet (CWallet* wallet, std::string strWalletFile, std::string& strBackupWarning, std::string& strBackupError); #endif // BITCOIN_WALLET_WALLETDB_H diff --git a/src/znode-payments.cpp b/src/znode-payments.cpp new file mode 100644 index 0000000000..96f7e9e6c8 --- /dev/null +++ b/src/znode-payments.cpp @@ -0,0 +1,958 @@ +// Copyright (c) 2014-2017 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "activeznode.h" +#include "darksend.h" +//#include "governance-classes.h" +#include "znode-payments.h" +#include "znode-sync.h" +#include "znodeman.h" +//#include "netfulfilledman.h" +//#include "spork.h" +#include "util.h" + +#include + +/** Object for who's going to get paid on which blocks */ +CZnodePayments mnpayments; + +CCriticalSection cs_vecPayees; +CCriticalSection cs_mapZnodeBlocks; +CCriticalSection cs_mapZnodePaymentVotes; + +/** +* IsBlockValueValid +* +* Determine if coinbase outgoing created money is the correct value +* +* Why is this needed? +* - In Dash some blocks are superblocks, which output much higher amounts of coins +* - Otherblocks are 10% lower in outgoing value, so in total, no extra coins are created +* - When non-superblocks are detected, the normal schedule should be maintained +*/ + +bool IsBlockValueValid(const CBlock& block, int nBlockHeight, CAmount blockReward, std::string &strErrorRet) +{ + strErrorRet = ""; + + bool isBlockRewardValueMet = (block.vtx[0].GetValueOut() <= blockReward); + if(fDebug) LogPrintf("block.vtx[0].GetValueOut() %lld <= blockReward %lld\n", block.vtx[0].GetValueOut(), blockReward); + + // we are still using budgets, but we have no data about them anymore, + // all we know is predefined budget cycle and window + + const Consensus::Params& consensusParams = Params().GetConsensus(); + + if(nBlockHeight < consensusParams.nSuperblockStartBlock) { + int nOffset = nBlockHeight % consensusParams.nBudgetPaymentsCycleBlocks; + if(nBlockHeight >= consensusParams.nBudgetPaymentsStartBlock && + nOffset < consensusParams.nBudgetPaymentsWindowBlocks) { + // NOTE: make sure SPORK_13_OLD_SUPERBLOCK_FLAG is disabled when 12.1 starts to go live +// if(znodeSync.IsSynced() && !sporkManager.IsSporkActive(SPORK_13_OLD_SUPERBLOCK_FLAG)) { +// // no budget blocks should be accepted here, if SPORK_13_OLD_SUPERBLOCK_FLAG is disabled +// LogPrint("gobject", "IsBlockValueValid -- Client synced but budget spork is disabled, checking block value against block reward\n"); +// if(!isBlockRewardValueMet) { +// strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, budgets are disabled", +// nBlockHeight, block.vtx[0].GetValueOut(), blockReward); +// } +// return isBlockRewardValueMet; +// } + LogPrint("gobject", "IsBlockValueValid -- WARNING: Skipping budget block value checks, accepting block\n"); + // TODO: reprocess blocks to make sure they are legit? + return true; + } + // LogPrint("gobject", "IsBlockValueValid -- Block is not in budget cycle window, checking block value against block reward\n"); + if(!isBlockRewardValueMet) { + strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, block is not in budget cycle window", + nBlockHeight, block.vtx[0].GetValueOut(), blockReward); + } + return isBlockRewardValueMet; + } + + // superblocks started + +// CAmount nSuperblockMaxValue = blockReward + CSuperblock::GetPaymentsLimit(nBlockHeight); +// bool isSuperblockMaxValueMet = (block.vtx[0].GetValueOut() <= nSuperblockMaxValue); +// bool isSuperblockMaxValueMet = false; + +// LogPrint("gobject", "block.vtx[0].GetValueOut() %lld <= nSuperblockMaxValue %lld\n", block.vtx[0].GetValueOut(), nSuperblockMaxValue); + + if(!znodeSync.IsSynced()) { + // not enough data but at least it must NOT exceed superblock max value +// if(CSuperblock::IsValidBlockHeight(nBlockHeight)) { +// if(fDebug) LogPrintf("IsBlockPayeeValid -- WARNING: Client not synced, checking superblock max bounds only\n"); +// if(!isSuperblockMaxValueMet) { +// strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded superblock max value", +// nBlockHeight, block.vtx[0].GetValueOut(), nSuperblockMaxValue); +// } +// return isSuperblockMaxValueMet; +// } + if(!isBlockRewardValueMet) { + strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, only regular blocks are allowed at this height", + nBlockHeight, block.vtx[0].GetValueOut(), blockReward); + } + // it MUST be a regular block otherwise + return isBlockRewardValueMet; + } + + // we are synced, let's try to check as much data as we can + +// if(sporkManager.IsSporkActive(SPORK_9_SUPERBLOCKS_ENABLED)) { +//// if(CSuperblockManager::IsSuperblockTriggered(nBlockHeight)) { +//// if(CSuperblockManager::IsValid(block.vtx[0], nBlockHeight, blockReward)) { +//// LogPrint("gobject", "IsBlockValueValid -- Valid superblock at height %d: %s", nBlockHeight, block.vtx[0].ToString()); +//// // all checks are done in CSuperblock::IsValid, nothing to do here +//// return true; +//// } +//// +//// // triggered but invalid? that's weird +//// LogPrintf("IsBlockValueValid -- ERROR: Invalid superblock detected at height %d: %s", nBlockHeight, block.vtx[0].ToString()); +//// // should NOT allow invalid superblocks, when superblocks are enabled +//// strErrorRet = strprintf("invalid superblock detected at height %d", nBlockHeight); +//// return false; +//// } +// LogPrint("gobject", "IsBlockValueValid -- No triggered superblock detected at height %d\n", nBlockHeight); +// if(!isBlockRewardValueMet) { +// strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, no triggered superblock detected", +// nBlockHeight, block.vtx[0].GetValueOut(), blockReward); +// } +// } else { +// // should NOT allow superblocks at all, when superblocks are disabled +// LogPrint("gobject", "IsBlockValueValid -- Superblocks are disabled, no superblocks allowed\n"); +// if(!isBlockRewardValueMet) { +// strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, superblocks are disabled", +// nBlockHeight, block.vtx[0].GetValueOut(), blockReward); +// } +// } + + // it MUST be a regular block + return isBlockRewardValueMet; +} + +bool IsBlockPayeeValid(const CTransaction& txNew, int nBlockHeight, CAmount blockReward) +{ + if(!znodeSync.IsSynced()) { + //there is no budget data to use to check anything, let's just accept the longest chain + if(fDebug) LogPrintf("IsBlockPayeeValid -- WARNING: Client not synced, skipping block payee checks\n"); + return true; + } + + // we are still using budgets, but we have no data about them anymore, + // we can only check znode payments + + const Consensus::Params& consensusParams = Params().GetConsensus(); + + if(nBlockHeight < consensusParams.nSuperblockStartBlock) { + if(mnpayments.IsTransactionValid(txNew, nBlockHeight)) { + LogPrint("mnpayments", "IsBlockPayeeValid -- Valid znode payment at height %d: %s", nBlockHeight, txNew.ToString()); + return true; + } + + int nOffset = nBlockHeight % consensusParams.nBudgetPaymentsCycleBlocks; + if(nBlockHeight >= consensusParams.nBudgetPaymentsStartBlock && + nOffset < consensusParams.nBudgetPaymentsWindowBlocks) { +// if(!sporkManager.IsSporkActive(SPORK_13_OLD_SUPERBLOCK_FLAG)) { +// // no budget blocks should be accepted here, if SPORK_13_OLD_SUPERBLOCK_FLAG is disabled +// LogPrint("gobject", "IsBlockPayeeValid -- ERROR: Client synced but budget spork is disabled and znode payment is invalid\n"); +// return false; +// } + // NOTE: this should never happen in real, SPORK_13_OLD_SUPERBLOCK_FLAG MUST be disabled when 12.1 starts to go live + LogPrint("gobject", "IsBlockPayeeValid -- WARNING: Probably valid budget block, have no data, accepting\n"); + // TODO: reprocess blocks to make sure they are legit? + return true; + } + +// if(sporkManager.IsSporkActive(SPORK_8_ZNODE_PAYMENT_ENFORCEMENT)) { +// LogPrintf("IsBlockPayeeValid -- ERROR: Invalid znode payment detected at height %d: %s", nBlockHeight, txNew.ToString()); +// return false; +// } + + LogPrintf("IsBlockPayeeValid -- WARNING: Znode payment enforcement is disabled, accepting any payee\n"); + return true; + } + + // superblocks started + // SEE IF THIS IS A VALID SUPERBLOCK + +// if(sporkManager.IsSporkActive(SPORK_9_SUPERBLOCKS_ENABLED)) { +//// if(CSuperblockManager::IsSuperblockTriggered(nBlockHeight)) { +//// if(CSuperblockManager::IsValid(txNew, nBlockHeight, blockReward)) { +//// LogPrint("gobject", "IsBlockPayeeValid -- Valid superblock at height %d: %s", nBlockHeight, txNew.ToString()); +//// return true; +//// } +//// +//// LogPrintf("IsBlockPayeeValid -- ERROR: Invalid superblock detected at height %d: %s", nBlockHeight, txNew.ToString()); +//// // should NOT allow such superblocks, when superblocks are enabled +//// return false; +//// } +// // continue validation, should pay MN +// LogPrint("gobject", "IsBlockPayeeValid -- No triggered superblock detected at height %d\n", nBlockHeight); +// } else { +// // should NOT allow superblocks at all, when superblocks are disabled +// LogPrint("gobject", "IsBlockPayeeValid -- Superblocks are disabled, no superblocks allowed\n"); +// } + + // IF THIS ISN'T A SUPERBLOCK OR SUPERBLOCK IS INVALID, IT SHOULD PAY A ZNODE DIRECTLY + if(mnpayments.IsTransactionValid(txNew, nBlockHeight)) { + LogPrint("mnpayments", "IsBlockPayeeValid -- Valid znode payment at height %d: %s", nBlockHeight, txNew.ToString()); + return true; + } + +// if(sporkManager.IsSporkActive(SPORK_8_ZNODE_PAYMENT_ENFORCEMENT)) { +// LogPrintf("IsBlockPayeeValid -- ERROR: Invalid znode payment detected at height %d: %s", nBlockHeight, txNew.ToString()); +// return false; +// } + + LogPrintf("IsBlockPayeeValid -- WARNING: Znode payment enforcement is disabled, accepting any payee\n"); + return true; +} + +void FillBlockPayments(CMutableTransaction& txNew, int nBlockHeight, CAmount blockReward, CTxOut& txoutZnodeRet, std::vector& voutSuperblockRet) +{ + // only create superblocks if spork is enabled AND if superblock is actually triggered + // (height should be validated inside) +// if(sporkManager.IsSporkActive(SPORK_9_SUPERBLOCKS_ENABLED) && +// CSuperblockManager::IsSuperblockTriggered(nBlockHeight)) { +// LogPrint("gobject", "FillBlockPayments -- triggered superblock creation at height %d\n", nBlockHeight); +// CSuperblockManager::CreateSuperblock(txNew, nBlockHeight, voutSuperblockRet); +// return; +// } + + // FILL BLOCK PAYEE WITH ZNODE PAYMENT OTHERWISE + mnpayments.FillBlockPayee(txNew, nBlockHeight, blockReward, txoutZnodeRet); + LogPrint("mnpayments", "FillBlockPayments -- nBlockHeight %d blockReward %lld txoutZnodeRet %s txNew %s", + nBlockHeight, blockReward, txoutZnodeRet.ToString(), txNew.ToString()); +} + +std::string GetRequiredPaymentsString(int nBlockHeight) +{ + // IF WE HAVE A ACTIVATED TRIGGER FOR THIS HEIGHT - IT IS A SUPERBLOCK, GET THE REQUIRED PAYEES +// if(CSuperblockManager::IsSuperblockTriggered(nBlockHeight)) { +// return CSuperblockManager::GetRequiredPaymentsString(nBlockHeight); +// } + + // OTHERWISE, PAY ZNODE + return mnpayments.GetRequiredPaymentsString(nBlockHeight); +} + +void CZnodePayments::Clear() +{ + LOCK2(cs_mapZnodeBlocks, cs_mapZnodePaymentVotes); + mapZnodeBlocks.clear(); + mapZnodePaymentVotes.clear(); +} + +bool CZnodePayments::CanVote(COutPoint outZnode, int nBlockHeight) +{ + LOCK(cs_mapZnodePaymentVotes); + + if (mapZnodesLastVote.count(outZnode) && mapZnodesLastVote[outZnode] == nBlockHeight) { + return false; + } + + //record this znode voted + mapZnodesLastVote[outZnode] = nBlockHeight; + return true; +} + +/** +* FillBlockPayee +* +* Fill Znode ONLY payment block +*/ + +void CZnodePayments::FillBlockPayee(CMutableTransaction& txNew, int nBlockHeight, CAmount blockReward, CTxOut& txoutZnodeRet) +{ + // make sure it's not filled yet + txoutZnodeRet = CTxOut(); + + CScript payee; + + if(!mnpayments.GetBlockPayee(nBlockHeight, payee)) { + // no znode detected... + int nCount = 0; + CZnode *winningNode = mnodeman.GetNextZnodeInQueueForPayment(nBlockHeight, true, nCount); + if(!winningNode) { + // ...and we can't calculate it on our own + LogPrintf("CZnodePayments::FillBlockPayee -- Failed to detect znode to pay\n"); + return; + } + // fill payee with locally calculated winner and hope for the best + payee = GetScriptForDestination(winningNode->pubKeyCollateralAddress.GetID()); + } + + // GET ZNODE PAYMENT VARIABLES SETUP + CAmount znodePayment = GetZnodePayment(nBlockHeight, blockReward); + + // split reward between miner ... + txNew.vout[0].nValue -= znodePayment; + // ... and znode + txoutZnodeRet = CTxOut(znodePayment, payee); + txNew.vout.push_back(txoutZnodeRet); + + CTxDestination address1; + ExtractDestination(payee, address1); + CBitcoinAddress address2(address1); + + LogPrintf("CZnodePayments::FillBlockPayee -- Znode payment %lld to %s\n", znodePayment, address2.ToString()); +} + +int CZnodePayments::GetMinZnodePaymentsProto() { +// return sporkManager.IsSporkActive(SPORK_10_ZNODE_PAY_UPDATED_NODES) +// ? MIN_ZNODE_PAYMENT_PROTO_VERSION_2 +// : MIN_ZNODE_PAYMENT_PROTO_VERSION_1; + + return MIN_ZNODE_PAYMENT_PROTO_VERSION_1; +} + +void CZnodePayments::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) +{ + // Ignore any payments messages until znode list is synced + if(!znodeSync.IsZnodeListSynced()) return; + + if(fLiteMode) return; // disable all Dash specific functionality + + if (strCommand == NetMsgType::ZNODEPAYMENTSYNC) { //Znode Payments Request Sync + + // Ignore such requests until we are fully synced. + // We could start processing this after znode list is synced + // but this is a heavy one so it's better to finish sync first. + if (!znodeSync.IsSynced()) return; + + int nCountNeeded; + vRecv >> nCountNeeded; + +// if(netfulfilledman.HasFulfilledRequest(pfrom->addr, NetMsgType::ZNODEPAYMENTSYNC)) { +// // Asking for the payments list multiple times in a short period of time is no good +// LogPrintf("ZNODEPAYMENTSYNC -- peer already asked me for the list, peer=%d\n", pfrom->id); +// Misbehaving(pfrom->GetId(), 20); +// return; +// } +// netfulfilledman.AddFulfilledRequest(pfrom->addr, NetMsgType::ZNODEPAYMENTSYNC); + + Sync(pfrom); + LogPrintf("ZNODEPAYMENTSYNC -- Sent Znode payment votes to peer %d\n", pfrom->id); + + } else if (strCommand == NetMsgType::ZNODEPAYMENTVOTE) { // Znode Payments Vote for the Winner + + CZnodePaymentVote vote; + vRecv >> vote; + + if(pfrom->nVersion < GetMinZnodePaymentsProto()) return; + + if(!pCurrentBlockIndex) return; + + uint256 nHash = vote.GetHash(); + + pfrom->setAskFor.erase(nHash); + + { + LOCK(cs_mapZnodePaymentVotes); + if(mapZnodePaymentVotes.count(nHash)) { + LogPrint("mnpayments", "ZNODEPAYMENTVOTE -- hash=%s, nHeight=%d seen\n", nHash.ToString(), pCurrentBlockIndex->nHeight); + return; + } + + // Avoid processing same vote multiple times + mapZnodePaymentVotes[nHash] = vote; + // but first mark vote as non-verified, + // AddPaymentVote() below should take care of it if vote is actually ok + mapZnodePaymentVotes[nHash].MarkAsNotVerified(); + } + + int nFirstBlock = pCurrentBlockIndex->nHeight - GetStorageLimit(); + if(vote.nBlockHeight < nFirstBlock || vote.nBlockHeight > pCurrentBlockIndex->nHeight+20) { + LogPrint("mnpayments", "ZNODEPAYMENTVOTE -- vote out of range: nFirstBlock=%d, nBlockHeight=%d, nHeight=%d\n", nFirstBlock, vote.nBlockHeight, pCurrentBlockIndex->nHeight); + return; + } + + std::string strError = ""; + if(!vote.IsValid(pfrom, pCurrentBlockIndex->nHeight, strError)) { + LogPrint("mnpayments", "ZNODEPAYMENTVOTE -- invalid message, error: %s\n", strError); + return; + } + + if(!CanVote(vote.vinZnode.prevout, vote.nBlockHeight)) { + LogPrintf("ZNODEPAYMENTVOTE -- znode already voted, znode=%s\n", vote.vinZnode.prevout.ToStringShort()); + return; + } + + znode_info_t mnInfo = mnodeman.GetZnodeInfo(vote.vinZnode); + if(!mnInfo.fInfoValid) { + // mn was not found, so we can't check vote, some info is probably missing + LogPrintf("ZNODEPAYMENTVOTE -- znode is missing %s\n", vote.vinZnode.prevout.ToStringShort()); + mnodeman.AskForMN(pfrom, vote.vinZnode); + return; + } + + int nDos = 0; + if(!vote.CheckSignature(mnInfo.pubKeyZnode, pCurrentBlockIndex->nHeight, nDos)) { + if(nDos) { + LogPrintf("ZNODEPAYMENTVOTE -- ERROR: invalid signature\n"); + Misbehaving(pfrom->GetId(), nDos); + } else { + // only warn about anything non-critical (i.e. nDos == 0) in debug mode + LogPrint("mnpayments", "ZNODEPAYMENTVOTE -- WARNING: invalid signature\n"); + } + // Either our info or vote info could be outdated. + // In case our info is outdated, ask for an update, + mnodeman.AskForMN(pfrom, vote.vinZnode); + // but there is nothing we can do if vote info itself is outdated + // (i.e. it was signed by a mn which changed its key), + // so just quit here. + return; + } + + CTxDestination address1; + ExtractDestination(vote.payee, address1); + CBitcoinAddress address2(address1); + + LogPrint("mnpayments", "ZNODEPAYMENTVOTE -- vote: address=%s, nBlockHeight=%d, nHeight=%d, prevout=%s\n", address2.ToString(), vote.nBlockHeight, pCurrentBlockIndex->nHeight, vote.vinZnode.prevout.ToStringShort()); + + if(AddPaymentVote(vote)){ + vote.Relay(); + znodeSync.AddedPaymentVote(); + } + } +} + +bool CZnodePaymentVote::Sign() +{ + std::string strError; + std::string strMessage = vinZnode.prevout.ToStringShort() + + boost::lexical_cast(nBlockHeight) + + ScriptToAsmStr(payee); + + if(!darkSendSigner.SignMessage(strMessage, vchSig, activeZnode.keyZnode)) { + LogPrintf("CZnodePaymentVote::Sign -- SignMessage() failed\n"); + return false; + } + + if(!darkSendSigner.VerifyMessage(activeZnode.pubKeyZnode, vchSig, strMessage, strError)) { + LogPrintf("CZnodePaymentVote::Sign -- VerifyMessage() failed, error: %s\n", strError); + return false; + } + + return true; +} + +bool CZnodePayments::GetBlockPayee(int nBlockHeight, CScript& payee) +{ + if(mapZnodeBlocks.count(nBlockHeight)){ + return mapZnodeBlocks[nBlockHeight].GetBestPayee(payee); + } + + return false; +} + +// Is this znode scheduled to get paid soon? +// -- Only look ahead up to 8 blocks to allow for propagation of the latest 2 blocks of votes +bool CZnodePayments::IsScheduled(CZnode& mn, int nNotBlockHeight) +{ + LOCK(cs_mapZnodeBlocks); + + if(!pCurrentBlockIndex) return false; + + CScript mnpayee; + mnpayee = GetScriptForDestination(mn.pubKeyCollateralAddress.GetID()); + + CScript payee; + for(int64_t h = pCurrentBlockIndex->nHeight; h <= pCurrentBlockIndex->nHeight + 8; h++){ + if(h == nNotBlockHeight) continue; + if(mapZnodeBlocks.count(h) && mapZnodeBlocks[h].GetBestPayee(payee) && mnpayee == payee) { + return true; + } + } + + return false; +} + +bool CZnodePayments::AddPaymentVote(const CZnodePaymentVote& vote) +{ + uint256 blockHash = uint256(); + if(!GetBlockHash(blockHash, vote.nBlockHeight - 101)) return false; + + if(HasVerifiedPaymentVote(vote.GetHash())) return false; + + LOCK2(cs_mapZnodeBlocks, cs_mapZnodePaymentVotes); + + mapZnodePaymentVotes[vote.GetHash()] = vote; + + if(!mapZnodeBlocks.count(vote.nBlockHeight)) { + CZnodeBlockPayees blockPayees(vote.nBlockHeight); + mapZnodeBlocks[vote.nBlockHeight] = blockPayees; + } + + mapZnodeBlocks[vote.nBlockHeight].AddPayee(vote); + + return true; +} + +bool CZnodePayments::HasVerifiedPaymentVote(uint256 hashIn) +{ + LOCK(cs_mapZnodePaymentVotes); + std::map::iterator it = mapZnodePaymentVotes.find(hashIn); + return it != mapZnodePaymentVotes.end() && it->second.IsVerified(); +} + +void CZnodeBlockPayees::AddPayee(const CZnodePaymentVote& vote) +{ + LOCK(cs_vecPayees); + + BOOST_FOREACH(CZnodePayee& payee, vecPayees) { + if (payee.GetPayee() == vote.payee) { + payee.AddVoteHash(vote.GetHash()); + return; + } + } + CZnodePayee payeeNew(vote.payee, vote.GetHash()); + vecPayees.push_back(payeeNew); +} + +bool CZnodeBlockPayees::GetBestPayee(CScript& payeeRet) +{ + LOCK(cs_vecPayees); + + if(!vecPayees.size()) { + LogPrint("mnpayments", "CZnodeBlockPayees::GetBestPayee -- ERROR: couldn't find any payee\n"); + return false; + } + + int nVotes = -1; + BOOST_FOREACH(CZnodePayee& payee, vecPayees) { + if (payee.GetVoteCount() > nVotes) { + payeeRet = payee.GetPayee(); + nVotes = payee.GetVoteCount(); + } + } + + return (nVotes > -1); +} + +bool CZnodeBlockPayees::HasPayeeWithVotes(CScript payeeIn, int nVotesReq) +{ + LOCK(cs_vecPayees); + + BOOST_FOREACH(CZnodePayee& payee, vecPayees) { + if (payee.GetVoteCount() >= nVotesReq && payee.GetPayee() == payeeIn) { + return true; + } + } + + LogPrint("mnpayments", "CZnodeBlockPayees::HasPayeeWithVotes -- ERROR: couldn't find any payee with %d+ votes\n", nVotesReq); + return false; +} + +bool CZnodeBlockPayees::IsTransactionValid(const CTransaction& txNew) +{ + LOCK(cs_vecPayees); + + int nMaxSignatures = 0; + std::string strPayeesPossible = ""; + + CAmount nZnodePayment = GetZnodePayment(nBlockHeight, txNew.GetValueOut()); + + //require at least MNPAYMENTS_SIGNATURES_REQUIRED signatures + + BOOST_FOREACH(CZnodePayee& payee, vecPayees) { + if (payee.GetVoteCount() >= nMaxSignatures) { + nMaxSignatures = payee.GetVoteCount(); + } + } + + // if we don't have at least MNPAYMENTS_SIGNATURES_REQUIRED signatures on a payee, approve whichever is the longest chain + if(nMaxSignatures < MNPAYMENTS_SIGNATURES_REQUIRED) return true; + + BOOST_FOREACH(CZnodePayee& payee, vecPayees) { + if (payee.GetVoteCount() >= MNPAYMENTS_SIGNATURES_REQUIRED) { + BOOST_FOREACH(CTxOut txout, txNew.vout) { + if (payee.GetPayee() == txout.scriptPubKey && nZnodePayment == txout.nValue) { + LogPrint("mnpayments", "CZnodeBlockPayees::IsTransactionValid -- Found required payment\n"); + return true; + } + } + + CTxDestination address1; + ExtractDestination(payee.GetPayee(), address1); + CBitcoinAddress address2(address1); + + if(strPayeesPossible == "") { + strPayeesPossible = address2.ToString(); + } else { + strPayeesPossible += "," + address2.ToString(); + } + } + } + + LogPrintf("CZnodeBlockPayees::IsTransactionValid -- ERROR: Missing required payment, possible payees: '%s', amount: %f DASH\n", strPayeesPossible, (float)nZnodePayment/COIN); + return false; +} + +std::string CZnodeBlockPayees::GetRequiredPaymentsString() +{ + LOCK(cs_vecPayees); + + std::string strRequiredPayments = "Unknown"; + + BOOST_FOREACH(CZnodePayee& payee, vecPayees) + { + CTxDestination address1; + ExtractDestination(payee.GetPayee(), address1); + CBitcoinAddress address2(address1); + + if (strRequiredPayments != "Unknown") { + strRequiredPayments += ", " + address2.ToString() + ":" + boost::lexical_cast(payee.GetVoteCount()); + } else { + strRequiredPayments = address2.ToString() + ":" + boost::lexical_cast(payee.GetVoteCount()); + } + } + + return strRequiredPayments; +} + +std::string CZnodePayments::GetRequiredPaymentsString(int nBlockHeight) +{ + LOCK(cs_mapZnodeBlocks); + + if(mapZnodeBlocks.count(nBlockHeight)){ + return mapZnodeBlocks[nBlockHeight].GetRequiredPaymentsString(); + } + + return "Unknown"; +} + +bool CZnodePayments::IsTransactionValid(const CTransaction& txNew, int nBlockHeight) +{ + LOCK(cs_mapZnodeBlocks); + + if(mapZnodeBlocks.count(nBlockHeight)){ + return mapZnodeBlocks[nBlockHeight].IsTransactionValid(txNew); + } + + return true; +} + +void CZnodePayments::CheckAndRemove() +{ + if(!pCurrentBlockIndex) return; + + LOCK2(cs_mapZnodeBlocks, cs_mapZnodePaymentVotes); + + int nLimit = GetStorageLimit(); + + std::map::iterator it = mapZnodePaymentVotes.begin(); + while(it != mapZnodePaymentVotes.end()) { + CZnodePaymentVote vote = (*it).second; + + if(pCurrentBlockIndex->nHeight - vote.nBlockHeight > nLimit) { + LogPrint("mnpayments", "CZnodePayments::CheckAndRemove -- Removing old Znode payment: nBlockHeight=%d\n", vote.nBlockHeight); + mapZnodePaymentVotes.erase(it++); + mapZnodeBlocks.erase(vote.nBlockHeight); + } else { + ++it; + } + } + LogPrintf("CZnodePayments::CheckAndRemove -- %s\n", ToString()); +} + +bool CZnodePaymentVote::IsValid(CNode* pnode, int nValidationHeight, std::string& strError) +{ + CZnode* pmn = mnodeman.Find(vinZnode); + + if(!pmn) { + strError = strprintf("Unknown Znode: prevout=%s", vinZnode.prevout.ToStringShort()); + // Only ask if we are already synced and still have no idea about that Znode + if(znodeSync.IsZnodeListSynced()) { + mnodeman.AskForMN(pnode, vinZnode); + } + + return false; + } + + int nMinRequiredProtocol; + if(nBlockHeight >= nValidationHeight) { + // new votes must comply SPORK_10_ZNODE_PAY_UPDATED_NODES rules + nMinRequiredProtocol = mnpayments.GetMinZnodePaymentsProto(); + } else { + // allow non-updated znodes for old blocks + nMinRequiredProtocol = MIN_ZNODE_PAYMENT_PROTO_VERSION_1; + } + + if(pmn->nProtocolVersion < nMinRequiredProtocol) { + strError = strprintf("Znode protocol is too old: nProtocolVersion=%d, nMinRequiredProtocol=%d", pmn->nProtocolVersion, nMinRequiredProtocol); + return false; + } + + // Only znodes should try to check znode rank for old votes - they need to pick the right winner for future blocks. + // Regular clients (miners included) need to verify znode rank for future block votes only. + if(!fZNode && nBlockHeight < nValidationHeight) return true; + + int nRank = mnodeman.GetZnodeRank(vinZnode, nBlockHeight - 101, nMinRequiredProtocol, false); + + if(nRank == -1) { + LogPrint("mnpayments", "CZnodePaymentVote::IsValid -- Can't calculate rank for znode %s\n", + vinZnode.prevout.ToStringShort()); + return false; + } + + if(nRank > MNPAYMENTS_SIGNATURES_TOTAL) { + // It's common to have znodes mistakenly think they are in the top 10 + // We don't want to print all of these messages in normal mode, debug mode should print though + strError = strprintf("Znode is not in the top %d (%d)", MNPAYMENTS_SIGNATURES_TOTAL, nRank); + // Only ban for new mnw which is out of bounds, for old mnw MN list itself might be way too much off + if(nRank > MNPAYMENTS_SIGNATURES_TOTAL*2 && nBlockHeight > nValidationHeight) { + strError = strprintf("Znode is not in the top %d (%d)", MNPAYMENTS_SIGNATURES_TOTAL*2, nRank); + LogPrintf("CZnodePaymentVote::IsValid -- Error: %s\n", strError); + Misbehaving(pnode->GetId(), 20); + } + // Still invalid however + return false; + } + + return true; +} + +bool CZnodePayments::ProcessBlock(int nBlockHeight) +{ + // DETERMINE IF WE SHOULD BE VOTING FOR THE NEXT PAYEE + + if(fLiteMode || !fZNode) return false; + + // We have little chances to pick the right winner if winners list is out of sync + // but we have no choice, so we'll try. However it doesn't make sense to even try to do so + // if we have not enough data about znodes. + if(!znodeSync.IsZnodeListSynced()) return false; + + int nRank = mnodeman.GetZnodeRank(activeZnode.vin, nBlockHeight - 101, GetMinZnodePaymentsProto(), false); + + if (nRank == -1) { + LogPrint("mnpayments", "CZnodePayments::ProcessBlock -- Unknown Znode\n"); + return false; + } + + if (nRank > MNPAYMENTS_SIGNATURES_TOTAL) { + LogPrint("mnpayments", "CZnodePayments::ProcessBlock -- Znode not in the top %d (%d)\n", MNPAYMENTS_SIGNATURES_TOTAL, nRank); + return false; + } + + + // LOCATE THE NEXT ZNODE WHICH SHOULD BE PAID + + LogPrintf("CZnodePayments::ProcessBlock -- Start: nBlockHeight=%d, znode=%s\n", nBlockHeight, activeZnode.vin.prevout.ToStringShort()); + + // pay to the oldest MN that still had no payment but its input is old enough and it was active long enough + int nCount = 0; + CZnode *pmn = mnodeman.GetNextZnodeInQueueForPayment(nBlockHeight, true, nCount); + + if (pmn == NULL) { + LogPrintf("CZnodePayments::ProcessBlock -- ERROR: Failed to find znode to pay\n"); + return false; + } + + LogPrintf("CZnodePayments::ProcessBlock -- Znode found by GetNextZnodeInQueueForPayment(): %s\n", pmn->vin.prevout.ToStringShort()); + + + CScript payee = GetScriptForDestination(pmn->pubKeyCollateralAddress.GetID()); + + CZnodePaymentVote voteNew(activeZnode.vin, nBlockHeight, payee); + + CTxDestination address1; + ExtractDestination(payee, address1); + CBitcoinAddress address2(address1); + + LogPrintf("CZnodePayments::ProcessBlock -- vote: payee=%s, nBlockHeight=%d\n", address2.ToString(), nBlockHeight); + + // SIGN MESSAGE TO NETWORK WITH OUR ZNODE KEYS + + LogPrintf("CZnodePayments::ProcessBlock -- Signing vote\n"); + if (voteNew.Sign()) { + LogPrintf("CZnodePayments::ProcessBlock -- AddPaymentVote()\n"); + + if (AddPaymentVote(voteNew)) { + voteNew.Relay(); + return true; + } + } + + return false; +} + +void CZnodePaymentVote::Relay() +{ + // do not relay until synced + if (!znodeSync.IsWinnersListSynced()) return; + CInv inv(MSG_ZNODE_PAYMENT_VOTE, GetHash()); + RelayInv(inv); +} + +bool CZnodePaymentVote::CheckSignature(const CPubKey& pubKeyZnode, int nValidationHeight, int &nDos) +{ + // do not ban by default + nDos = 0; + + std::string strMessage = vinZnode.prevout.ToStringShort() + + boost::lexical_cast(nBlockHeight) + + ScriptToAsmStr(payee); + + std::string strError = ""; + if (!darkSendSigner.VerifyMessage(pubKeyZnode, vchSig, strMessage, strError)) { + // Only ban for future block vote when we are already synced. + // Otherwise it could be the case when MN which signed this vote is using another key now + // and we have no idea about the old one. + if(znodeSync.IsZnodeListSynced() && nBlockHeight > nValidationHeight) { + nDos = 20; + } + return error("CZnodePaymentVote::CheckSignature -- Got bad Znode payment signature, znode=%s, error: %s", vinZnode.prevout.ToStringShort().c_str(), strError); + } + + return true; +} + +std::string CZnodePaymentVote::ToString() const +{ + std::ostringstream info; + + info << vinZnode.prevout.ToStringShort() << + ", " << nBlockHeight << + ", " << ScriptToAsmStr(payee) << + ", " << (int)vchSig.size(); + + return info.str(); +} + +// Send only votes for future blocks, node should request every other missing payment block individually +void CZnodePayments::Sync(CNode* pnode) +{ + LOCK(cs_mapZnodeBlocks); + + if(!pCurrentBlockIndex) return; + + int nInvCount = 0; + + for(int h = pCurrentBlockIndex->nHeight; h < pCurrentBlockIndex->nHeight + 20; h++) { + if(mapZnodeBlocks.count(h)) { + BOOST_FOREACH(CZnodePayee& payee, mapZnodeBlocks[h].vecPayees) { + std::vector vecVoteHashes = payee.GetVoteHashes(); + BOOST_FOREACH(uint256& hash, vecVoteHashes) { + if(!HasVerifiedPaymentVote(hash)) continue; + pnode->PushInventory(CInv(MSG_ZNODE_PAYMENT_VOTE, hash)); + nInvCount++; + } + } + } + } + + LogPrintf("CZnodePayments::Sync -- Sent %d votes to peer %d\n", nInvCount, pnode->id); + pnode->PushMessage(NetMsgType::SYNCSTATUSCOUNT, ZNODE_SYNC_MNW, nInvCount); +} + +// Request low data/unknown payment blocks in batches directly from some node instead of/after preliminary Sync. +void CZnodePayments::RequestLowDataPaymentBlocks(CNode* pnode) +{ + if(!pCurrentBlockIndex) return; + + LOCK2(cs_main, cs_mapZnodeBlocks); + + std::vector vToFetch; + int nLimit = GetStorageLimit(); + + const CBlockIndex *pindex = pCurrentBlockIndex; + + while(pCurrentBlockIndex->nHeight - pindex->nHeight < nLimit) { + if(!mapZnodeBlocks.count(pindex->nHeight)) { + // We have no idea about this block height, let's ask + vToFetch.push_back(CInv(MSG_ZNODE_PAYMENT_BLOCK, pindex->GetBlockHash())); + // We should not violate GETDATA rules + if(vToFetch.size() == MAX_INV_SZ) { + LogPrintf("CZnodePayments::SyncLowDataPaymentBlocks -- asking peer %d for %d blocks\n", pnode->id, MAX_INV_SZ); + pnode->PushMessage(NetMsgType::GETDATA, vToFetch); + // Start filling new batch + vToFetch.clear(); + } + } + if(!pindex->pprev) break; + pindex = pindex->pprev; + } + + std::map::iterator it = mapZnodeBlocks.begin(); + + while(it != mapZnodeBlocks.end()) { + int nTotalVotes = 0; + bool fFound = false; + BOOST_FOREACH(CZnodePayee& payee, it->second.vecPayees) { + if(payee.GetVoteCount() >= MNPAYMENTS_SIGNATURES_REQUIRED) { + fFound = true; + break; + } + nTotalVotes += payee.GetVoteCount(); + } + // A clear winner (MNPAYMENTS_SIGNATURES_REQUIRED+ votes) was found + // or no clear winner was found but there are at least avg number of votes + if(fFound || nTotalVotes >= (MNPAYMENTS_SIGNATURES_TOTAL + MNPAYMENTS_SIGNATURES_REQUIRED)/2) { + // so just move to the next block + ++it; + continue; + } + // DEBUG +// DBG ( +// // Let's see why this failed +// BOOST_FOREACH(CZnodePayee& payee, it->second.vecPayees) { +// CTxDestination address1; +// ExtractDestination(payee.GetPayee(), address1); +// CBitcoinAddress address2(address1); +// printf("payee %s votes %d\n", address2.ToString().c_str(), payee.GetVoteCount()); +// } +// printf("block %d votes total %d\n", it->first, nTotalVotes); +// ) + // END DEBUG + // Low data block found, let's try to sync it + uint256 hash; + if(GetBlockHash(hash, it->first)) { + vToFetch.push_back(CInv(MSG_ZNODE_PAYMENT_BLOCK, hash)); + } + // We should not violate GETDATA rules + if(vToFetch.size() == MAX_INV_SZ) { + LogPrintf("CZnodePayments::SyncLowDataPaymentBlocks -- asking peer %d for %d payment blocks\n", pnode->id, MAX_INV_SZ); + pnode->PushMessage(NetMsgType::GETDATA, vToFetch); + // Start filling new batch + vToFetch.clear(); + } + ++it; + } + // Ask for the rest of it + if(!vToFetch.empty()) { + LogPrintf("CZnodePayments::SyncLowDataPaymentBlocks -- asking peer %d for %d payment blocks\n", pnode->id, vToFetch.size()); + pnode->PushMessage(NetMsgType::GETDATA, vToFetch); + } +} + +std::string CZnodePayments::ToString() const +{ + std::ostringstream info; + + info << "Votes: " << (int)mapZnodePaymentVotes.size() << + ", Blocks: " << (int)mapZnodeBlocks.size(); + + return info.str(); +} + +bool CZnodePayments::IsEnoughData() +{ + float nAverageVotes = (MNPAYMENTS_SIGNATURES_TOTAL + MNPAYMENTS_SIGNATURES_REQUIRED) / 2; + int nStorageLimit = GetStorageLimit(); + return GetBlockCount() > nStorageLimit && GetVoteCount() > nStorageLimit * nAverageVotes; +} + +int CZnodePayments::GetStorageLimit() +{ + return std::max(int(mnodeman.size() * nStorageCoeff), nMinBlocksToStore); +} + +void CZnodePayments::UpdatedBlockTip(const CBlockIndex *pindex) +{ + pCurrentBlockIndex = pindex; + LogPrint("mnpayments", "CZnodePayments::UpdatedBlockTip -- pCurrentBlockIndex->nHeight=%d\n", pCurrentBlockIndex->nHeight); + + ProcessBlock(pindex->nHeight + 10); +} diff --git a/src/znode-payments.h b/src/znode-payments.h new file mode 100644 index 0000000000..eb16f79128 --- /dev/null +++ b/src/znode-payments.h @@ -0,0 +1,224 @@ +// Copyright (c) 2014-2017 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef ZNODE_PAYMENTS_H +#define ZNODE_PAYMENTS_H + +#include "util.h" +#include "core_io.h" +#include "key.h" +#include "main.h" +#include "znode.h" +#include "utilstrencodings.h" + +class CZnodePayments; +class CZnodePaymentVote; +class CZnodeBlockPayees; + +static const int MNPAYMENTS_SIGNATURES_REQUIRED = 6; +static const int MNPAYMENTS_SIGNATURES_TOTAL = 10; + +//! minimum peer version that can receive and send znode payment messages, +// vote for znode and be elected as a payment winner +// V1 - Last protocol version before update +// V2 - Newest protocol version +static const int MIN_ZNODE_PAYMENT_PROTO_VERSION_1 = 70206; +static const int MIN_ZNODE_PAYMENT_PROTO_VERSION_2 = 70206; + +extern CCriticalSection cs_vecPayees; +extern CCriticalSection cs_mapZnodeBlocks; +extern CCriticalSection cs_mapZnodePayeeVotes; + +extern CZnodePayments mnpayments; + +/// TODO: all 4 functions do not belong here really, they should be refactored/moved somewhere (main.cpp ?) +bool IsBlockValueValid(const CBlock& block, int nBlockHeight, CAmount blockReward, std::string &strErrorRet); +bool IsBlockPayeeValid(const CTransaction& txNew, int nBlockHeight, CAmount blockReward); +void FillBlockPayments(CMutableTransaction& txNew, int nBlockHeight, CAmount blockReward, CTxOut& txoutZnodeRet, std::vector& voutSuperblockRet); +std::string GetRequiredPaymentsString(int nBlockHeight); + +class CZnodePayee +{ +private: + CScript scriptPubKey; + std::vector vecVoteHashes; + +public: + CZnodePayee() : + scriptPubKey(), + vecVoteHashes() + {} + + CZnodePayee(CScript payee, uint256 hashIn) : + scriptPubKey(payee), + vecVoteHashes() + { + vecVoteHashes.push_back(hashIn); + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(*(CScriptBase*)(&scriptPubKey)); + READWRITE(vecVoteHashes); + } + + CScript GetPayee() { return scriptPubKey; } + + void AddVoteHash(uint256 hashIn) { vecVoteHashes.push_back(hashIn); } + std::vector GetVoteHashes() { return vecVoteHashes; } + int GetVoteCount() { return vecVoteHashes.size(); } +}; + +// Keep track of votes for payees from znodes +class CZnodeBlockPayees +{ +public: + int nBlockHeight; + std::vector vecPayees; + + CZnodeBlockPayees() : + nBlockHeight(0), + vecPayees() + {} + CZnodeBlockPayees(int nBlockHeightIn) : + nBlockHeight(nBlockHeightIn), + vecPayees() + {} + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(nBlockHeight); + READWRITE(vecPayees); + } + + void AddPayee(const CZnodePaymentVote& vote); + bool GetBestPayee(CScript& payeeRet); + bool HasPayeeWithVotes(CScript payeeIn, int nVotesReq); + + bool IsTransactionValid(const CTransaction& txNew); + + std::string GetRequiredPaymentsString(); +}; + +// vote for the winning payment +class CZnodePaymentVote +{ +public: + CTxIn vinZnode; + + int nBlockHeight; + CScript payee; + std::vector vchSig; + + CZnodePaymentVote() : + vinZnode(), + nBlockHeight(0), + payee(), + vchSig() + {} + + CZnodePaymentVote(CTxIn vinZnode, int nBlockHeight, CScript payee) : + vinZnode(vinZnode), + nBlockHeight(nBlockHeight), + payee(payee), + vchSig() + {} + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(vinZnode); + READWRITE(nBlockHeight); + READWRITE(*(CScriptBase*)(&payee)); + READWRITE(vchSig); + } + + uint256 GetHash() const { + CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); + ss << *(CScriptBase*)(&payee); + ss << nBlockHeight; + ss << vinZnode.prevout; + return ss.GetHash(); + } + + bool Sign(); + bool CheckSignature(const CPubKey& pubKeyZnode, int nValidationHeight, int &nDos); + + bool IsValid(CNode* pnode, int nValidationHeight, std::string& strError); + void Relay(); + + bool IsVerified() { return !vchSig.empty(); } + void MarkAsNotVerified() { vchSig.clear(); } + + std::string ToString() const; +}; + +// +// Znode Payments Class +// Keeps track of who should get paid for which blocks +// + +class CZnodePayments +{ +private: + // znode count times nStorageCoeff payments blocks should be stored ... + const float nStorageCoeff; + // ... but at least nMinBlocksToStore (payments blocks) + const int nMinBlocksToStore; + + // Keep track of current block index + const CBlockIndex *pCurrentBlockIndex; + +public: + std::map mapZnodePaymentVotes; + std::map mapZnodeBlocks; + std::map mapZnodesLastVote; + + CZnodePayments() : nStorageCoeff(1.25), nMinBlocksToStore(5000) {} + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(mapZnodePaymentVotes); + READWRITE(mapZnodeBlocks); + } + + void Clear(); + + bool AddPaymentVote(const CZnodePaymentVote& vote); + bool HasVerifiedPaymentVote(uint256 hashIn); + bool ProcessBlock(int nBlockHeight); + + void Sync(CNode* node); + void RequestLowDataPaymentBlocks(CNode* pnode); + void CheckAndRemove(); + + bool GetBlockPayee(int nBlockHeight, CScript& payee); + bool IsTransactionValid(const CTransaction& txNew, int nBlockHeight); + bool IsScheduled(CZnode& mn, int nNotBlockHeight); + + bool CanVote(COutPoint outZnode, int nBlockHeight); + + int GetMinZnodePaymentsProto(); + void ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); + std::string GetRequiredPaymentsString(int nBlockHeight); + void FillBlockPayee(CMutableTransaction& txNew, int nBlockHeight, CAmount blockReward, CTxOut& txoutZnodeRet); + std::string ToString() const; + + int GetBlockCount() { return mapZnodeBlocks.size(); } + int GetVoteCount() { return mapZnodePaymentVotes.size(); } + + bool IsEnoughData(); + int GetStorageLimit(); + + void UpdatedBlockTip(const CBlockIndex *pindex); +}; + +#endif diff --git a/src/znode-sync.cpp b/src/znode-sync.cpp new file mode 100644 index 0000000000..7727ceaf70 --- /dev/null +++ b/src/znode-sync.cpp @@ -0,0 +1,520 @@ +// Copyright (c) 2014-2017 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "activeznode.h" +#include "checkpoints.h" +//#include "governance.h" +#include "main.h" +#include "znode.h" +#include "znode-payments.h" +#include "znode-sync.h" +#include "znodeman.h" +//#include "netfulfilledman.h" +//#include "spork.h" +#include "util.h" + +class CZnodeSync; +CZnodeSync znodeSync; + +bool CZnodeSync::CheckNodeHeight(CNode* pnode, bool fDisconnectStuckNodes) +{ + CNodeStateStats stats; + if(!GetNodeStateStats(pnode->id, stats) || stats.nCommonHeight == -1 || stats.nSyncHeight == -1) return false; // not enough info about this peer + + // Check blocks and headers, allow a small error margin of 1 block + if(pCurrentBlockIndex->nHeight - 1 > stats.nCommonHeight) { + // This peer probably stuck, don't sync any additional data from it + if(fDisconnectStuckNodes) { + // Disconnect to free this connection slot for another peer. + pnode->fDisconnect = true; + LogPrintf("CZnodeSync::CheckNodeHeight -- disconnecting from stuck peer, nHeight=%d, nCommonHeight=%d, peer=%d\n", + pCurrentBlockIndex->nHeight, stats.nCommonHeight, pnode->id); + } else { + LogPrintf("CZnodeSync::CheckNodeHeight -- skipping stuck peer, nHeight=%d, nCommonHeight=%d, peer=%d\n", + pCurrentBlockIndex->nHeight, stats.nCommonHeight, pnode->id); + } + return false; + } + else if(pCurrentBlockIndex->nHeight < stats.nSyncHeight - 1) { + // This peer announced more headers than we have blocks currently + LogPrintf("CZnodeSync::CheckNodeHeight -- skipping peer, who announced more headers than we have blocks currently, nHeight=%d, nSyncHeight=%d, peer=%d\n", + pCurrentBlockIndex->nHeight, stats.nSyncHeight, pnode->id); + return false; + } + + return true; +} + +bool CZnodeSync::IsBlockchainSynced(bool fBlockAccepted) +{ + static bool fBlockchainSynced = false; + static int64_t nTimeLastProcess = GetTime(); + static int nSkipped = 0; + static bool fFirstBlockAccepted = false; + + // if the last call to this function was more than 60 minutes ago (client was in sleep mode) reset the sync process + if(GetTime() - nTimeLastProcess > 60*60) { + Reset(); + fBlockchainSynced = false; + } + + if(!pCurrentBlockIndex || !pindexBestHeader || fImporting || fReindex) return false; + + if(fBlockAccepted) { + // this should be only triggered while we are still syncing + if(!IsSynced()) { + // we are trying to download smth, reset blockchain sync status + if(fDebug) LogPrintf("CZnodeSync::IsBlockchainSynced -- reset\n"); + fFirstBlockAccepted = true; + fBlockchainSynced = false; + nTimeLastProcess = GetTime(); + return false; + } + } else { + // skip if we already checked less than 1 tick ago + if(GetTime() - nTimeLastProcess < ZNODE_SYNC_TICK_SECONDS) { + nSkipped++; + return fBlockchainSynced; + } + } + + if(fDebug) LogPrintf("CZnodeSync::IsBlockchainSynced -- state before check: %ssynced, skipped %d times\n", fBlockchainSynced ? "" : "not ", nSkipped); + + nTimeLastProcess = GetTime(); + nSkipped = 0; + + if(fBlockchainSynced) return true; + + if(fCheckpointsEnabled && pCurrentBlockIndex->nHeight < Checkpoints::GetTotalBlocksEstimate(Params().Checkpoints())) + return false; + + std::vector vNodesCopy = CopyNodeVector(); + + // We have enough peers and assume most of them are synced + if(vNodesCopy.size() >= ZNODE_SYNC_ENOUGH_PEERS) { + // Check to see how many of our peers are (almost) at the same height as we are + int nNodesAtSameHeight = 0; + BOOST_FOREACH(CNode* pnode, vNodesCopy) + { + // Make sure this peer is presumably at the same height + if(!CheckNodeHeight(pnode)) continue; + nNodesAtSameHeight++; + // if we have decent number of such peers, most likely we are synced now + if(nNodesAtSameHeight >= ZNODE_SYNC_ENOUGH_PEERS) { + LogPrintf("CZnodeSync::IsBlockchainSynced -- found enough peers on the same height as we are, done\n"); + fBlockchainSynced = true; + ReleaseNodeVector(vNodesCopy); + return true; + } + } + } + ReleaseNodeVector(vNodesCopy); + + // wait for at least one new block to be accepted + if(!fFirstBlockAccepted) return false; + + // same as !IsInitialBlockDownload() but no cs_main needed here + int64_t nMaxBlockTime = std::max(pCurrentBlockIndex->GetBlockTime(), pindexBestHeader->GetBlockTime()); + fBlockchainSynced = pindexBestHeader->nHeight - pCurrentBlockIndex->nHeight < 24 * 6 && + GetTime() - nMaxBlockTime < Params().MaxTipAge(); + + return fBlockchainSynced; +} + +void CZnodeSync::Fail() +{ + nTimeLastFailure = GetTime(); + nRequestedZnodeAssets = ZNODE_SYNC_FAILED; +} + +void CZnodeSync::Reset() +{ + nRequestedZnodeAssets = ZNODE_SYNC_INITIAL; + nRequestedZnodeAttempt = 0; + nTimeAssetSyncStarted = GetTime(); + nTimeLastZnodeList = GetTime(); + nTimeLastPaymentVote = GetTime(); + nTimeLastGovernanceItem = GetTime(); + nTimeLastFailure = 0; + nCountFailures = 0; +} + +std::string CZnodeSync::GetAssetName() +{ + switch(nRequestedZnodeAssets) + { + case(ZNODE_SYNC_INITIAL): return "ZNODE_SYNC_INITIAL"; + case(ZNODE_SYNC_SPORKS): return "ZNODE_SYNC_SPORKS"; + case(ZNODE_SYNC_LIST): return "ZNODE_SYNC_LIST"; + case(ZNODE_SYNC_MNW): return "ZNODE_SYNC_MNW"; + case(ZNODE_SYNC_GOVERNANCE): return "ZNODE_SYNC_GOVERNANCE"; + case(ZNODE_SYNC_FAILED): return "ZNODE_SYNC_FAILED"; + case ZNODE_SYNC_FINISHED: return "ZNODE_SYNC_FINISHED"; + default: return "UNKNOWN"; + } +} + +void CZnodeSync::SwitchToNextAsset() +{ + switch(nRequestedZnodeAssets) + { + case(ZNODE_SYNC_FAILED): + throw std::runtime_error("Can't switch to next asset from failed, should use Reset() first!"); + break; + case(ZNODE_SYNC_INITIAL): + ClearFulfilledRequests(); + nRequestedZnodeAssets = ZNODE_SYNC_SPORKS; + LogPrintf("CZnodeSync::SwitchToNextAsset -- Starting %s\n", GetAssetName()); + break; + case(ZNODE_SYNC_SPORKS): + nTimeLastZnodeList = GetTime(); + nRequestedZnodeAssets = ZNODE_SYNC_LIST; + LogPrintf("CZnodeSync::SwitchToNextAsset -- Starting %s\n", GetAssetName()); + break; + case(ZNODE_SYNC_LIST): + nTimeLastPaymentVote = GetTime(); + nRequestedZnodeAssets = ZNODE_SYNC_MNW; + LogPrintf("CZnodeSync::SwitchToNextAsset -- Starting %s\n", GetAssetName()); + break; + case(ZNODE_SYNC_MNW): + nTimeLastGovernanceItem = GetTime(); + nRequestedZnodeAssets = ZNODE_SYNC_GOVERNANCE; + LogPrintf("CZnodeSync::SwitchToNextAsset -- Starting %s\n", GetAssetName()); + break; + case(ZNODE_SYNC_GOVERNANCE): + LogPrintf("CZnodeSync::SwitchToNextAsset -- Sync has finished\n"); + nRequestedZnodeAssets = ZNODE_SYNC_FINISHED; +// uiInterface.NotifyAdditionalDataSyncProgressChanged(1); + //try to activate our znode if possible + activeZnode.ManageState(); + + TRY_LOCK(cs_vNodes, lockRecv); + if(!lockRecv) return; + + BOOST_FOREACH(CNode* pnode, vNodes) { +// netfulfilledman.AddFulfilledRequest(pnode->addr, "full-sync"); + } + + break; + } + nRequestedZnodeAttempt = 0; + nTimeAssetSyncStarted = GetTime(); +} + +std::string CZnodeSync::GetSyncStatus() +{ + switch (znodeSync.nRequestedZnodeAssets) { + case ZNODE_SYNC_INITIAL: return _("Synchronization pending..."); + case ZNODE_SYNC_SPORKS: return _("Synchronizing sporks..."); + case ZNODE_SYNC_LIST: return _("Synchronizing znodes..."); + case ZNODE_SYNC_MNW: return _("Synchronizing znode payments..."); + case ZNODE_SYNC_GOVERNANCE: return _("Synchronizing governance objects..."); + case ZNODE_SYNC_FAILED: return _("Synchronization failed"); + case ZNODE_SYNC_FINISHED: return _("Synchronization finished"); + default: return ""; + } +} + +void CZnodeSync::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) +{ + if (strCommand == NetMsgType::SYNCSTATUSCOUNT) { //Sync status count + + //do not care about stats if sync process finished or failed + if(IsSynced() || IsFailed()) return; + + int nItemID; + int nCount; + vRecv >> nItemID >> nCount; + + LogPrintf("SYNCSTATUSCOUNT -- got inventory count: nItemID=%d nCount=%d peer=%d\n", nItemID, nCount, pfrom->id); + } +} + +void CZnodeSync::ClearFulfilledRequests() +{ + TRY_LOCK(cs_vNodes, lockRecv); + if(!lockRecv) return; + + BOOST_FOREACH(CNode* pnode, vNodes) + { +// netfulfilledman.RemoveFulfilledRequest(pnode->addr, "spork-sync"); +// netfulfilledman.RemoveFulfilledRequest(pnode->addr, "znode-list-sync"); +// netfulfilledman.RemoveFulfilledRequest(pnode->addr, "znode-payment-sync"); +// netfulfilledman.RemoveFulfilledRequest(pnode->addr, "governance-sync"); +// netfulfilledman.RemoveFulfilledRequest(pnode->addr, "full-sync"); + } +} + +void CZnodeSync::ProcessTick() +{ + static int nTick = 0; + if(nTick++ % ZNODE_SYNC_TICK_SECONDS != 0) return; + if(!pCurrentBlockIndex) return; + + //the actual count of znodes we have currently + int nMnCount = mnodeman.CountZnodes(); + + if(fDebug) LogPrintf("CZnodeSync::ProcessTick -- nTick %d nMnCount %d\n", nTick, nMnCount); + + // RESET SYNCING INCASE OF FAILURE + { + if(IsSynced()) { + /* + Resync if we lost all znodes from sleep/wake or failed to sync originally + */ + if(nMnCount == 0) { + LogPrintf("CZnodeSync::ProcessTick -- WARNING: not enough data, restarting sync\n"); + Reset(); + } else { + std::vector vNodesCopy = CopyNodeVector(); +// governance.RequestGovernanceObjectVotes(vNodesCopy); + ReleaseNodeVector(vNodesCopy); + return; + } + } + + //try syncing again + if(IsFailed()) { + if(nTimeLastFailure + (1*60) < GetTime()) { // 1 minute cooldown after failed sync + Reset(); + } + return; + } + } + + // INITIAL SYNC SETUP / LOG REPORTING + double nSyncProgress = double(nRequestedZnodeAttempt + (nRequestedZnodeAssets - 1) * 8) / (8*4); + LogPrintf("CZnodeSync::ProcessTick -- nTick %d nRequestedZnodeAssets %d nRequestedZnodeAttempt %d nSyncProgress %f\n", nTick, nRequestedZnodeAssets, nRequestedZnodeAttempt, nSyncProgress); +// uiInterface.NotifyAdditionalDataSyncProgressChanged(nSyncProgress); + + // sporks synced but blockchain is not, wait until we're almost at a recent block to continue + if(Params().NetworkIDString() != CBaseChainParams::REGTEST && + !IsBlockchainSynced() && nRequestedZnodeAssets > ZNODE_SYNC_SPORKS) + { + LogPrintf("CZnodeSync::ProcessTick -- nTick %d nRequestedZnodeAssets %d nRequestedZnodeAttempt %d -- blockchain is not synced yet\n", nTick, nRequestedZnodeAssets, nRequestedZnodeAttempt); + nTimeLastZnodeList = GetTime(); + nTimeLastPaymentVote = GetTime(); + nTimeLastGovernanceItem = GetTime(); + return; + } + + if(nRequestedZnodeAssets == ZNODE_SYNC_INITIAL || + (nRequestedZnodeAssets == ZNODE_SYNC_SPORKS && IsBlockchainSynced())) + { + SwitchToNextAsset(); + } + + std::vector vNodesCopy = CopyNodeVector(); + + BOOST_FOREACH(CNode* pnode, vNodesCopy) + { + // Don't try to sync any data from outbound "znode" connections - + // they are temporary and should be considered unreliable for a sync process. + // Inbound connection this early is most likely a "znode" connection + // initialted from another node, so skip it too. + if(pnode->fZnode || (fZNode && pnode->fInbound)) continue; + + // QUICK MODE (REGTEST ONLY!) + if(Params().NetworkIDString() == CBaseChainParams::REGTEST) + { + if(nRequestedZnodeAttempt <= 2) { + pnode->PushMessage(NetMsgType::GETSPORKS); //get current network sporks + } else if(nRequestedZnodeAttempt < 4) { + mnodeman.DsegUpdate(pnode); + } else if(nRequestedZnodeAttempt < 6) { + int nMnCount = mnodeman.CountZnodes(); + pnode->PushMessage(NetMsgType::ZNODEPAYMENTSYNC, nMnCount); //sync payment votes + SendGovernanceSyncRequest(pnode); + } else { + nRequestedZnodeAssets = ZNODE_SYNC_FINISHED; + } + nRequestedZnodeAttempt++; + ReleaseNodeVector(vNodesCopy); + return; + } + + // NORMAL NETWORK MODE - TESTNET/MAINNET + { +// if(netfulfilledman.HasFulfilledRequest(pnode->addr, "full-sync")) { +// // We already fully synced from this node recently, +// // disconnect to free this connection slot for another peer. +// pnode->fDisconnect = true; +// LogPrintf("CZnodeSync::ProcessTick -- disconnecting from recently synced peer %d\n", pnode->id); +// continue; +// } +// +// // SPORK : ALWAYS ASK FOR SPORKS AS WE SYNC (we skip this mode now) +// +// if(!netfulfilledman.HasFulfilledRequest(pnode->addr, "spork-sync")) { +// // only request once from each peer +// netfulfilledman.AddFulfilledRequest(pnode->addr, "spork-sync"); +// // get current network sporks +// pnode->PushMessage(NetMsgType::GETSPORKS); +// LogPrintf("CZnodeSync::ProcessTick -- nTick %d nRequestedZnodeAssets %d -- requesting sporks from peer %d\n", nTick, nRequestedZnodeAssets, pnode->id); +// continue; // always get sporks first, switch to the next node without waiting for the next tick +// } + + // MNLIST : SYNC ZNODE LIST FROM OTHER CONNECTED CLIENTS + + if(nRequestedZnodeAssets == ZNODE_SYNC_LIST) { + LogPrint("znode", "CZnodeSync::ProcessTick -- nTick %d nRequestedZnodeAssets %d nTimeLastZnodeList %lld GetTime() %lld diff %lld\n", nTick, nRequestedZnodeAssets, nTimeLastZnodeList, GetTime(), GetTime() - nTimeLastZnodeList); + // check for timeout first + if(nTimeLastZnodeList < GetTime() - ZNODE_SYNC_TIMEOUT_SECONDS) { + LogPrintf("CZnodeSync::ProcessTick -- nTick %d nRequestedZnodeAssets %d -- timeout\n", nTick, nRequestedZnodeAssets); + if (nRequestedZnodeAttempt == 0) { + LogPrintf("CZnodeSync::ProcessTick -- ERROR: failed to sync %s\n", GetAssetName()); + // there is no way we can continue without znode list, fail here and try later + Fail(); + ReleaseNodeVector(vNodesCopy); + return; + } + SwitchToNextAsset(); + ReleaseNodeVector(vNodesCopy); + return; + } + + // only request once from each peer +// if(netfulfilledman.HasFulfilledRequest(pnode->addr, "znode-list-sync")) continue; +// netfulfilledman.AddFulfilledRequest(pnode->addr, "znode-list-sync"); + + if (pnode->nVersion < mnpayments.GetMinZnodePaymentsProto()) continue; + nRequestedZnodeAttempt++; + + mnodeman.DsegUpdate(pnode); + + ReleaseNodeVector(vNodesCopy); + return; //this will cause each peer to get one request each six seconds for the various assets we need + } + + // MNW : SYNC ZNODE PAYMENT VOTES FROM OTHER CONNECTED CLIENTS + + if(nRequestedZnodeAssets == ZNODE_SYNC_MNW) { + LogPrint("mnpayments", "CZnodeSync::ProcessTick -- nTick %d nRequestedZnodeAssets %d nTimeLastPaymentVote %lld GetTime() %lld diff %lld\n", nTick, nRequestedZnodeAssets, nTimeLastPaymentVote, GetTime(), GetTime() - nTimeLastPaymentVote); + // check for timeout first + // This might take a lot longer than ZNODE_SYNC_TIMEOUT_SECONDS minutes due to new blocks, + // but that should be OK and it should timeout eventually. + if(nTimeLastPaymentVote < GetTime() - ZNODE_SYNC_TIMEOUT_SECONDS) { + LogPrintf("CZnodeSync::ProcessTick -- nTick %d nRequestedZnodeAssets %d -- timeout\n", nTick, nRequestedZnodeAssets); + if (nRequestedZnodeAttempt == 0) { + LogPrintf("CZnodeSync::ProcessTick -- ERROR: failed to sync %s\n", GetAssetName()); + // probably not a good idea to proceed without winner list + Fail(); + ReleaseNodeVector(vNodesCopy); + return; + } + SwitchToNextAsset(); + ReleaseNodeVector(vNodesCopy); + return; + } + + // check for data + // if mnpayments already has enough blocks and votes, switch to the next asset + // try to fetch data from at least two peers though + if(nRequestedZnodeAttempt > 1 && mnpayments.IsEnoughData()) { + LogPrintf("CZnodeSync::ProcessTick -- nTick %d nRequestedZnodeAssets %d -- found enough data\n", nTick, nRequestedZnodeAssets); + SwitchToNextAsset(); + ReleaseNodeVector(vNodesCopy); + return; + } + + // only request once from each peer +// if(netfulfilledman.HasFulfilledRequest(pnode->addr, "znode-payment-sync")) continue; +// netfulfilledman.AddFulfilledRequest(pnode->addr, "znode-payment-sync"); + + if(pnode->nVersion < mnpayments.GetMinZnodePaymentsProto()) continue; + nRequestedZnodeAttempt++; + + // ask node for all payment votes it has (new nodes will only return votes for future payments) + pnode->PushMessage(NetMsgType::ZNODEPAYMENTSYNC, mnpayments.GetStorageLimit()); + // ask node for missing pieces only (old nodes will not be asked) + mnpayments.RequestLowDataPaymentBlocks(pnode); + + ReleaseNodeVector(vNodesCopy); + return; //this will cause each peer to get one request each six seconds for the various assets we need + } + + // GOVOBJ : SYNC GOVERNANCE ITEMS FROM OUR PEERS + + if(nRequestedZnodeAssets == ZNODE_SYNC_GOVERNANCE) { + LogPrint("gobject", "CZnodeSync::ProcessTick -- nTick %d nRequestedZnodeAssets %d nTimeLastGovernanceItem %lld GetTime() %lld diff %lld\n", nTick, nRequestedZnodeAssets, nTimeLastGovernanceItem, GetTime(), GetTime() - nTimeLastGovernanceItem); + + // check for timeout first + if(GetTime() - nTimeLastGovernanceItem > ZNODE_SYNC_TIMEOUT_SECONDS) { + LogPrintf("CZnodeSync::ProcessTick -- nTick %d nRequestedZnodeAssets %d -- timeout\n", nTick, nRequestedZnodeAssets); + if(nRequestedZnodeAttempt == 0) { + LogPrintf("CZnodeSync::ProcessTick -- WARNING: failed to sync %s\n", GetAssetName()); + // it's kind of ok to skip this for now, hopefully we'll catch up later? + } + SwitchToNextAsset(); + ReleaseNodeVector(vNodesCopy); + return; + } + + // only request obj sync once from each peer, then request votes on per-obj basis +// if(netfulfilledman.HasFulfilledRequest(pnode->addr, "governance-sync")) { +// int nObjsLeftToAsk = governance.RequestGovernanceObjectVotes(pnode); +// int nObjsLeftToAsk = governance.RequestGovernanceObjectVotes(pnode); +// static int64_t nTimeNoObjectsLeft = 0; +// // check for data +// if(nObjsLeftToAsk == 0) { +// static int nLastTick = 0; +// static int nLastVotes = 0; +// if(nTimeNoObjectsLeft == 0) { +// // asked all objects for votes for the first time +// nTimeNoObjectsLeft = GetTime(); +// } +// // make sure the condition below is checked only once per tick +// if(nLastTick == nTick) continue; +// if(GetTime() - nTimeNoObjectsLeft > ZNODE_SYNC_TIMEOUT_SECONDS && +// governance.GetVoteCount() - nLastVotes < std::max(int(0.0001 * nLastVotes), ZNODE_SYNC_TICK_SECONDS) +// ) { +// // We already asked for all objects, waited for ZNODE_SYNC_TIMEOUT_SECONDS +// // after that and less then 0.01% or ZNODE_SYNC_TICK_SECONDS +// // (i.e. 1 per second) votes were recieved during the last tick. +// // We can be pretty sure that we are done syncing. +// LogPrintf("CZnodeSync::ProcessTick -- nTick %d nRequestedZnodeAssets %d -- asked for all objects, nothing to do\n", nTick, nRequestedZnodeAssets); +// // reset nTimeNoObjectsLeft to be able to use the same condition on resync +// nTimeNoObjectsLeft = 0; +// SwitchToNextAsset(); +// ReleaseNodeVector(vNodesCopy); +// return; +// } +// nLastTick = nTick; +// nLastVotes = governance.GetVoteCount(); +// } +// continue; +// } +// netfulfilledman.AddFulfilledRequest(pnode->addr, "governance-sync"); + +// if (pnode->nVersion < MIN_GOVERNANCE_PEER_PROTO_VERSION) continue; + nRequestedZnodeAttempt++; + + SendGovernanceSyncRequest(pnode); + + ReleaseNodeVector(vNodesCopy); + return; //this will cause each peer to get one request each six seconds for the various assets we need + } + } + } + // looped through all nodes, release them + ReleaseNodeVector(vNodesCopy); +} + +void CZnodeSync::SendGovernanceSyncRequest(CNode* pnode) +{ +// if(pnode->nVersion >= GOVERNANCE_FILTER_PROTO_VERSION) { +// CBloomFilter filter; +// filter.clear(); +// +// pnode->PushMessage(NetMsgType::MNGOVERNANCESYNC, uint256(), filter); +// } +// else { +// pnode->PushMessage(NetMsgType::MNGOVERNANCESYNC, uint256()); +// } +} + +void CZnodeSync::UpdatedBlockTip(const CBlockIndex *pindex) +{ + pCurrentBlockIndex = pindex; +} diff --git a/src/znode-sync.h b/src/znode-sync.h new file mode 100644 index 0000000000..3578c480da --- /dev/null +++ b/src/znode-sync.h @@ -0,0 +1,92 @@ +// Copyright (c) 2014-2017 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef ZNODE_SYNC_H +#define ZNODE_SYNC_H + +#include "chain.h" +#include "net.h" + +#include + +class CZnodeSync; + +static const int ZNODE_SYNC_FAILED = -1; +static const int ZNODE_SYNC_INITIAL = 0; +static const int ZNODE_SYNC_SPORKS = 1; +static const int ZNODE_SYNC_LIST = 2; +static const int ZNODE_SYNC_MNW = 3; +static const int ZNODE_SYNC_GOVERNANCE = 4; +static const int ZNODE_SYNC_GOVOBJ = 10; +static const int ZNODE_SYNC_GOVOBJ_VOTE = 11; +static const int ZNODE_SYNC_FINISHED = 999; + +static const int ZNODE_SYNC_TICK_SECONDS = 6; +static const int ZNODE_SYNC_TIMEOUT_SECONDS = 30; // our blocks are 2.5 minutes so 30 seconds should be fine + +static const int ZNODE_SYNC_ENOUGH_PEERS = 6; + +extern CZnodeSync znodeSync; + +// +// CZnodeSync : Sync znode assets in stages +// + +class CZnodeSync +{ +private: + // Keep track of current asset + int nRequestedZnodeAssets; + // Count peers we've requested the asset from + int nRequestedZnodeAttempt; + + // Time when current znode asset sync started + int64_t nTimeAssetSyncStarted; + + // Last time when we received some znode asset ... + int64_t nTimeLastZnodeList; + int64_t nTimeLastPaymentVote; + int64_t nTimeLastGovernanceItem; + // ... or failed + int64_t nTimeLastFailure; + + // How many times we failed + int nCountFailures; + + // Keep track of current block index + const CBlockIndex *pCurrentBlockIndex; + + bool CheckNodeHeight(CNode* pnode, bool fDisconnectStuckNodes = false); + void Fail(); + void ClearFulfilledRequests(); + +public: + CZnodeSync() { Reset(); } + + void AddedZnodeList() { nTimeLastZnodeList = GetTime(); } + void AddedPaymentVote() { nTimeLastPaymentVote = GetTime(); } + void AddedGovernanceItem() { nTimeLastGovernanceItem = GetTime(); }; + + void SendGovernanceSyncRequest(CNode* pnode); + + bool IsFailed() { return nRequestedZnodeAssets == ZNODE_SYNC_FAILED; } + bool IsBlockchainSynced(bool fBlockAccepted = false); + bool IsZnodeListSynced() { return nRequestedZnodeAssets > ZNODE_SYNC_LIST; } + bool IsWinnersListSynced() { return nRequestedZnodeAssets > ZNODE_SYNC_MNW; } + bool IsSynced() { return nRequestedZnodeAssets == ZNODE_SYNC_FINISHED; } + + int GetAssetID() { return nRequestedZnodeAssets; } + int GetAttempt() { return nRequestedZnodeAttempt; } + std::string GetAssetName(); + std::string GetSyncStatus(); + + void Reset(); + void SwitchToNextAsset(); + + void ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); + void ProcessTick(); + + void UpdatedBlockTip(const CBlockIndex *pindex); +}; + +#endif diff --git a/src/znode.cpp b/src/znode.cpp new file mode 100644 index 0000000000..17282dd594 --- /dev/null +++ b/src/znode.cpp @@ -0,0 +1,936 @@ +// Copyright (c) 2014-2017 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "activeznode.h" +#include "consensus/validation.h" +#include "darksend.h" +#include "init.h" +//#include "governance.h" +#include "znode.h" +#include "znode-payments.h" +#include "znode-sync.h" +#include "znodeman.h" +#include "util.h" + +#include + + +CZnode::CZnode() : + vin(), + addr(), + pubKeyCollateralAddress(), + pubKeyZnode(), + lastPing(), + vchSig(), + sigTime(GetAdjustedTime()), + nLastDsq(0), + nTimeLastChecked(0), + nTimeLastPaid(0), + nTimeLastWatchdogVote(0), + nActiveState(ZNODE_ENABLED), + nCacheCollateralBlock(0), + nBlockLastPaid(0), + nProtocolVersion(PROTOCOL_VERSION), + nPoSeBanScore(0), + nPoSeBanHeight(0), + fAllowMixingTx(true), + fUnitTest(false) +{} + +CZnode::CZnode(CService addrNew, CTxIn vinNew, CPubKey pubKeyCollateralAddressNew, CPubKey pubKeyZnodeNew, int nProtocolVersionIn) : + vin(vinNew), + addr(addrNew), + pubKeyCollateralAddress(pubKeyCollateralAddressNew), + pubKeyZnode(pubKeyZnodeNew), + lastPing(), + vchSig(), + sigTime(GetAdjustedTime()), + nLastDsq(0), + nTimeLastChecked(0), + nTimeLastPaid(0), + nTimeLastWatchdogVote(0), + nActiveState(ZNODE_ENABLED), + nCacheCollateralBlock(0), + nBlockLastPaid(0), + nProtocolVersion(nProtocolVersionIn), + nPoSeBanScore(0), + nPoSeBanHeight(0), + fAllowMixingTx(true), + fUnitTest(false) +{} + +CZnode::CZnode(const CZnode& other) : + vin(other.vin), + addr(other.addr), + pubKeyCollateralAddress(other.pubKeyCollateralAddress), + pubKeyZnode(other.pubKeyZnode), + lastPing(other.lastPing), + vchSig(other.vchSig), + sigTime(other.sigTime), + nLastDsq(other.nLastDsq), + nTimeLastChecked(other.nTimeLastChecked), + nTimeLastPaid(other.nTimeLastPaid), + nTimeLastWatchdogVote(other.nTimeLastWatchdogVote), + nActiveState(other.nActiveState), + nCacheCollateralBlock(other.nCacheCollateralBlock), + nBlockLastPaid(other.nBlockLastPaid), + nProtocolVersion(other.nProtocolVersion), + nPoSeBanScore(other.nPoSeBanScore), + nPoSeBanHeight(other.nPoSeBanHeight), + fAllowMixingTx(other.fAllowMixingTx), + fUnitTest(other.fUnitTest) +{} + +CZnode::CZnode(const CZnodeBroadcast& mnb) : + vin(mnb.vin), + addr(mnb.addr), + pubKeyCollateralAddress(mnb.pubKeyCollateralAddress), + pubKeyZnode(mnb.pubKeyZnode), + lastPing(mnb.lastPing), + vchSig(mnb.vchSig), + sigTime(mnb.sigTime), + nLastDsq(0), + nTimeLastChecked(0), + nTimeLastPaid(0), + nTimeLastWatchdogVote(mnb.sigTime), + nActiveState(mnb.nActiveState), + nCacheCollateralBlock(0), + nBlockLastPaid(0), + nProtocolVersion(mnb.nProtocolVersion), + nPoSeBanScore(0), + nPoSeBanHeight(0), + fAllowMixingTx(true), + fUnitTest(false) +{} +//CSporkManager sporkManager; +// +// When a new znode broadcast is sent, update our information +// +bool CZnode::UpdateFromNewBroadcast(CZnodeBroadcast& mnb) +{ + if(mnb.sigTime <= sigTime && !mnb.fRecovery) return false; + + pubKeyZnode = mnb.pubKeyZnode; + sigTime = mnb.sigTime; + vchSig = mnb.vchSig; + nProtocolVersion = mnb.nProtocolVersion; + addr = mnb.addr; + nPoSeBanScore = 0; + nPoSeBanHeight = 0; + nTimeLastChecked = 0; + int nDos = 0; + if(mnb.lastPing == CZnodePing() || (mnb.lastPing != CZnodePing() && mnb.lastPing.CheckAndUpdate(this, true, nDos))) { + lastPing = mnb.lastPing; + mnodeman.mapSeenZnodePing.insert(std::make_pair(lastPing.GetHash(), lastPing)); + } + // if it matches our Znode privkey... + if(fZNode && pubKeyZnode == activeZnode.pubKeyZnode) { + nPoSeBanScore = -ZNODE_POSE_BAN_MAX_SCORE; + if(nProtocolVersion == PROTOCOL_VERSION) { + // ... and PROTOCOL_VERSION, then we've been remotely activated ... + activeZnode.ManageState(); + } else { + // ... otherwise we need to reactivate our node, do not add it to the list and do not relay + // but also do not ban the node we get this message from + LogPrintf("CZnode::UpdateFromNewBroadcast -- wrong PROTOCOL_VERSION, re-activate your MN: message nProtocolVersion=%d PROTOCOL_VERSION=%d\n", nProtocolVersion, PROTOCOL_VERSION); + return false; + } + } + return true; +} + +// +// Deterministically calculate a given "score" for a Znode 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 +// +arith_uint256 CZnode::CalculateScore(const uint256& blockHash) +{ + uint256 aux = ArithToUint256(UintToArith256(vin.prevout.hash) + vin.prevout.n); + + CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); + ss << blockHash; + arith_uint256 hash2 = UintToArith256(ss.GetHash()); + + CHashWriter ss2(SER_GETHASH, PROTOCOL_VERSION); + ss2 << blockHash; + ss2 << aux; + arith_uint256 hash3 = UintToArith256(ss2.GetHash()); + + return (hash3 > hash2 ? hash3 - hash2 : hash2 - hash3); +} + +void CZnode::Check(bool fForce) +{ + LOCK(cs); + + if(ShutdownRequested()) return; + + if(!fForce && (GetTime() - nTimeLastChecked < ZNODE_CHECK_SECONDS)) return; + nTimeLastChecked = GetTime(); + + LogPrint("znode", "CZnode::Check -- Znode %s is in %s state\n", vin.prevout.ToStringShort(), GetStateString()); + + //once spent, stop doing the checks + if(IsOutpointSpent()) return; + + int nHeight = 0; + if(!fUnitTest) { + TRY_LOCK(cs_main, lockMain); + if(!lockMain) return; + + CCoins coins; + if(!pcoinsTip->GetCoins(vin.prevout.hash, coins) || + (unsigned int)vin.prevout.n>=coins.vout.size() || + coins.vout[vin.prevout.n].IsNull()) { + nActiveState = ZNODE_OUTPOINT_SPENT; + LogPrint("znode", "CZnode::Check -- Failed to find Znode UTXO, znode=%s\n", vin.prevout.ToStringShort()); + return; + } + + nHeight = chainActive.Height(); + } + + if(IsPoSeBanned()) { + if(nHeight < nPoSeBanHeight) return; // too early? + // Otherwise give it a chance to proceed further to do all the usual checks and to change its state. + // Znode still will be on the edge and can be banned back easily if it keeps ignoring mnverify + // or connect attempts. Will require few mnverify messages to strengthen its position in mn list. + LogPrintf("CZnode::Check -- Znode %s is unbanned and back in list now\n", vin.prevout.ToStringShort()); + DecreasePoSeBanScore(); + } else if(nPoSeBanScore >= ZNODE_POSE_BAN_MAX_SCORE) { + nActiveState = ZNODE_POSE_BAN; + // ban for the whole payment cycle + nPoSeBanHeight = nHeight + mnodeman.size(); + LogPrintf("CZnode::Check -- Znode %s is banned till block %d now\n", vin.prevout.ToStringShort(), nPoSeBanHeight); + return; + } + + int nActiveStatePrev = nActiveState; + bool fOurZnode = fZNode && activeZnode.pubKeyZnode == pubKeyZnode; + + // znode doesn't meet payment protocol requirements ... + bool fRequireUpdate = nProtocolVersion < mnpayments.GetMinZnodePaymentsProto() || + // or it's our own node and we just updated it to the new protocol but we are still waiting for activation ... + (fOurZnode && nProtocolVersion < PROTOCOL_VERSION); + + if(fRequireUpdate) { + nActiveState = ZNODE_UPDATE_REQUIRED; + if(nActiveStatePrev != nActiveState) { + LogPrint("znode", "CZnode::Check -- Znode %s is in %s state now\n", vin.prevout.ToStringShort(), GetStateString()); + } + return; + } + + // keep old znodes on start, give them a chance to receive updates... + bool fWaitForPing = !znodeSync.IsZnodeListSynced() && !IsPingedWithin(ZNODE_MIN_MNP_SECONDS); + + if(fWaitForPing && !fOurZnode) { + // ...but if it was already expired before the initial check - return right away + if(IsExpired() || IsWatchdogExpired() || IsNewStartRequired()) { + LogPrint("znode", "CZnode::Check -- Znode %s is in %s state, waiting for ping\n", vin.prevout.ToStringShort(), GetStateString()); + return; + } + } + + // don't expire if we are still in "waiting for ping" mode unless it's our own znode + if(!fWaitForPing || fOurZnode) { + + if(!IsPingedWithin(ZNODE_NEW_START_REQUIRED_SECONDS)) { + nActiveState = ZNODE_NEW_START_REQUIRED; + if(nActiveStatePrev != nActiveState) { + LogPrint("znode", "CZnode::Check -- Znode %s is in %s state now\n", vin.prevout.ToStringShort(), GetStateString()); + } + return; + } + + bool fWatchdogActive = znodeSync.IsSynced() && mnodeman.IsWatchdogActive(); + bool fWatchdogExpired = (fWatchdogActive && ((GetTime() - nTimeLastWatchdogVote) > ZNODE_WATCHDOG_MAX_SECONDS)); + + LogPrint("znode", "CZnode::Check -- outpoint=%s, nTimeLastWatchdogVote=%d, GetTime()=%d, fWatchdogExpired=%d\n", + vin.prevout.ToStringShort(), nTimeLastWatchdogVote, GetTime(), fWatchdogExpired); + + if(fWatchdogExpired) { + nActiveState = ZNODE_WATCHDOG_EXPIRED; + if(nActiveStatePrev != nActiveState) { + LogPrint("znode", "CZnode::Check -- Znode %s is in %s state now\n", vin.prevout.ToStringShort(), GetStateString()); + } + return; + } + + if(!IsPingedWithin(ZNODE_EXPIRATION_SECONDS)) { + nActiveState = ZNODE_EXPIRED; + if(nActiveStatePrev != nActiveState) { + LogPrint("znode", "CZnode::Check -- Znode %s is in %s state now\n", vin.prevout.ToStringShort(), GetStateString()); + } + return; + } + } + + if(lastPing.sigTime - sigTime < ZNODE_MIN_MNP_SECONDS) { + nActiveState = ZNODE_PRE_ENABLED; + if(nActiveStatePrev != nActiveState) { + LogPrint("znode", "CZnode::Check -- Znode %s is in %s state now\n", vin.prevout.ToStringShort(), GetStateString()); + } + return; + } + + nActiveState = ZNODE_ENABLED; // OK + if(nActiveStatePrev != nActiveState) { + LogPrint("znode", "CZnode::Check -- Znode %s is in %s state now\n", vin.prevout.ToStringShort(), GetStateString()); + } +} + +bool CZnode::IsValidNetAddr() +{ + return IsValidNetAddr(addr); +} + +bool CZnode::IsValidForPayment() +{ + if(nActiveState == ZNODE_ENABLED) { + return true; + } +// if(!sporkManager.IsSporkActive(SPORK_14_REQUIRE_SENTINEL_FLAG) && +// (nActiveState == ZNODE_WATCHDOG_EXPIRED)) { +// return true; +// } + + return false; +} + +bool CZnode::IsValidNetAddr(CService addrIn) +{ + // 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().NetworkIDString() == CBaseChainParams::REGTEST || + (addrIn.IsIPv4() && IsReachable(addrIn) && addrIn.IsRoutable()); +} + +znode_info_t CZnode::GetInfo() +{ + znode_info_t info; + info.vin = vin; + info.addr = addr; + info.pubKeyCollateralAddress = pubKeyCollateralAddress; + info.pubKeyZnode = pubKeyZnode; + info.sigTime = sigTime; + info.nLastDsq = nLastDsq; + info.nTimeLastChecked = nTimeLastChecked; + info.nTimeLastPaid = nTimeLastPaid; + info.nTimeLastWatchdogVote = nTimeLastWatchdogVote; + info.nTimeLastPing = lastPing.sigTime; + info.nActiveState = nActiveState; + info.nProtocolVersion = nProtocolVersion; + info.fInfoValid = true; + return info; +} + +std::string CZnode::StateToString(int nStateIn) +{ + switch(nStateIn) { + case ZNODE_PRE_ENABLED: return "PRE_ENABLED"; + case ZNODE_ENABLED: return "ENABLED"; + case ZNODE_EXPIRED: return "EXPIRED"; + case ZNODE_OUTPOINT_SPENT: return "OUTPOINT_SPENT"; + case ZNODE_UPDATE_REQUIRED: return "UPDATE_REQUIRED"; + case ZNODE_WATCHDOG_EXPIRED: return "WATCHDOG_EXPIRED"; + case ZNODE_NEW_START_REQUIRED: return "NEW_START_REQUIRED"; + case ZNODE_POSE_BAN: return "POSE_BAN"; + default: return "UNKNOWN"; + } +} + +std::string CZnode::GetStateString() const +{ + return StateToString(nActiveState); +} + +std::string CZnode::GetStatus() const +{ + // TODO: return smth a bit more human readable here + return GetStateString(); +} + +int CZnode::GetCollateralAge() +{ + int nHeight; + { + TRY_LOCK(cs_main, lockMain); + if(!lockMain || !chainActive.Tip()) return -1; + nHeight = chainActive.Height(); + } + + if (nCacheCollateralBlock == 0) { + int nInputAge = GetInputAge(vin); + if(nInputAge > 0) { + nCacheCollateralBlock = nHeight - nInputAge; + } else { + return nInputAge; + } + } + + return nHeight - nCacheCollateralBlock; +} + +void CZnode::UpdateLastPaid(const CBlockIndex *pindex, int nMaxBlocksToScanBack) +{ + if(!pindex) return; + + const CBlockIndex *BlockReading = pindex; + + CScript mnpayee = GetScriptForDestination(pubKeyCollateralAddress.GetID()); + // LogPrint("znode", "CZnode::UpdateLastPaidBlock -- searching for block with payment to %s\n", vin.prevout.ToStringShort()); + + LOCK(cs_mapZnodeBlocks); + + for (int i = 0; BlockReading && BlockReading->nHeight > nBlockLastPaid && i < nMaxBlocksToScanBack; i++) { + if(mnpayments.mapZnodeBlocks.count(BlockReading->nHeight) && + mnpayments.mapZnodeBlocks[BlockReading->nHeight].HasPayeeWithVotes(mnpayee, 2)) + { + CBlock block; + if(!ReadBlockFromDisk(block, BlockReading, Params().GetConsensus())) // shouldn't really happen + continue; + + CAmount nZnodePayment = GetZnodePayment(BlockReading->nHeight, block.vtx[0].GetValueOut()); + + BOOST_FOREACH(CTxOut txout, block.vtx[0].vout) + if(mnpayee == txout.scriptPubKey && nZnodePayment == txout.nValue) { + nBlockLastPaid = BlockReading->nHeight; + nTimeLastPaid = BlockReading->nTime; + LogPrint("znode", "CZnode::UpdateLastPaidBlock -- searching for block with payment to %s -- found new %d\n", vin.prevout.ToStringShort(), nBlockLastPaid); + return; + } + } + + if (BlockReading->pprev == NULL) { assert(BlockReading); break; } + BlockReading = BlockReading->pprev; + } + + // Last payment for this znode wasn't found in latest mnpayments blocks + // or it was found in mnpayments blocks but wasn't found in the blockchain. + // LogPrint("znode", "CZnode::UpdateLastPaidBlock -- searching for block with payment to %s -- keeping old %d\n", vin.prevout.ToStringShort(), nBlockLastPaid); +} + +bool CZnodeBroadcast::Create(std::string strService, std::string strKeyZnode, std::string strTxHash, std::string strOutputIndex, std::string& strErrorRet, CZnodeBroadcast &mnbRet, bool fOffline) +{ + CTxIn txin; + CPubKey pubKeyCollateralAddressNew; + CKey keyCollateralAddressNew; + CPubKey pubKeyZnodeNew; + CKey keyZnodeNew; + + //need correct blocks to send ping + if(!fOffline && !znodeSync.IsBlockchainSynced()) { + strErrorRet = "Sync in progress. Must wait until sync is complete to start Znode"; + LogPrintf("CZnodeBroadcast::Create -- %s\n", strErrorRet); + return false; + } + + //TODO + if(!darkSendSigner.GetKeysFromSecret(strKeyZnode, keyZnodeNew, pubKeyZnodeNew)) { + strErrorRet = strprintf("Invalid znode key %s", strKeyZnode); + LogPrintf("CZnodeBroadcast::Create -- %s\n", strErrorRet); + return false; + } + + if(!pwalletMain->GetZnodeVinAndKeys(txin, pubKeyCollateralAddressNew, keyCollateralAddressNew, strTxHash, strOutputIndex)) { + strErrorRet = strprintf("Could not allocate txin %s:%s for znode %s", strTxHash, strOutputIndex, strService); + LogPrintf("CZnodeBroadcast::Create -- %s\n", strErrorRet); + return false; + } + + CService service = CService(strService); + int mainnetDefaultPort = Params(CBaseChainParams::MAIN).GetDefaultPort(); + if(Params().NetworkIDString() == CBaseChainParams::MAIN) { + if(service.GetPort() != mainnetDefaultPort) { + strErrorRet = strprintf("Invalid port %u for znode %s, only %d is supported on mainnet.", service.GetPort(), strService, mainnetDefaultPort); + LogPrintf("CZnodeBroadcast::Create -- %s\n", strErrorRet); + return false; + } + } else if (service.GetPort() == mainnetDefaultPort) { + strErrorRet = strprintf("Invalid port %u for znode %s, %d is the only supported on mainnet.", service.GetPort(), strService, mainnetDefaultPort); + LogPrintf("CZnodeBroadcast::Create -- %s\n", strErrorRet); + return false; + } + + return Create(txin, CService(strService), keyCollateralAddressNew, pubKeyCollateralAddressNew, keyZnodeNew, pubKeyZnodeNew, strErrorRet, mnbRet); +} + +bool CZnodeBroadcast::Create(CTxIn txin, CService service, CKey keyCollateralAddressNew, CPubKey pubKeyCollateralAddressNew, CKey keyZnodeNew, CPubKey pubKeyZnodeNew, std::string &strErrorRet, CZnodeBroadcast &mnbRet) +{ + // wait for reindex and/or import to finish + if (fImporting || fReindex) return false; + + LogPrint("znode", "CZnodeBroadcast::Create -- pubKeyCollateralAddressNew = %s, pubKeyZnodeNew.GetID() = %s\n", + CBitcoinAddress(pubKeyCollateralAddressNew.GetID()).ToString(), + pubKeyZnodeNew.GetID().ToString()); + + + CZnodePing mnp(txin); + if(!mnp.Sign(keyZnodeNew, pubKeyZnodeNew)) { + strErrorRet = strprintf("Failed to sign ping, znode=%s", txin.prevout.ToStringShort()); + LogPrintf("CZnodeBroadcast::Create -- %s\n", strErrorRet); + mnbRet = CZnodeBroadcast(); + return false; + } + + mnbRet = CZnodeBroadcast(service, txin, pubKeyCollateralAddressNew, pubKeyZnodeNew, PROTOCOL_VERSION); + + if(!mnbRet.IsValidNetAddr()) { + strErrorRet = strprintf("Invalid IP address, znode=%s", txin.prevout.ToStringShort()); + LogPrintf("CZnodeBroadcast::Create -- %s\n", strErrorRet); + mnbRet = CZnodeBroadcast(); + return false; + } + + mnbRet.lastPing = mnp; + if(!mnbRet.Sign(keyCollateralAddressNew)) { + strErrorRet = strprintf("Failed to sign broadcast, znode=%s", txin.prevout.ToStringShort()); + LogPrintf("CZnodeBroadcast::Create -- %s\n", strErrorRet); + mnbRet = CZnodeBroadcast(); + return false; + } + + return true; +} + +bool CZnodeBroadcast::SimpleCheck(int& nDos) +{ + nDos = 0; + + // make sure addr is valid + if(!IsValidNetAddr()) { + LogPrintf("CZnodeBroadcast::SimpleCheck -- Invalid addr, rejected: znode=%s addr=%s\n", + vin.prevout.ToStringShort(), addr.ToString()); + return false; + } + + // make sure signature isn't in the future (past is OK) + if (sigTime > GetAdjustedTime() + 60 * 60) { + LogPrintf("CZnodeBroadcast::SimpleCheck -- Signature rejected, too far into the future: znode=%s\n", vin.prevout.ToStringShort()); + nDos = 1; + return false; + } + + // empty ping or incorrect sigTime/unknown blockhash + if(lastPing == CZnodePing() || !lastPing.SimpleCheck(nDos)) { + // one of us is probably forked or smth, just mark it as expired and check the rest of the rules + nActiveState = ZNODE_EXPIRED; + } + + if(nProtocolVersion < mnpayments.GetMinZnodePaymentsProto()) { + LogPrintf("CZnodeBroadcast::SimpleCheck -- ignoring outdated Znode: znode=%s nProtocolVersion=%d\n", vin.prevout.ToStringShort(), nProtocolVersion); + return false; + } + + CScript pubkeyScript; + pubkeyScript = GetScriptForDestination(pubKeyCollateralAddress.GetID()); + + if(pubkeyScript.size() != 25) { + LogPrintf("CZnodeBroadcast::SimpleCheck -- pubKeyCollateralAddress has the wrong size\n"); + nDos = 100; + return false; + } + + CScript pubkeyScript2; + pubkeyScript2 = GetScriptForDestination(pubKeyZnode.GetID()); + + if(pubkeyScript2.size() != 25) { + LogPrintf("CZnodeBroadcast::SimpleCheck -- pubKeyZnode has the wrong size\n"); + nDos = 100; + return false; + } + + if(!vin.scriptSig.empty()) { + LogPrintf("CZnodeBroadcast::SimpleCheck -- Ignore Not Empty ScriptSig %s\n",vin.ToString()); + nDos = 100; + return false; + } + + int mainnetDefaultPort = Params(CBaseChainParams::MAIN).GetDefaultPort(); + if(Params().NetworkIDString() == CBaseChainParams::MAIN) { + if(addr.GetPort() != mainnetDefaultPort) return false; + } else if(addr.GetPort() == mainnetDefaultPort) return false; + + return true; +} + +bool CZnodeBroadcast::Update(CZnode* pmn, int& nDos) +{ + nDos = 0; + + if(pmn->sigTime == sigTime && !fRecovery) { + // mapSeenZnodeBroadcast in CZnodeMan::CheckMnbAndUpdateZnodeList should filter legit duplicates + // but this still can happen if we just started, which is ok, just do nothing here. + return false; + } + + // this broadcast is older than the one that we already have - it's bad and should never happen + // unless someone is doing something fishy + if(pmn->sigTime > sigTime) { + LogPrintf("CZnodeBroadcast::Update -- Bad sigTime %d (existing broadcast is at %d) for Znode %s %s\n", + sigTime, pmn->sigTime, vin.prevout.ToStringShort(), addr.ToString()); + return false; + } + + pmn->Check(); + + // znode is banned by PoSe + if(pmn->IsPoSeBanned()) { + LogPrintf("CZnodeBroadcast::Update -- Banned by PoSe, znode=%s\n", vin.prevout.ToStringShort()); + return false; + } + + // IsVnAssociatedWithPubkey is validated once in CheckOutpoint, after that they just need to match + if(pmn->pubKeyCollateralAddress != pubKeyCollateralAddress) { + LogPrintf("CZnodeBroadcast::Update -- Got mismatched pubKeyCollateralAddress and vin\n"); + nDos = 33; + return false; + } + + if (!CheckSignature(nDos)) { + LogPrintf("CZnodeBroadcast::Update -- CheckSignature() failed, znode=%s\n", vin.prevout.ToStringShort()); + return false; + } + + // if ther was no znode broadcast recently or if it matches our Znode privkey... + if(!pmn->IsBroadcastedWithin(ZNODE_MIN_MNB_SECONDS) || (fZNode && pubKeyZnode == activeZnode.pubKeyZnode)) { + // take the newest entry + LogPrintf("CZnodeBroadcast::Update -- Got UPDATED Znode entry: addr=%s\n", addr.ToString()); + if(pmn->UpdateFromNewBroadcast((*this))) { + pmn->Check(); + Relay(); + } + znodeSync.AddedZnodeList(); + } + + return true; +} + +bool CZnodeBroadcast::CheckOutpoint(int& nDos) +{ + // we are a znode with the same vin (i.e. already activated) and this mnb is ours (matches our Znode privkey) + // so nothing to do here for us + if(fZNode && vin.prevout == activeZnode.vin.prevout && pubKeyZnode == activeZnode.pubKeyZnode) { + return false; + } + + if (!CheckSignature(nDos)) { + LogPrintf("CZnodeBroadcast::CheckOutpoint -- CheckSignature() failed, znode=%s\n", vin.prevout.ToStringShort()); + return false; + } + + { + TRY_LOCK(cs_main, lockMain); + if(!lockMain) { + // not mnb fault, let it to be checked again later + LogPrint("znode", "CZnodeBroadcast::CheckOutpoint -- Failed to aquire lock, addr=%s", addr.ToString()); + mnodeman.mapSeenZnodeBroadcast.erase(GetHash()); + return false; + } + + CCoins coins; + if(!pcoinsTip->GetCoins(vin.prevout.hash, coins) || + (unsigned int)vin.prevout.n>=coins.vout.size() || + coins.vout[vin.prevout.n].IsNull()) { + LogPrint("znode", "CZnodeBroadcast::CheckOutpoint -- Failed to find Znode UTXO, znode=%s\n", vin.prevout.ToStringShort()); + return false; + } + if(coins.vout[vin.prevout.n].nValue != 1000 * COIN) { + LogPrint("znode", "CZnodeBroadcast::CheckOutpoint -- Znode UTXO should have 1000 DASH, znode=%s\n", vin.prevout.ToStringShort()); + return false; + } + if(chainActive.Height() - coins.nHeight + 1 < Params().GetConsensus().nZnodeMinimumConfirmations) { + LogPrintf("CZnodeBroadcast::CheckOutpoint -- Znode UTXO must have at least %d confirmations, znode=%s\n", + Params().GetConsensus().nZnodeMinimumConfirmations, vin.prevout.ToStringShort()); + // maybe we miss few blocks, let this mnb to be checked again later + mnodeman.mapSeenZnodeBroadcast.erase(GetHash()); + return false; + } + } + + LogPrint("znode", "CZnodeBroadcast::CheckOutpoint -- Znode UTXO verified\n"); + + // make sure the vout that was signed is related to the transaction that spawned the Znode + // - this is expensive, so it's only done once per Znode + if(!darkSendSigner.IsVinAssociatedWithPubkey(vin, pubKeyCollateralAddress)) { + LogPrintf("CZnodeMan::CheckOutpoint -- Got mismatched pubKeyCollateralAddress and vin\n"); + nDos = 33; + return false; + } + + // verify that sig time is legit in past + // should be at least not earlier than block when 1000 DASH tx got nZnodeMinimumConfirmations + uint256 hashBlock = uint256(); + CTransaction tx2; + GetTransaction(vin.prevout.hash, tx2, Params().GetConsensus(), hashBlock, true); + { + LOCK(cs_main); + BlockMap::iterator mi = mapBlockIndex.find(hashBlock); + if (mi != mapBlockIndex.end() && (*mi).second) { + CBlockIndex* pMNIndex = (*mi).second; // block for 1000 DASH tx -> 1 confirmation + CBlockIndex* pConfIndex = chainActive[pMNIndex->nHeight + Params().GetConsensus().nZnodeMinimumConfirmations - 1]; // block where tx got nZnodeMinimumConfirmations + if(pConfIndex->GetBlockTime() > sigTime) { + LogPrintf("CZnodeBroadcast::CheckOutpoint -- Bad sigTime %d (%d conf block is at %d) for Znode %s %s\n", + sigTime, Params().GetConsensus().nZnodeMinimumConfirmations, pConfIndex->GetBlockTime(), vin.prevout.ToStringShort(), addr.ToString()); + return false; + } + } + } + + return true; +} + +bool CZnodeBroadcast::Sign(CKey& keyCollateralAddress) +{ + std::string strError; + std::string strMessage; + + sigTime = GetAdjustedTime(); + + strMessage = addr.ToString() + boost::lexical_cast(sigTime) + + pubKeyCollateralAddress.GetID().ToString() + pubKeyZnode.GetID().ToString() + + boost::lexical_cast(nProtocolVersion); + + if(!darkSendSigner.SignMessage(strMessage, vchSig, keyCollateralAddress)) { + LogPrintf("CZnodeBroadcast::Sign -- SignMessage() failed\n"); + return false; + } + + if(!darkSendSigner.VerifyMessage(pubKeyCollateralAddress, vchSig, strMessage, strError)) { + LogPrintf("CZnodeBroadcast::Sign -- VerifyMessage() failed, error: %s\n", strError); + return false; + } + + return true; +} + +bool CZnodeBroadcast::CheckSignature(int& nDos) +{ + std::string strMessage; + std::string strError = ""; + nDos = 0; + + strMessage = addr.ToString() + boost::lexical_cast(sigTime) + + pubKeyCollateralAddress.GetID().ToString() + pubKeyZnode.GetID().ToString() + + boost::lexical_cast(nProtocolVersion); + + LogPrint("znode", "CZnodeBroadcast::CheckSignature -- strMessage: %s pubKeyCollateralAddress address: %s sig: %s\n", strMessage, CBitcoinAddress(pubKeyCollateralAddress.GetID()).ToString(), EncodeBase64(&vchSig[0], vchSig.size())); + + if(!darkSendSigner.VerifyMessage(pubKeyCollateralAddress, vchSig, strMessage, strError)){ + LogPrintf("CZnodeBroadcast::CheckSignature -- Got bad Znode announce signature, error: %s\n", strError); + nDos = 100; + return false; + } + + return true; +} + +void CZnodeBroadcast::Relay() +{ + CInv inv(MSG_ZNODE_ANNOUNCE, GetHash()); + RelayInv(inv); +} + +CZnodePing::CZnodePing(CTxIn& vinNew) +{ + LOCK(cs_main); + if (!chainActive.Tip() || chainActive.Height() < 12) return; + + vin = vinNew; + blockHash = chainActive[chainActive.Height() - 12]->GetBlockHash(); + sigTime = GetAdjustedTime(); + vchSig = std::vector(); +} + +bool CZnodePing::Sign(CKey& keyZnode, CPubKey& pubKeyZnode) +{ + std::string strError; + std::string strZNodeSignMessage; + + sigTime = GetAdjustedTime(); + std::string strMessage = vin.ToString() + blockHash.ToString() + boost::lexical_cast(sigTime); + + if(!darkSendSigner.SignMessage(strMessage, vchSig, keyZnode)) { + LogPrintf("CZnodePing::Sign -- SignMessage() failed\n"); + return false; + } + + if(!darkSendSigner.VerifyMessage(pubKeyZnode, vchSig, strMessage, strError)) { + LogPrintf("CZnodePing::Sign -- VerifyMessage() failed, error: %s\n", strError); + return false; + } + + return true; +} + +bool CZnodePing::CheckSignature(CPubKey& pubKeyZnode, int &nDos) +{ + std::string strMessage = vin.ToString() + blockHash.ToString() + boost::lexical_cast(sigTime); + std::string strError = ""; + nDos = 0; + + if(!darkSendSigner.VerifyMessage(pubKeyZnode, vchSig, strMessage, strError)) { + LogPrintf("CZnodePing::CheckSignature -- Got bad Znode ping signature, znode=%s, error: %s\n", vin.prevout.ToStringShort(), strError); + nDos = 33; + return false; + } + return true; +} + +bool CZnodePing::SimpleCheck(int& nDos) +{ + // don't ban by default + nDos = 0; + + if (sigTime > GetAdjustedTime() + 60 * 60) { + LogPrintf("CZnodePing::SimpleCheck -- Signature rejected, too far into the future, znode=%s\n", vin.prevout.ToStringShort()); + nDos = 1; + return false; + } + + { + LOCK(cs_main); + BlockMap::iterator mi = mapBlockIndex.find(blockHash); + if (mi == mapBlockIndex.end()) { + LogPrint("znode", "CZnodePing::SimpleCheck -- Znode ping is invalid, unknown block hash: znode=%s blockHash=%s\n", vin.prevout.ToStringShort(), blockHash.ToString()); + // maybe we stuck or forked so we shouldn't ban this node, just fail to accept this ping + // TODO: or should we also request this block? + return false; + } + } + LogPrint("znode", "CZnodePing::SimpleCheck -- Znode ping verified: znode=%s blockHash=%s sigTime=%d\n", vin.prevout.ToStringShort(), blockHash.ToString(), sigTime); + return true; +} + +bool CZnodePing::CheckAndUpdate(CZnode* pmn, bool fFromNewBroadcast, int& nDos) +{ + // don't ban by default + nDos = 0; + + if (!SimpleCheck(nDos)) { + return false; + } + + if (pmn == NULL) { + LogPrint("znode", "CZnodePing::CheckAndUpdate -- Couldn't find Znode entry, znode=%s\n", vin.prevout.ToStringShort()); + return false; + } + + if(!fFromNewBroadcast) { + if (pmn->IsUpdateRequired()) { + LogPrint("znode", "CZnodePing::CheckAndUpdate -- znode protocol is outdated, znode=%s\n", vin.prevout.ToStringShort()); + return false; + } + + if (pmn->IsNewStartRequired()) { + LogPrint("znode", "CZnodePing::CheckAndUpdate -- znode is completely expired, new start is required, znode=%s\n", vin.prevout.ToStringShort()); + return false; + } + } + + { + LOCK(cs_main); + BlockMap::iterator mi = mapBlockIndex.find(blockHash); + if ((*mi).second && (*mi).second->nHeight < chainActive.Height() - 24) { + LogPrintf("CZnodePing::CheckAndUpdate -- Znode ping is invalid, block hash is too old: znode=%s blockHash=%s\n", vin.prevout.ToStringShort(), blockHash.ToString()); + // nDos = 1; + return false; + } + } + + LogPrint("znode", "CZnodePing::CheckAndUpdate -- New ping: znode=%s blockHash=%s sigTime=%d\n", vin.prevout.ToStringShort(), blockHash.ToString(), sigTime); + + // LogPrintf("mnping - Found corresponding mn for vin: %s\n", vin.prevout.ToStringShort()); + // update only if there is no known ping for this znode or + // last ping was more then ZNODE_MIN_MNP_SECONDS-60 ago comparing to this one + if (pmn->IsPingedWithin(ZNODE_MIN_MNP_SECONDS - 60, sigTime)) { + LogPrint("znode", "CZnodePing::CheckAndUpdate -- Znode ping arrived too early, znode=%s\n", vin.prevout.ToStringShort()); + //nDos = 1; //disable, this is happening frequently and causing banned peers + return false; + } + + if (!CheckSignature(pmn->pubKeyZnode, nDos)) return false; + + // so, ping seems to be ok + + // if we are still syncing and there was no known ping for this mn for quite a while + // (NOTE: assuming that ZNODE_EXPIRATION_SECONDS/2 should be enough to finish mn list sync) + if(!znodeSync.IsZnodeListSynced() && !pmn->IsPingedWithin(ZNODE_EXPIRATION_SECONDS/2)) { + // let's bump sync timeout + LogPrint("znode", "CZnodePing::CheckAndUpdate -- bumping sync timeout, znode=%s\n", vin.prevout.ToStringShort()); + znodeSync.AddedZnodeList(); + } + + // let's store this ping as the last one + LogPrint("znode", "CZnodePing::CheckAndUpdate -- Znode ping accepted, znode=%s\n", vin.prevout.ToStringShort()); + pmn->lastPing = *this; + + // and update mnodeman.mapSeenZnodeBroadcast.lastPing which is probably outdated + CZnodeBroadcast mnb(*pmn); + uint256 hash = mnb.GetHash(); + if (mnodeman.mapSeenZnodeBroadcast.count(hash)) { + mnodeman.mapSeenZnodeBroadcast[hash].second.lastPing = *this; + } + + pmn->Check(true); // force update, ignoring cache + if (!pmn->IsEnabled()) return false; + + LogPrint("znode", "CZnodePing::CheckAndUpdate -- Znode ping acceepted and relayed, znode=%s\n", vin.prevout.ToStringShort()); + Relay(); + + return true; +} + +void CZnodePing::Relay() +{ + CInv inv(MSG_ZNODE_PING, GetHash()); + RelayInv(inv); +} + +//void CZnode::AddGovernanceVote(uint256 nGovernanceObjectHash) +//{ +// if(mapGovernanceObjectsVotedOn.count(nGovernanceObjectHash)) { +// mapGovernanceObjectsVotedOn[nGovernanceObjectHash]++; +// } else { +// mapGovernanceObjectsVotedOn.insert(std::make_pair(nGovernanceObjectHash, 1)); +// } +//} + +//void CZnode::RemoveGovernanceObject(uint256 nGovernanceObjectHash) +//{ +// std::map::iterator it = mapGovernanceObjectsVotedOn.find(nGovernanceObjectHash); +// if(it == mapGovernanceObjectsVotedOn.end()) { +// return; +// } +// mapGovernanceObjectsVotedOn.erase(it); +//} + +void CZnode::UpdateWatchdogVoteTime() +{ + LOCK(cs); + nTimeLastWatchdogVote = GetTime(); +} + +/** +* FLAG GOVERNANCE ITEMS AS DIRTY +* +* - When znode come and go on the network, we must flag the items they voted on to recalc it's cached flags +* +*/ +//void CZnode::FlagGovernanceItemsAsDirty() +//{ +// std::vector vecDirty; +// { +// std::map::iterator it = mapGovernanceObjectsVotedOn.begin(); +// while(it != mapGovernanceObjectsVotedOn.end()) { +// vecDirty.push_back(it->first); +// ++it; +// } +// } +// for(size_t i = 0; i < vecDirty.size(); ++i) { +// mnodeman.AddDirtyGovernanceObjectHash(vecDirty[i]); +// } +//} diff --git a/src/znode.h b/src/znode.h new file mode 100644 index 0000000000..ab4e1781c3 --- /dev/null +++ b/src/znode.h @@ -0,0 +1,440 @@ +// Copyright (c) 2014-2017 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef ZNODE_H +#define ZNODE_H + +#include "key.h" +#include "main.h" +#include "net.h" +#include "spork.h" +#include "timedata.h" +#include "utiltime.h" + +class CZnode; +class CZnodeBroadcast; +class CZnodePing; + +static const int ZNODE_CHECK_SECONDS = 5; +static const int ZNODE_MIN_MNB_SECONDS = 5 * 60; +static const int ZNODE_MIN_MNP_SECONDS = 10 * 60; +static const int ZNODE_EXPIRATION_SECONDS = 65 * 60; +static const int ZNODE_WATCHDOG_MAX_SECONDS = 120 * 60; +static const int ZNODE_NEW_START_REQUIRED_SECONDS = 180 * 60; + +static const int ZNODE_POSE_BAN_MAX_SCORE = 5; +// +// The Znode Ping Class : Contains a different serialize method for sending pings from znodes throughout the network +// + +class CZnodePing +{ +public: + CTxIn vin; + uint256 blockHash; + int64_t sigTime; //mnb message times + std::vector vchSig; + //removed stop + + CZnodePing() : + vin(), + blockHash(), + sigTime(0), + vchSig() + {} + + CZnodePing(CTxIn& vinNew); + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(vin); + READWRITE(blockHash); + READWRITE(sigTime); + READWRITE(vchSig); + } + + void swap(CZnodePing& first, CZnodePing& second) // nothrow + { + // enable ADL (not necessary in our case, but good practice) + using std::swap; + + // by swapping the members of two classes, + // the two classes are effectively swapped + swap(first.vin, second.vin); + swap(first.blockHash, second.blockHash); + swap(first.sigTime, second.sigTime); + swap(first.vchSig, second.vchSig); + } + + uint256 GetHash() const + { + CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); + ss << vin; + ss << sigTime; + return ss.GetHash(); + } + + bool IsExpired() { return GetTime() - sigTime > ZNODE_NEW_START_REQUIRED_SECONDS; } + + bool Sign(CKey& keyZnode, CPubKey& pubKeyZnode); + bool CheckSignature(CPubKey& pubKeyZnode, int &nDos); + bool SimpleCheck(int& nDos); + bool CheckAndUpdate(CZnode* pmn, bool fFromNewBroadcast, int& nDos); + void Relay(); + + CZnodePing& operator=(CZnodePing from) + { + swap(*this, from); + return *this; + } + friend bool operator==(const CZnodePing& a, const CZnodePing& b) + { + return a.vin == b.vin && a.blockHash == b.blockHash; + } + friend bool operator!=(const CZnodePing& a, const CZnodePing& b) + { + return !(a == b); + } + +}; + +struct znode_info_t +{ + znode_info_t() + : vin(), + addr(), + pubKeyCollateralAddress(), + pubKeyZnode(), + sigTime(0), + nLastDsq(0), + nTimeLastChecked(0), + nTimeLastPaid(0), + nTimeLastWatchdogVote(0), + nTimeLastPing(0), + nActiveState(0), + nProtocolVersion(0), + fInfoValid(false) + {} + + CTxIn vin; + CService addr; + CPubKey pubKeyCollateralAddress; + CPubKey pubKeyZnode; + int64_t sigTime; //mnb message time + int64_t nLastDsq; //the dsq count from the last dsq broadcast of this node + int64_t nTimeLastChecked; + int64_t nTimeLastPaid; + int64_t nTimeLastWatchdogVote; + int64_t nTimeLastPing; + int nActiveState; + int nProtocolVersion; + bool fInfoValid; +}; + +// +// The Znode 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 CZnode +{ +private: + // critical section to protect the inner data structures + mutable CCriticalSection cs; + +public: + enum state { + ZNODE_PRE_ENABLED, + ZNODE_ENABLED, + ZNODE_EXPIRED, + ZNODE_OUTPOINT_SPENT, + ZNODE_UPDATE_REQUIRED, + ZNODE_WATCHDOG_EXPIRED, + ZNODE_NEW_START_REQUIRED, + ZNODE_POSE_BAN + }; + + CTxIn vin; + CService addr; + CPubKey pubKeyCollateralAddress; + CPubKey pubKeyZnode; + CZnodePing lastPing; + std::vector vchSig; + int64_t sigTime; //mnb message time + int64_t nLastDsq; //the dsq count from the last dsq broadcast of this node + int64_t nTimeLastChecked; + int64_t nTimeLastPaid; + int64_t nTimeLastWatchdogVote; + int nActiveState; + int nCacheCollateralBlock; + int nBlockLastPaid; + int nProtocolVersion; + int nPoSeBanScore; + int nPoSeBanHeight; + bool fAllowMixingTx; + bool fUnitTest; + + // KEEP TRACK OF GOVERNANCE ITEMS EACH ZNODE HAS VOTE UPON FOR RECALCULATION + std::map mapGovernanceObjectsVotedOn; + + CZnode(); + CZnode(const CZnode& other); + CZnode(const CZnodeBroadcast& mnb); + CZnode(CService addrNew, CTxIn vinNew, CPubKey pubKeyCollateralAddressNew, CPubKey pubKeyZnodeNew, int nProtocolVersionIn); + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + LOCK(cs); + READWRITE(vin); + READWRITE(addr); + READWRITE(pubKeyCollateralAddress); + READWRITE(pubKeyZnode); + READWRITE(lastPing); + READWRITE(vchSig); + READWRITE(sigTime); + READWRITE(nLastDsq); + READWRITE(nTimeLastChecked); + READWRITE(nTimeLastPaid); + READWRITE(nTimeLastWatchdogVote); + READWRITE(nActiveState); + READWRITE(nCacheCollateralBlock); + READWRITE(nBlockLastPaid); + READWRITE(nProtocolVersion); + READWRITE(nPoSeBanScore); + READWRITE(nPoSeBanHeight); + READWRITE(fAllowMixingTx); + READWRITE(fUnitTest); + READWRITE(mapGovernanceObjectsVotedOn); + } + + void swap(CZnode& first, CZnode& second) // nothrow + { + // enable ADL (not necessary in our case, but good practice) + using std::swap; + + // by swapping the members of two classes, + // the two classes are effectively swapped + swap(first.vin, second.vin); + swap(first.addr, second.addr); + swap(first.pubKeyCollateralAddress, second.pubKeyCollateralAddress); + swap(first.pubKeyZnode, second.pubKeyZnode); + swap(first.lastPing, second.lastPing); + swap(first.vchSig, second.vchSig); + swap(first.sigTime, second.sigTime); + swap(first.nLastDsq, second.nLastDsq); + swap(first.nTimeLastChecked, second.nTimeLastChecked); + swap(first.nTimeLastPaid, second.nTimeLastPaid); + swap(first.nTimeLastWatchdogVote, second.nTimeLastWatchdogVote); + swap(first.nActiveState, second.nActiveState); + swap(first.nCacheCollateralBlock, second.nCacheCollateralBlock); + swap(first.nBlockLastPaid, second.nBlockLastPaid); + swap(first.nProtocolVersion, second.nProtocolVersion); + swap(first.nPoSeBanScore, second.nPoSeBanScore); + swap(first.nPoSeBanHeight, second.nPoSeBanHeight); + swap(first.fAllowMixingTx, second.fAllowMixingTx); + swap(first.fUnitTest, second.fUnitTest); + swap(first.mapGovernanceObjectsVotedOn, second.mapGovernanceObjectsVotedOn); + } + + // CALCULATE A RANK AGAINST OF GIVEN BLOCK + arith_uint256 CalculateScore(const uint256& blockHash); + + bool UpdateFromNewBroadcast(CZnodeBroadcast& mnb); + + void Check(bool fForce = false); + + bool IsBroadcastedWithin(int nSeconds) { return GetAdjustedTime() - sigTime < nSeconds; } + + bool IsPingedWithin(int nSeconds, int64_t nTimeToCheckAt = -1) + { + if(lastPing == CZnodePing()) return false; + + if(nTimeToCheckAt == -1) { + nTimeToCheckAt = GetAdjustedTime(); + } + return nTimeToCheckAt - lastPing.sigTime < nSeconds; + } + + bool IsEnabled() { return nActiveState == ZNODE_ENABLED; } + bool IsPreEnabled() { return nActiveState == ZNODE_PRE_ENABLED; } + bool IsPoSeBanned() { return nActiveState == ZNODE_POSE_BAN; } + // NOTE: this one relies on nPoSeBanScore, not on nActiveState as everything else here + bool IsPoSeVerified() { return nPoSeBanScore <= -ZNODE_POSE_BAN_MAX_SCORE; } + bool IsExpired() { return nActiveState == ZNODE_EXPIRED; } + bool IsOutpointSpent() { return nActiveState == ZNODE_OUTPOINT_SPENT; } + bool IsUpdateRequired() { return nActiveState == ZNODE_UPDATE_REQUIRED; } + bool IsWatchdogExpired() { return nActiveState == ZNODE_WATCHDOG_EXPIRED; } + bool IsNewStartRequired() { return nActiveState == ZNODE_NEW_START_REQUIRED; } + + static bool IsValidStateForAutoStart(int nActiveStateIn) + { + return nActiveStateIn == ZNODE_ENABLED || + nActiveStateIn == ZNODE_PRE_ENABLED || + nActiveStateIn == ZNODE_EXPIRED || + nActiveStateIn == ZNODE_WATCHDOG_EXPIRED; + } + + bool IsValidForPayment(); + + bool IsValidNetAddr(); + static bool IsValidNetAddr(CService addrIn); + + void IncreasePoSeBanScore() { if(nPoSeBanScore < ZNODE_POSE_BAN_MAX_SCORE) nPoSeBanScore++; } + void DecreasePoSeBanScore() { if(nPoSeBanScore > -ZNODE_POSE_BAN_MAX_SCORE) nPoSeBanScore--; } + + znode_info_t GetInfo(); + + static std::string StateToString(int nStateIn); + std::string GetStateString() const; + std::string GetStatus() const; + + int GetCollateralAge(); + + int GetLastPaidTime() { return nTimeLastPaid; } + int GetLastPaidBlock() { return nBlockLastPaid; } + void UpdateLastPaid(const CBlockIndex *pindex, int nMaxBlocksToScanBack); + + // KEEP TRACK OF EACH GOVERNANCE ITEM INCASE THIS NODE GOES OFFLINE, SO WE CAN RECALC THEIR STATUS + void AddGovernanceVote(uint256 nGovernanceObjectHash); + // RECALCULATE CACHED STATUS FLAGS FOR ALL AFFECTED OBJECTS + void FlagGovernanceItemsAsDirty(); + + void RemoveGovernanceObject(uint256 nGovernanceObjectHash); + + void UpdateWatchdogVoteTime(); + + CZnode& operator=(CZnode from) + { + swap(*this, from); + return *this; + } + friend bool operator==(const CZnode& a, const CZnode& b) + { + return a.vin == b.vin; + } + friend bool operator!=(const CZnode& a, const CZnode& b) + { + return !(a.vin == b.vin); + } + +}; + + +// +// The Znode Broadcast Class : Contains a different serialize method for sending znodes through the network +// + +class CZnodeBroadcast : public CZnode +{ +public: + + bool fRecovery; + + CZnodeBroadcast() : CZnode(), fRecovery(false) {} + CZnodeBroadcast(const CZnode& mn) : CZnode(mn), fRecovery(false) {} + CZnodeBroadcast(CService addrNew, CTxIn vinNew, CPubKey pubKeyCollateralAddressNew, CPubKey pubKeyZnodeNew, int nProtocolVersionIn) : + CZnode(addrNew, vinNew, pubKeyCollateralAddressNew, pubKeyZnodeNew, nProtocolVersionIn), fRecovery(false) {} + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(vin); + READWRITE(addr); + READWRITE(pubKeyCollateralAddress); + READWRITE(pubKeyZnode); + READWRITE(vchSig); + READWRITE(sigTime); + READWRITE(nProtocolVersion); + READWRITE(lastPing); + } + + uint256 GetHash() const + { + CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); + ss << vin; + ss << pubKeyCollateralAddress; + ss << sigTime; + return ss.GetHash(); + } + + /// Create Znode broadcast, needs to be relayed manually after that + static bool Create(CTxIn vin, CService service, CKey keyCollateralAddressNew, CPubKey pubKeyCollateralAddressNew, CKey keyZnodeNew, CPubKey pubKeyZnodeNew, std::string &strErrorRet, CZnodeBroadcast &mnbRet); + static bool Create(std::string strService, std::string strKey, std::string strTxHash, std::string strOutputIndex, std::string& strErrorRet, CZnodeBroadcast &mnbRet, bool fOffline = false); + + bool SimpleCheck(int& nDos); + bool Update(CZnode* pmn, int& nDos); + bool CheckOutpoint(int& nDos); + + bool Sign(CKey& keyCollateralAddress); + bool CheckSignature(int& nDos); + void Relay(); +}; + +class CZnodeVerification +{ +public: + CTxIn vin1; + CTxIn vin2; + CService addr; + int nonce; + int nBlockHeight; + std::vector vchSig1; + std::vector vchSig2; + + CZnodeVerification() : + vin1(), + vin2(), + addr(), + nonce(0), + nBlockHeight(0), + vchSig1(), + vchSig2() + {} + + CZnodeVerification(CService addr, int nonce, int nBlockHeight) : + vin1(), + vin2(), + addr(addr), + nonce(nonce), + nBlockHeight(nBlockHeight), + vchSig1(), + vchSig2() + {} + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(vin1); + READWRITE(vin2); + READWRITE(addr); + READWRITE(nonce); + READWRITE(nBlockHeight); + READWRITE(vchSig1); + READWRITE(vchSig2); + } + + uint256 GetHash() const + { + CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); + ss << vin1; + ss << vin2; + ss << addr; + ss << nonce; + ss << nBlockHeight; + return ss.GetHash(); + } + + void Relay() const + { + CInv inv(MSG_ZNODE_VERIFY, GetHash()); + RelayInv(inv); + } +}; + +#endif diff --git a/src/znodeconfig.cpp b/src/znodeconfig.cpp new file mode 100644 index 0000000000..d24a5bb42c --- /dev/null +++ b/src/znodeconfig.cpp @@ -0,0 +1,91 @@ + +#include "netbase.h" +#include "znodeconfig.h" +#include "util.h" +#include "chainparams.h" + +#include +#include + +CZnodeConfig znodeConfig; + +void CZnodeConfig::add(std::string alias, std::string ip, std::string privKey, std::string txHash, std::string outputIndex) { + CZnodeEntry cme(alias, ip, privKey, txHash, outputIndex); + entries.push_back(cme); +} + +bool CZnodeConfig::read(std::string& strErr) { + int linenumber = 1; + boost::filesystem::path pathZnodeConfigFile = GetZnodeConfigFile(); + boost::filesystem::ifstream streamConfig(pathZnodeConfigFile); + + if (!streamConfig.good()) { + FILE* configFile = fopen(pathZnodeConfigFile.string().c_str(), "a"); + if (configFile != NULL) { + std::string strHeader = "# Znode config file\n" + "# Format: alias IP:port znodeprivkey collateral_output_txid collateral_output_index\n" + "# Example: mn1 127.0.0.2:19999 93HaYBVUCYjEMeeH1Y4sBGLALQZE1Yc1K64xiqgX37tGBDQL8Xg 2bcd3c84c84f87eaa86e4e56834c92927a07f9e18718810b92e0d0324456a67c 0\n"; + fwrite(strHeader.c_str(), std::strlen(strHeader.c_str()), 1, configFile); + fclose(configFile); + } + return true; // Nothing to read, so just return + } + + for(std::string line; std::getline(streamConfig, line); linenumber++) + { + if(line.empty()) continue; + + std::istringstream iss(line); + std::string comment, alias, ip, privKey, txHash, outputIndex; + + if (iss >> comment) { + if(comment.at(0) == '#') continue; + iss.str(line); + iss.clear(); + } + + if (!(iss >> alias >> ip >> privKey >> txHash >> outputIndex)) { + iss.str(line); + iss.clear(); + if (!(iss >> alias >> ip >> privKey >> txHash >> outputIndex)) { + strErr = _("Could not parse znode.conf") + "\n" + + strprintf(_("Line: %d"), linenumber) + "\n\"" + line + "\""; + streamConfig.close(); + return false; + } + } + + int port = 0; + std::string hostname = ""; + SplitHostPort(ip, port, hostname); + if(port == 0 || hostname == "") { + strErr = _("Failed to parse host:port string") + "\n"+ + strprintf(_("Line: %d"), linenumber) + "\n\"" + line + "\""; + streamConfig.close(); + return false; + } + int mainnetDefaultPort = Params(CBaseChainParams::MAIN).GetDefaultPort(); + if(Params().NetworkIDString() == CBaseChainParams::MAIN) { + if(port != mainnetDefaultPort) { + strErr = _("Invalid port detected in znode.conf") + "\n" + + strprintf(_("Port: %d"), port) + "\n" + + strprintf(_("Line: %d"), linenumber) + "\n\"" + line + "\"" + "\n" + + strprintf(_("(must be %d for mainnet)"), mainnetDefaultPort); + streamConfig.close(); + return false; + } + } else if(port == mainnetDefaultPort) { + strErr = _("Invalid port detected in znode.conf") + "\n" + + strprintf(_("Line: %d"), linenumber) + "\n\"" + line + "\"" + "\n" + + strprintf(_("(%d could be used only on mainnet)"), mainnetDefaultPort); + streamConfig.close(); + return false; + } + + + add(alias, ip, privKey, txHash, outputIndex); + } + + streamConfig.close(); + return true; +} diff --git a/src/znodeconfig.h b/src/znodeconfig.h new file mode 100644 index 0000000000..72abe0af32 --- /dev/null +++ b/src/znodeconfig.h @@ -0,0 +1,99 @@ + +// Copyright (c) 2014-2017 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef SRC_ZNODECONFIG_H_ +#define SRC_ZNODECONFIG_H_ + +class CZnodeConfig; +extern CZnodeConfig znodeConfig; + +class CZnodeConfig +{ + +public: + + class CZnodeEntry { + + private: + std::string alias; + std::string ip; + std::string privKey; + std::string txHash; + std::string outputIndex; + public: + + CZnodeEntry(std::string alias, std::string ip, std::string privKey, std::string txHash, std::string outputIndex) { + this->alias = alias; + this->ip = ip; + this->privKey = privKey; + this->txHash = txHash; + this->outputIndex = outputIndex; + } + + const std::string& getAlias() const { + return alias; + } + + void setAlias(const std::string& alias) { + this->alias = alias; + } + + const std::string& getOutputIndex() const { + return outputIndex; + } + + void setOutputIndex(const std::string& outputIndex) { + this->outputIndex = outputIndex; + } + + const std::string& getPrivKey() const { + return privKey; + } + + void setPrivKey(const std::string& privKey) { + this->privKey = privKey; + } + + const std::string& getTxHash() const { + return txHash; + } + + void setTxHash(const std::string& txHash) { + this->txHash = txHash; + } + + const std::string& getIp() const { + return ip; + } + + void setIp(const std::string& ip) { + this->ip = ip; + } + }; + + CZnodeConfig() { + entries = std::vector(); + } + + void clear(); + bool read(std::string& strErr); + void add(std::string alias, std::string ip, std::string privKey, std::string txHash, std::string outputIndex); + + std::vector& getEntries() { + return entries; + } + + int getCount() { + return (int)entries.size(); + } + +private: + std::vector entries; + + +}; + + +#endif /* SRC_ZNODECONFIG_H_ */ diff --git a/src/znodeman.cpp b/src/znodeman.cpp new file mode 100644 index 0000000000..b95a8d82b4 --- /dev/null +++ b/src/znodeman.cpp @@ -0,0 +1,1680 @@ +// Copyright (c) 2014-2017 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "activeznode.h" +#include "addrman.h" +#include "darksend.h" +//#include "governance.h" +#include "znode-payments.h" +#include "znode-sync.h" +#include "znodeman.h" +//#include "netfulfilledman.h" +#include "util.h" + +/** Znode manager */ +CZnodeMan mnodeman; +//CNetFulfilledRequestManager netfulfilledman; + +const std::string CZnodeMan::SERIALIZATION_VERSION_STRING = "CZnodeMan-Version-4"; + +struct CompareLastPaidBlock +{ + bool operator()(const std::pair& t1, + const std::pair& t2) const + { + return (t1.first != t2.first) ? (t1.first < t2.first) : (t1.second->vin < t2.second->vin); + } +}; + +struct CompareScoreMN +{ + bool operator()(const std::pair& t1, + const std::pair& t2) const + { + return (t1.first != t2.first) ? (t1.first < t2.first) : (t1.second->vin < t2.second->vin); + } +}; + +CZnodeIndex::CZnodeIndex() + : nSize(0), + mapIndex(), + mapReverseIndex() +{} + +bool CZnodeIndex::Get(int nIndex, CTxIn& vinZnode) const +{ + rindex_m_cit it = mapReverseIndex.find(nIndex); + if(it == mapReverseIndex.end()) { + return false; + } + vinZnode = it->second; + return true; +} + +int CZnodeIndex::GetZnodeIndex(const CTxIn& vinZnode) const +{ + index_m_cit it = mapIndex.find(vinZnode); + if(it == mapIndex.end()) { + return -1; + } + return it->second; +} + +void CZnodeIndex::AddZnodeVIN(const CTxIn& vinZnode) +{ + index_m_it it = mapIndex.find(vinZnode); + if(it != mapIndex.end()) { + return; + } + int nNextIndex = nSize; + mapIndex[vinZnode] = nNextIndex; + mapReverseIndex[nNextIndex] = vinZnode; + ++nSize; +} + +void CZnodeIndex::Clear() +{ + mapIndex.clear(); + mapReverseIndex.clear(); + nSize = 0; +} +struct CompareByAddr + +{ + bool operator()(const CZnode* t1, + const CZnode* t2) const + { + return t1->addr < t2->addr; + } +}; + +void CZnodeIndex::RebuildIndex() +{ + nSize = mapIndex.size(); + for(index_m_it it = mapIndex.begin(); it != mapIndex.end(); ++it) { + mapReverseIndex[it->second] = it->first; + } +} + +CZnodeMan::CZnodeMan() +: cs(), + vZnodes(), + mAskedUsForZnodeList(), + mWeAskedForZnodeList(), + mWeAskedForZnodeListEntry(), + mWeAskedForVerification(), + mMnbRecoveryRequests(), + mMnbRecoveryGoodReplies(), + listScheduledMnbRequestConnections(), + nLastIndexRebuildTime(0), + indexZnodes(), + indexZnodesOld(), + fIndexRebuilt(false), + fZnodesAdded(false), + fZnodesRemoved(false), +// vecDirtyGovernanceObjectHashes(), + nLastWatchdogVoteTime(0), + mapSeenZnodeBroadcast(), + mapSeenZnodePing(), + nDsqCount(0) +{} + +bool CZnodeMan::Add(CZnode &mn) +{ + LOCK(cs); + + CZnode *pmn = Find(mn.vin); + if (pmn == NULL) { + LogPrint("znode", "CZnodeMan::Add -- Adding new Znode: addr=%s, %i now\n", mn.addr.ToString(), size() + 1); + vZnodes.push_back(mn); + indexZnodes.AddZnodeVIN(mn.vin); + fZnodesAdded = true; + return true; + } + + return false; +} + +void CZnodeMan::AskForMN(CNode* pnode, const CTxIn &vin) +{ + if(!pnode) return; + + LOCK(cs); + + std::map >::iterator it1 = mWeAskedForZnodeListEntry.find(vin.prevout); + if (it1 != mWeAskedForZnodeListEntry.end()) { + std::map::iterator it2 = it1->second.find(pnode->addr); + if (it2 != it1->second.end()) { + if (GetTime() < it2->second) { + // we've asked recently, should not repeat too often or we could get banned + return; + } + // we asked this node for this outpoint but it's ok to ask again already + LogPrintf("CZnodeMan::AskForMN -- Asking same peer %s for missing znode entry again: %s\n", pnode->addr.ToString(), vin.prevout.ToStringShort()); + } else { + // we already asked for this outpoint but not this node + LogPrintf("CZnodeMan::AskForMN -- Asking new peer %s for missing znode entry: %s\n", pnode->addr.ToString(), vin.prevout.ToStringShort()); + } + } else { + // we never asked any node for this outpoint + LogPrintf("CZnodeMan::AskForMN -- Asking peer %s for missing znode entry for the first time: %s\n", pnode->addr.ToString(), vin.prevout.ToStringShort()); + } + mWeAskedForZnodeListEntry[vin.prevout][pnode->addr] = GetTime() + DSEG_UPDATE_SECONDS; + + pnode->PushMessage(NetMsgType::DSEG, vin); +} + +void CZnodeMan::Check() +{ + LOCK(cs); + + LogPrint("znode", "CZnodeMan::Check -- nLastWatchdogVoteTime=%d, IsWatchdogActive()=%d\n", nLastWatchdogVoteTime, IsWatchdogActive()); + + BOOST_FOREACH(CZnode& mn, vZnodes) { + mn.Check(); + } +} + +void CZnodeMan::CheckAndRemove() +{ + if(!znodeSync.IsZnodeListSynced()) return; + + LogPrintf("CZnodeMan::CheckAndRemove\n"); + + { + // Need LOCK2 here to ensure consistent locking order because code below locks cs_main + // in CheckMnbAndUpdateZnodeList() + LOCK2(cs_main, cs); + + Check(); + + // Remove spent znodes, prepare structures and make requests to reasure the state of inactive ones + std::vector::iterator it = vZnodes.begin(); + std::vector > vecZnodeRanks; + // ask for up to MNB_RECOVERY_MAX_ASK_ENTRIES znode entries at a time + int nAskForMnbRecovery = MNB_RECOVERY_MAX_ASK_ENTRIES; + while(it != vZnodes.end()) { + CZnodeBroadcast mnb = CZnodeBroadcast(*it); + uint256 hash = mnb.GetHash(); + // If collateral was spent ... + if ((*it).IsOutpointSpent()) { + LogPrint("znode", "CZnodeMan::CheckAndRemove -- Removing Znode: %s addr=%s %i now\n", (*it).GetStateString(), (*it).addr.ToString(), size() - 1); + + // erase all of the broadcasts we've seen from this txin, ... + mapSeenZnodeBroadcast.erase(hash); + mWeAskedForZnodeListEntry.erase((*it).vin.prevout); + + // and finally remove it from the list +// it->FlagGovernanceItemsAsDirty(); + it = vZnodes.erase(it); + fZnodesRemoved = true; + } else { + bool fAsk = pCurrentBlockIndex && + (nAskForMnbRecovery > 0) && + znodeSync.IsSynced() && + it->IsNewStartRequired() && + !IsMnbRecoveryRequested(hash); + if(fAsk) { + // this mn is in a non-recoverable state and we haven't asked other nodes yet + std::set setRequested; + // calulate only once and only when it's needed + if(vecZnodeRanks.empty()) { + int nRandomBlockHeight = GetRandInt(pCurrentBlockIndex->nHeight); + vecZnodeRanks = GetZnodeRanks(nRandomBlockHeight); + } + bool fAskedForMnbRecovery = false; + // ask first MNB_RECOVERY_QUORUM_TOTAL znodes we can connect to and we haven't asked recently + for(int i = 0; setRequested.size() < MNB_RECOVERY_QUORUM_TOTAL && i < (int)vecZnodeRanks.size(); i++) { + // avoid banning + if(mWeAskedForZnodeListEntry.count(it->vin.prevout) && mWeAskedForZnodeListEntry[it->vin.prevout].count(vecZnodeRanks[i].second.addr)) continue; + // didn't ask recently, ok to ask now + CService addr = vecZnodeRanks[i].second.addr; + setRequested.insert(addr); + listScheduledMnbRequestConnections.push_back(std::make_pair(addr, hash)); + fAskedForMnbRecovery = true; + } + if(fAskedForMnbRecovery) { + LogPrint("znode", "CZnodeMan::CheckAndRemove -- Recovery initiated, znode=%s\n", it->vin.prevout.ToStringShort()); + nAskForMnbRecovery--; + } + // wait for mnb recovery replies for MNB_RECOVERY_WAIT_SECONDS seconds + mMnbRecoveryRequests[hash] = std::make_pair(GetTime() + MNB_RECOVERY_WAIT_SECONDS, setRequested); + } + ++it; + } + } + + // proces replies for ZNODE_NEW_START_REQUIRED znodes + LogPrint("znode", "CZnodeMan::CheckAndRemove -- mMnbRecoveryGoodReplies size=%d\n", (int)mMnbRecoveryGoodReplies.size()); + std::map >::iterator itMnbReplies = mMnbRecoveryGoodReplies.begin(); + while(itMnbReplies != mMnbRecoveryGoodReplies.end()){ + if(mMnbRecoveryRequests[itMnbReplies->first].first < GetTime()) { + // all nodes we asked should have replied now + if(itMnbReplies->second.size() >= MNB_RECOVERY_QUORUM_REQUIRED) { + // majority of nodes we asked agrees that this mn doesn't require new mnb, reprocess one of new mnbs + LogPrint("znode", "CZnodeMan::CheckAndRemove -- reprocessing mnb, znode=%s\n", itMnbReplies->second[0].vin.prevout.ToStringShort()); + // mapSeenZnodeBroadcast.erase(itMnbReplies->first); + int nDos; + itMnbReplies->second[0].fRecovery = true; + CheckMnbAndUpdateZnodeList(NULL, itMnbReplies->second[0], nDos); + } + LogPrint("znode", "CZnodeMan::CheckAndRemove -- removing mnb recovery reply, znode=%s, size=%d\n", itMnbReplies->second[0].vin.prevout.ToStringShort(), (int)itMnbReplies->second.size()); + mMnbRecoveryGoodReplies.erase(itMnbReplies++); + } else { + ++itMnbReplies; + } + } + } + { + // no need for cm_main below + LOCK(cs); + + std::map > >::iterator itMnbRequest = mMnbRecoveryRequests.begin(); + while(itMnbRequest != mMnbRecoveryRequests.end()){ + // Allow this mnb to be re-verified again after MNB_RECOVERY_RETRY_SECONDS seconds + // if mn is still in ZNODE_NEW_START_REQUIRED state. + if(GetTime() - itMnbRequest->second.first > MNB_RECOVERY_RETRY_SECONDS) { + mMnbRecoveryRequests.erase(itMnbRequest++); + } else { + ++itMnbRequest; + } + } + + // check who's asked for the Znode list + std::map::iterator it1 = mAskedUsForZnodeList.begin(); + while(it1 != mAskedUsForZnodeList.end()){ + if((*it1).second < GetTime()) { + mAskedUsForZnodeList.erase(it1++); + } else { + ++it1; + } + } + + // check who we asked for the Znode list + it1 = mWeAskedForZnodeList.begin(); + while(it1 != mWeAskedForZnodeList.end()){ + if((*it1).second < GetTime()){ + mWeAskedForZnodeList.erase(it1++); + } else { + ++it1; + } + } + + // check which Znodes we've asked for + std::map >::iterator it2 = mWeAskedForZnodeListEntry.begin(); + while(it2 != mWeAskedForZnodeListEntry.end()){ + std::map::iterator it3 = it2->second.begin(); + while(it3 != it2->second.end()){ + if(it3->second < GetTime()){ + it2->second.erase(it3++); + } else { + ++it3; + } + } + if(it2->second.empty()) { + mWeAskedForZnodeListEntry.erase(it2++); + } else { + ++it2; + } + } + + std::map::iterator it3 = mWeAskedForVerification.begin(); + while(it3 != mWeAskedForVerification.end()){ + if(it3->second.nBlockHeight < pCurrentBlockIndex->nHeight - MAX_POSE_BLOCKS) { + mWeAskedForVerification.erase(it3++); + } else { + ++it3; + } + } + + // NOTE: do not expire mapSeenZnodeBroadcast entries here, clean them on mnb updates! + + // remove expired mapSeenZnodePing + std::map::iterator it4 = mapSeenZnodePing.begin(); + while(it4 != mapSeenZnodePing.end()){ + if((*it4).second.IsExpired()) { + LogPrint("znode", "CZnodeMan::CheckAndRemove -- Removing expired Znode ping: hash=%s\n", (*it4).second.GetHash().ToString()); + mapSeenZnodePing.erase(it4++); + } else { + ++it4; + } + } + + // remove expired mapSeenZnodeVerification + std::map::iterator itv2 = mapSeenZnodeVerification.begin(); + while(itv2 != mapSeenZnodeVerification.end()){ + if((*itv2).second.nBlockHeight < pCurrentBlockIndex->nHeight - MAX_POSE_BLOCKS){ + LogPrint("znode", "CZnodeMan::CheckAndRemove -- Removing expired Znode verification: hash=%s\n", (*itv2).first.ToString()); + mapSeenZnodeVerification.erase(itv2++); + } else { + ++itv2; + } + } + + LogPrintf("CZnodeMan::CheckAndRemove -- %s\n", ToString()); + + if(fZnodesRemoved) { + CheckAndRebuildZnodeIndex(); + } + } + + if(fZnodesRemoved) { + NotifyZnodeUpdates(); + } +} + +void CZnodeMan::Clear() +{ + LOCK(cs); + vZnodes.clear(); + mAskedUsForZnodeList.clear(); + mWeAskedForZnodeList.clear(); + mWeAskedForZnodeListEntry.clear(); + mapSeenZnodeBroadcast.clear(); + mapSeenZnodePing.clear(); + nDsqCount = 0; + nLastWatchdogVoteTime = 0; + indexZnodes.Clear(); + indexZnodesOld.Clear(); +} + +int CZnodeMan::CountZnodes(int nProtocolVersion) +{ + LOCK(cs); + int nCount = 0; + nProtocolVersion = nProtocolVersion == -1 ? mnpayments.GetMinZnodePaymentsProto() : nProtocolVersion; + + BOOST_FOREACH(CZnode& mn, vZnodes) { + if(mn.nProtocolVersion < nProtocolVersion) continue; + nCount++; + } + + return nCount; +} + +int CZnodeMan::CountEnabled(int nProtocolVersion) +{ + LOCK(cs); + int nCount = 0; + nProtocolVersion = nProtocolVersion == -1 ? mnpayments.GetMinZnodePaymentsProto() : nProtocolVersion; + + BOOST_FOREACH(CZnode& mn, vZnodes) { + if(mn.nProtocolVersion < nProtocolVersion || !mn.IsEnabled()) continue; + nCount++; + } + + return nCount; +} + +/* Only IPv4 znodes are allowed in 12.1, saving this for later +int CZnodeMan::CountByIP(int nNetworkType) +{ + LOCK(cs); + int nNodeCount = 0; + + BOOST_FOREACH(CZnode& mn, vZnodes) + if ((nNetworkType == NET_IPV4 && mn.addr.IsIPv4()) || + (nNetworkType == NET_TOR && mn.addr.IsTor()) || + (nNetworkType == NET_IPV6 && mn.addr.IsIPv6())) { + nNodeCount++; + } + + return nNodeCount; +} +*/ + +void CZnodeMan::DsegUpdate(CNode* pnode) +{ + LOCK(cs); + + if(Params().NetworkIDString() == CBaseChainParams::MAIN) { + if(!(pnode->addr.IsRFC1918() || pnode->addr.IsLocal())) { + std::map::iterator it = mWeAskedForZnodeList.find(pnode->addr); + if(it != mWeAskedForZnodeList.end() && GetTime() < (*it).second) { + LogPrintf("CZnodeMan::DsegUpdate -- we already asked %s for the list; skipping...\n", pnode->addr.ToString()); + return; + } + } + } + + pnode->PushMessage(NetMsgType::DSEG, CTxIn()); + int64_t askAgain = GetTime() + DSEG_UPDATE_SECONDS; + mWeAskedForZnodeList[pnode->addr] = askAgain; + + LogPrint("znode", "CZnodeMan::DsegUpdate -- asked %s for the list\n", pnode->addr.ToString()); +} + +CZnode* CZnodeMan::Find(const CScript &payee) +{ + LOCK(cs); + + BOOST_FOREACH(CZnode& mn, vZnodes) + { + if(GetScriptForDestination(mn.pubKeyCollateralAddress.GetID()) == payee) + return &mn; + } + return NULL; +} + +CZnode* CZnodeMan::Find(const CTxIn &vin) +{ + LOCK(cs); + + BOOST_FOREACH(CZnode& mn, vZnodes) + { + if(mn.vin.prevout == vin.prevout) + return &mn; + } + return NULL; +} + +CZnode* CZnodeMan::Find(const CPubKey &pubKeyZnode) +{ + LOCK(cs); + + BOOST_FOREACH(CZnode& mn, vZnodes) + { + if(mn.pubKeyZnode == pubKeyZnode) + return &mn; + } + return NULL; +} + +bool CZnodeMan::Get(const CPubKey& pubKeyZnode, CZnode& znode) +{ + // Theses mutexes are recursive so double locking by the same thread is safe. + LOCK(cs); + CZnode* pMN = Find(pubKeyZnode); + if(!pMN) { + return false; + } + znode = *pMN; + return true; +} + +bool CZnodeMan::Get(const CTxIn& vin, CZnode& znode) +{ + // Theses mutexes are recursive so double locking by the same thread is safe. + LOCK(cs); + CZnode* pMN = Find(vin); + if(!pMN) { + return false; + } + znode = *pMN; + return true; +} + +znode_info_t CZnodeMan::GetZnodeInfo(const CTxIn& vin) +{ + znode_info_t info; + LOCK(cs); + CZnode* pMN = Find(vin); + if(!pMN) { + return info; + } + info = pMN->GetInfo(); + return info; +} + +znode_info_t CZnodeMan::GetZnodeInfo(const CPubKey& pubKeyZnode) +{ + znode_info_t info; + LOCK(cs); + CZnode* pMN = Find(pubKeyZnode); + if(!pMN) { + return info; + } + info = pMN->GetInfo(); + return info; +} + +bool CZnodeMan::Has(const CTxIn& vin) +{ + LOCK(cs); + CZnode* pMN = Find(vin); + return (pMN != NULL); +} + +// +// Deterministically select the oldest/best znode to pay on the network +// +CZnode* CZnodeMan::GetNextZnodeInQueueForPayment(bool fFilterSigTime, int& nCount) +{ + if(!pCurrentBlockIndex) { + nCount = 0; + return NULL; + } + return GetNextZnodeInQueueForPayment(pCurrentBlockIndex->nHeight, fFilterSigTime, nCount); +} + +CZnode* CZnodeMan::GetNextZnodeInQueueForPayment(int nBlockHeight, bool fFilterSigTime, int& nCount) +{ + // Need LOCK2 here to ensure consistent locking order because the GetBlockHash call below locks cs_main + LOCK2(cs_main,cs); + + CZnode *pBestZnode = NULL; + std::vector > vecZnodeLastPaid; + + /* + Make a vector with all of the last paid times + */ + + int nMnCount = CountEnabled(); + BOOST_FOREACH(CZnode &mn, vZnodes) + { + if(!mn.IsValidForPayment()) continue; + + // //check protocol version + if(mn.nProtocolVersion < mnpayments.GetMinZnodePaymentsProto()) continue; + + //it's in the list (up to 8 entries ahead of current block to allow propagation) -- so let's skip it + if(mnpayments.IsScheduled(mn, nBlockHeight)) continue; + + //it's too new, wait for a cycle + if(fFilterSigTime && mn.sigTime + (nMnCount*2.6*60) > GetAdjustedTime()) continue; + + //make sure it has at least as many confirmations as there are znodes + if(mn.GetCollateralAge() < nMnCount) continue; + + vecZnodeLastPaid.push_back(std::make_pair(mn.GetLastPaidBlock(), &mn)); + } + + nCount = (int)vecZnodeLastPaid.size(); + + //when the network is in the process of upgrading, don't penalize nodes that recently restarted + if(fFilterSigTime && nCount < nMnCount/3) return GetNextZnodeInQueueForPayment(nBlockHeight, false, nCount); + + // Sort them low to high + sort(vecZnodeLastPaid.begin(), vecZnodeLastPaid.end(), CompareLastPaidBlock()); + + uint256 blockHash; + if(!GetBlockHash(blockHash, nBlockHeight - 101)) { + LogPrintf("CZnode::GetNextZnodeInQueueForPayment -- ERROR: GetBlockHash() failed at nBlockHeight %d\n", nBlockHeight - 101); + return NULL; + } + // Look at 1/10 of the oldest nodes (by last payment), calculate their scores and pay the best one + // -- This doesn't look at who is being paid in the +8-10 blocks, allowing for double payments very rarely + // -- 1/100 payments should be a double payment on mainnet - (1/(3000/10))*2 + // -- (chance per block * chances before IsScheduled will fire) + int nTenthNetwork = nMnCount/10; + int nCountTenth = 0; + arith_uint256 nHighest = 0; + BOOST_FOREACH (PAIRTYPE(int, CZnode*)& s, vecZnodeLastPaid){ + arith_uint256 nScore = s.second->CalculateScore(blockHash); + if(nScore > nHighest){ + nHighest = nScore; + pBestZnode = s.second; + } + nCountTenth++; + if(nCountTenth >= nTenthNetwork) break; + } + return pBestZnode; +} + +CZnode* CZnodeMan::FindRandomNotInVec(const std::vector &vecToExclude, int nProtocolVersion) +{ + LOCK(cs); + + nProtocolVersion = nProtocolVersion == -1 ? mnpayments.GetMinZnodePaymentsProto() : nProtocolVersion; + + int nCountEnabled = CountEnabled(nProtocolVersion); + int nCountNotExcluded = nCountEnabled - vecToExclude.size(); + + LogPrintf("CZnodeMan::FindRandomNotInVec -- %d enabled znodes, %d znodes to choose from\n", nCountEnabled, nCountNotExcluded); + if(nCountNotExcluded < 1) return NULL; + + // fill a vector of pointers + std::vector vpZnodesShuffled; + BOOST_FOREACH(CZnode &mn, vZnodes) { + vpZnodesShuffled.push_back(&mn); + } + +// InsecureRand insecureRand; + // shuffle pointers +// std::random_shuffle(vpZnodesShuffled.begin(), vpZnodesShuffled.end(), insecureRand); + bool fExclude; + + // loop through + BOOST_FOREACH(CZnode* pmn, vpZnodesShuffled) { + if(pmn->nProtocolVersion < nProtocolVersion || !pmn->IsEnabled()) continue; + fExclude = false; + BOOST_FOREACH(const CTxIn &txinToExclude, vecToExclude) { + if(pmn->vin.prevout == txinToExclude.prevout) { + fExclude = true; + break; + } + } + if(fExclude) continue; + // found the one not in vecToExclude + LogPrint("znode", "CZnodeMan::FindRandomNotInVec -- found, znode=%s\n", pmn->vin.prevout.ToStringShort()); + return pmn; + } + + LogPrint("znode", "CZnodeMan::FindRandomNotInVec -- failed\n"); + return NULL; +} + +int CZnodeMan::GetZnodeRank(const CTxIn& vin, int nBlockHeight, int nMinProtocol, bool fOnlyActive) +{ + std::vector > vecZnodeScores; + + //make sure we know about this block + uint256 blockHash = uint256(); + if(!GetBlockHash(blockHash, nBlockHeight)) return -1; + + LOCK(cs); + + // scan for winner + BOOST_FOREACH(CZnode& mn, vZnodes) { + if(mn.nProtocolVersion < nMinProtocol) continue; + if(fOnlyActive) { + if(!mn.IsEnabled()) continue; + } + else { + if(!mn.IsValidForPayment()) continue; + } + int64_t nScore = mn.CalculateScore(blockHash).GetCompact(false); + + vecZnodeScores.push_back(std::make_pair(nScore, &mn)); + } + + sort(vecZnodeScores.rbegin(), vecZnodeScores.rend(), CompareScoreMN()); + + int nRank = 0; + BOOST_FOREACH (PAIRTYPE(int64_t, CZnode*)& scorePair, vecZnodeScores) { + nRank++; + if(scorePair.second->vin.prevout == vin.prevout) return nRank; + } + + return -1; +} + +std::vector > CZnodeMan::GetZnodeRanks(int nBlockHeight, int nMinProtocol) +{ + std::vector > vecZnodeScores; + std::vector > vecZnodeRanks; + + //make sure we know about this block + uint256 blockHash = uint256(); + if(!GetBlockHash(blockHash, nBlockHeight)) return vecZnodeRanks; + + LOCK(cs); + + // scan for winner + BOOST_FOREACH(CZnode& mn, vZnodes) { + + if(mn.nProtocolVersion < nMinProtocol || !mn.IsEnabled()) continue; + + int64_t nScore = mn.CalculateScore(blockHash).GetCompact(false); + + vecZnodeScores.push_back(std::make_pair(nScore, &mn)); + } + + sort(vecZnodeScores.rbegin(), vecZnodeScores.rend(), CompareScoreMN()); + + int nRank = 0; + BOOST_FOREACH (PAIRTYPE(int64_t, CZnode*)& s, vecZnodeScores) { + nRank++; + vecZnodeRanks.push_back(std::make_pair(nRank, *s.second)); + } + + return vecZnodeRanks; +} + +CZnode* CZnodeMan::GetZnodeByRank(int nRank, int nBlockHeight, int nMinProtocol, bool fOnlyActive) +{ + std::vector > vecZnodeScores; + + LOCK(cs); + + uint256 blockHash; + if(!GetBlockHash(blockHash, nBlockHeight)) { + LogPrintf("CZnode::GetZnodeByRank -- ERROR: GetBlockHash() failed at nBlockHeight %d\n", nBlockHeight); + return NULL; + } + + // Fill scores + BOOST_FOREACH(CZnode& mn, vZnodes) { + + if(mn.nProtocolVersion < nMinProtocol) continue; + if(fOnlyActive && !mn.IsEnabled()) continue; + + int64_t nScore = mn.CalculateScore(blockHash).GetCompact(false); + + vecZnodeScores.push_back(std::make_pair(nScore, &mn)); + } + + sort(vecZnodeScores.rbegin(), vecZnodeScores.rend(), CompareScoreMN()); + + int rank = 0; + BOOST_FOREACH (PAIRTYPE(int64_t, CZnode*)& s, vecZnodeScores){ + rank++; + if(rank == nRank) { + return s.second; + } + } + + return NULL; +} + +void CZnodeMan::ProcessZnodeConnections() +{ + //we don't care about this for regtest + if(Params().NetworkIDString() == CBaseChainParams::REGTEST) return; + + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) { + if(pnode->fZnode) { + if(darkSendPool.pSubmittedToZnode != NULL && pnode->addr == darkSendPool.pSubmittedToZnode->addr) continue; + LogPrintf("Closing Znode connection: peer=%d, addr=%s\n", pnode->id, pnode->addr.ToString()); + pnode->fDisconnect = true; + } + } +} + +std::pair > CZnodeMan::PopScheduledMnbRequestConnection() +{ + LOCK(cs); + if(listScheduledMnbRequestConnections.empty()) { + return std::make_pair(CService(), std::set()); + } + + std::set setResult; + + listScheduledMnbRequestConnections.sort(); + std::pair pairFront = listScheduledMnbRequestConnections.front(); + + // squash hashes from requests with the same CService as the first one into setResult + std::list< std::pair >::iterator it = listScheduledMnbRequestConnections.begin(); + while(it != listScheduledMnbRequestConnections.end()) { + if(pairFront.first == it->first) { + setResult.insert(it->second); + it = listScheduledMnbRequestConnections.erase(it); + } else { + // since list is sorted now, we can be sure that there is no more hashes left + // to ask for from this addr + break; + } + } + return std::make_pair(pairFront.first, setResult); +} + + +void CZnodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) +{ + if(fLiteMode) return; // disable all Dash specific functionality + if(!znodeSync.IsBlockchainSynced()) return; + + if (strCommand == NetMsgType::MNANNOUNCE) { //Znode Broadcast + + CZnodeBroadcast mnb; + vRecv >> mnb; + + pfrom->setAskFor.erase(mnb.GetHash()); + + LogPrint("znode", "MNANNOUNCE -- Znode announce, znode=%s\n", mnb.vin.prevout.ToStringShort()); + + int nDos = 0; + + if (CheckMnbAndUpdateZnodeList(pfrom, mnb, nDos)) { + // use announced Znode as a peer + addrman.Add(CAddress(mnb.addr, NODE_NETWORK), pfrom->addr, 2*60*60); + } else if(nDos > 0) { + Misbehaving(pfrom->GetId(), nDos); + } + + if(fZnodesAdded) { + NotifyZnodeUpdates(); + } + } else if (strCommand == NetMsgType::MNPING) { //Znode Ping + + CZnodePing mnp; + vRecv >> mnp; + + uint256 nHash = mnp.GetHash(); + + pfrom->setAskFor.erase(nHash); + + LogPrint("znode", "MNPING -- Znode ping, znode=%s\n", mnp.vin.prevout.ToStringShort()); + + // Need LOCK2 here to ensure consistent locking order because the CheckAndUpdate call below locks cs_main + LOCK2(cs_main, cs); + + if(mapSeenZnodePing.count(nHash)) return; //seen + mapSeenZnodePing.insert(std::make_pair(nHash, mnp)); + + LogPrint("znode", "MNPING -- Znode ping, znode=%s new\n", mnp.vin.prevout.ToStringShort()); + + // see if we have this Znode + CZnode* pmn = mnodeman.Find(mnp.vin); + + // too late, new MNANNOUNCE is required + if(pmn && pmn->IsNewStartRequired()) return; + + int nDos = 0; + if(mnp.CheckAndUpdate(pmn, false, nDos)) return; + + if(nDos > 0) { + // if anything significant failed, mark that node + Misbehaving(pfrom->GetId(), nDos); + } else if(pmn != NULL) { + // nothing significant failed, mn is a known one too + return; + } + + // something significant is broken or mn is unknown, + // we might have to ask for a znode entry once + AskForMN(pfrom, mnp.vin); + + } else if (strCommand == NetMsgType::DSEG) { //Get Znode list or specific entry + // Ignore such requests until we are fully synced. + // We could start processing this after znode list is synced + // but this is a heavy one so it's better to finish sync first. + if (!znodeSync.IsSynced()) return; + + CTxIn vin; + vRecv >> vin; + + LogPrint("znode", "DSEG -- Znode list, znode=%s\n", vin.prevout.ToStringShort()); + + LOCK(cs); + + if(vin == CTxIn()) { //only should ask for this once + //local network + bool isLocal = (pfrom->addr.IsRFC1918() || pfrom->addr.IsLocal()); + + if(!isLocal && Params().NetworkIDString() == CBaseChainParams::MAIN) { + std::map::iterator i = mAskedUsForZnodeList.find(pfrom->addr); + if (i != mAskedUsForZnodeList.end()){ + int64_t t = (*i).second; + if (GetTime() < t) { + Misbehaving(pfrom->GetId(), 34); + LogPrintf("DSEG -- peer already asked me for the list, peer=%d\n", pfrom->id); + return; + } + } + int64_t askAgain = GetTime() + DSEG_UPDATE_SECONDS; + mAskedUsForZnodeList[pfrom->addr] = askAgain; + } + } //else, asking for a specific node which is ok + + int nInvCount = 0; + + BOOST_FOREACH(CZnode& mn, vZnodes) { + if (vin != CTxIn() && vin != mn.vin) continue; // asked for specific vin but we are not there yet + if (mn.addr.IsRFC1918() || mn.addr.IsLocal()) continue; // do not send local network znode + if (mn.IsUpdateRequired()) continue; // do not send outdated znodes + + LogPrint("znode", "DSEG -- Sending Znode entry: znode=%s addr=%s\n", mn.vin.prevout.ToStringShort(), mn.addr.ToString()); + CZnodeBroadcast mnb = CZnodeBroadcast(mn); + uint256 hash = mnb.GetHash(); + pfrom->PushInventory(CInv(MSG_ZNODE_ANNOUNCE, hash)); + pfrom->PushInventory(CInv(MSG_ZNODE_PING, mn.lastPing.GetHash())); + nInvCount++; + + if (!mapSeenZnodeBroadcast.count(hash)) { + mapSeenZnodeBroadcast.insert(std::make_pair(hash, std::make_pair(GetTime(), mnb))); + } + + if (vin == mn.vin) { + LogPrintf("DSEG -- Sent 1 Znode inv to peer %d\n", pfrom->id); + return; + } + } + + if(vin == CTxIn()) { + pfrom->PushMessage(NetMsgType::SYNCSTATUSCOUNT, ZNODE_SYNC_LIST, nInvCount); + LogPrintf("DSEG -- Sent %d Znode invs to peer %d\n", nInvCount, pfrom->id); + return; + } + // smth weird happen - someone asked us for vin we have no idea about? + LogPrint("znode", "DSEG -- No invs sent to peer %d\n", pfrom->id); + + } else if (strCommand == NetMsgType::MNVERIFY) { // Znode Verify + + // Need LOCK2 here to ensure consistent locking order because the all functions below call GetBlockHash which locks cs_main + LOCK2(cs_main, cs); + + CZnodeVerification mnv; + vRecv >> mnv; + + if(mnv.vchSig1.empty()) { + // CASE 1: someone asked me to verify myself /IP we are using/ + SendVerifyReply(pfrom, mnv); + } else if (mnv.vchSig2.empty()) { + // CASE 2: we _probably_ got verification we requested from some znode + ProcessVerifyReply(pfrom, mnv); + } else { + // CASE 3: we _probably_ got verification broadcast signed by some znode which verified another one + ProcessVerifyBroadcast(pfrom, mnv); + } + } +} + +// Verification of znodes via unique direct requests. + +void CZnodeMan::DoFullVerificationStep() +{ + if(activeZnode.vin == CTxIn()) return; + if(!znodeSync.IsSynced()) return; + + std::vector > vecZnodeRanks = GetZnodeRanks(pCurrentBlockIndex->nHeight - 1, MIN_POSE_PROTO_VERSION); + + // Need LOCK2 here to ensure consistent locking order because the SendVerifyRequest call below locks cs_main + // through GetHeight() signal in ConnectNode + LOCK2(cs_main, cs); + + int nCount = 0; + + int nMyRank = -1; + int nRanksTotal = (int)vecZnodeRanks.size(); + + // send verify requests only if we are in top MAX_POSE_RANK + std::vector >::iterator it = vecZnodeRanks.begin(); + while(it != vecZnodeRanks.end()) { + if(it->first > MAX_POSE_RANK) { + LogPrint("znode", "CZnodeMan::DoFullVerificationStep -- Must be in top %d to send verify request\n", + (int)MAX_POSE_RANK); + return; + } + if(it->second.vin == activeZnode.vin) { + nMyRank = it->first; + LogPrint("znode", "CZnodeMan::DoFullVerificationStep -- Found self at rank %d/%d, verifying up to %d znodes\n", + nMyRank, nRanksTotal, (int)MAX_POSE_CONNECTIONS); + break; + } + ++it; + } + + // edge case: list is too short and this znode is not enabled + if(nMyRank == -1) return; + + // send verify requests to up to MAX_POSE_CONNECTIONS znodes + // starting from MAX_POSE_RANK + nMyRank and using MAX_POSE_CONNECTIONS as a step + int nOffset = MAX_POSE_RANK + nMyRank - 1; + if(nOffset >= (int)vecZnodeRanks.size()) return; + + std::vector vSortedByAddr; + BOOST_FOREACH(CZnode& mn, vZnodes) { + vSortedByAddr.push_back(&mn); + } + + sort(vSortedByAddr.begin(), vSortedByAddr.end(), CompareByAddr()); + + it = vecZnodeRanks.begin() + nOffset; + while(it != vecZnodeRanks.end()) { + if(it->second.IsPoSeVerified() || it->second.IsPoSeBanned()) { + LogPrint("znode", "CZnodeMan::DoFullVerificationStep -- Already %s%s%s znode %s address %s, skipping...\n", + it->second.IsPoSeVerified() ? "verified" : "", + it->second.IsPoSeVerified() && it->second.IsPoSeBanned() ? " and " : "", + it->second.IsPoSeBanned() ? "banned" : "", + it->second.vin.prevout.ToStringShort(), it->second.addr.ToString()); + nOffset += MAX_POSE_CONNECTIONS; + if(nOffset >= (int)vecZnodeRanks.size()) break; + it += MAX_POSE_CONNECTIONS; + continue; + } + LogPrint("znode", "CZnodeMan::DoFullVerificationStep -- Verifying znode %s rank %d/%d address %s\n", + it->second.vin.prevout.ToStringShort(), it->first, nRanksTotal, it->second.addr.ToString()); + if(SendVerifyRequest(CAddress(it->second.addr, NODE_NETWORK), vSortedByAddr)) { + nCount++; + if(nCount >= MAX_POSE_CONNECTIONS) break; + } + nOffset += MAX_POSE_CONNECTIONS; + if(nOffset >= (int)vecZnodeRanks.size()) break; + it += MAX_POSE_CONNECTIONS; + } + + LogPrint("znode", "CZnodeMan::DoFullVerificationStep -- Sent verification requests to %d znodes\n", nCount); +} + +// This function tries to find znodes with the same addr, +// find a verified one and ban all the other. If there are many nodes +// with the same addr but none of them is verified yet, then none of them are banned. +// It could take many times to run this before most of the duplicate nodes are banned. + +void CZnodeMan::CheckSameAddr() +{ + if(!znodeSync.IsSynced() || vZnodes.empty()) return; + + std::vector vBan; + std::vector vSortedByAddr; + + { + LOCK(cs); + + CZnode* pprevZnode = NULL; + CZnode* pverifiedZnode = NULL; + + BOOST_FOREACH(CZnode& mn, vZnodes) { + vSortedByAddr.push_back(&mn); + } + + sort(vSortedByAddr.begin(), vSortedByAddr.end(), CompareByAddr()); + + BOOST_FOREACH(CZnode* pmn, vSortedByAddr) { + // check only (pre)enabled znodes + if(!pmn->IsEnabled() && !pmn->IsPreEnabled()) continue; + // initial step + if(!pprevZnode) { + pprevZnode = pmn; + pverifiedZnode = pmn->IsPoSeVerified() ? pmn : NULL; + continue; + } + // second+ step + if(pmn->addr == pprevZnode->addr) { + if(pverifiedZnode) { + // another znode with the same ip is verified, ban this one + vBan.push_back(pmn); + } else if(pmn->IsPoSeVerified()) { + // this znode with the same ip is verified, ban previous one + vBan.push_back(pprevZnode); + // and keep a reference to be able to ban following znodes with the same ip + pverifiedZnode = pmn; + } + } else { + pverifiedZnode = pmn->IsPoSeVerified() ? pmn : NULL; + } + pprevZnode = pmn; + } + } + + // ban duplicates + BOOST_FOREACH(CZnode* pmn, vBan) { + LogPrintf("CZnodeMan::CheckSameAddr -- increasing PoSe ban score for znode %s\n", pmn->vin.prevout.ToStringShort()); + pmn->IncreasePoSeBanScore(); + } +} + +bool CZnodeMan::SendVerifyRequest(const CAddress& addr, const std::vector& vSortedByAddr) +{ +// if(netfulfilledman.HasFulfilledRequest(addr, strprintf("%s", NetMsgType::MNVERIFY)+"-request")) { + // we already asked for verification, not a good idea to do this too often, skip it +// LogPrint("znode", "CZnodeMan::SendVerifyRequest -- too many requests, skipping... addr=%s\n", addr.ToString()); +// return false; +// } + + CNode* pnode = ConnectNodeDash(addr, NULL, true); + if(pnode == NULL) { + LogPrintf("CZnodeMan::SendVerifyRequest -- can't connect to node to verify it, addr=%s\n", addr.ToString()); + return false; + } + +// netfulfilledman.AddFulfilledRequest(addr, strprintf("%s", NetMsgType::MNVERIFY)+"-request"); + // use random nonce, store it and require node to reply with correct one later + CZnodeVerification mnv(addr, GetRandInt(999999), pCurrentBlockIndex->nHeight - 1); + mWeAskedForVerification[addr] = mnv; + LogPrintf("CZnodeMan::SendVerifyRequest -- verifying node using nonce %d addr=%s\n", mnv.nonce, addr.ToString()); + pnode->PushMessage(NetMsgType::MNVERIFY, mnv); + + return true; +} + +void CZnodeMan::SendVerifyReply(CNode* pnode, CZnodeVerification& mnv) +{ + // only znodes can sign this, why would someone ask regular node? + if(!fZNode) { + // do not ban, malicious node might be using my IP + // and trying to confuse the node which tries to verify it + return; + } + +// if(netfulfilledman.HasFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-reply")) { +// // peer should not ask us that often +// LogPrintf("ZnodeMan::SendVerifyReply -- ERROR: peer already asked me recently, peer=%d\n", pnode->id); +// Misbehaving(pnode->id, 20); +// return; +// } + + uint256 blockHash; + if(!GetBlockHash(blockHash, mnv.nBlockHeight)) { + LogPrintf("ZnodeMan::SendVerifyReply -- can't get block hash for unknown block height %d, peer=%d\n", mnv.nBlockHeight, pnode->id); + return; + } + + std::string strMessage = strprintf("%s%d%s", activeZnode.service.ToString(), mnv.nonce, blockHash.ToString()); + + if(!darkSendSigner.SignMessage(strMessage, mnv.vchSig1, activeZnode.keyZnode)) { + LogPrintf("ZnodeMan::SendVerifyReply -- SignMessage() failed\n"); + return; + } + + std::string strError; + + if(!darkSendSigner.VerifyMessage(activeZnode.pubKeyZnode, mnv.vchSig1, strMessage, strError)) { + LogPrintf("ZnodeMan::SendVerifyReply -- VerifyMessage() failed, error: %s\n", strError); + return; + } + + pnode->PushMessage(NetMsgType::MNVERIFY, mnv); +// netfulfilledman.AddFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-reply"); +} + +void CZnodeMan::ProcessVerifyReply(CNode* pnode, CZnodeVerification& mnv) +{ + std::string strError; + + // did we even ask for it? if that's the case we should have matching fulfilled request +// if(!netfulfilledman.HasFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-request")) { +// LogPrintf("CZnodeMan::ProcessVerifyReply -- ERROR: we didn't ask for verification of %s, peer=%d\n", pnode->addr.ToString(), pnode->id); +// Misbehaving(pnode->id, 20); +// return; +// } + + // Received nonce for a known address must match the one we sent + if(mWeAskedForVerification[pnode->addr].nonce != mnv.nonce) { + LogPrintf("CZnodeMan::ProcessVerifyReply -- ERROR: wrong nounce: requested=%d, received=%d, peer=%d\n", + mWeAskedForVerification[pnode->addr].nonce, mnv.nonce, pnode->id); + Misbehaving(pnode->id, 20); + return; + } + + // Received nBlockHeight for a known address must match the one we sent + if(mWeAskedForVerification[pnode->addr].nBlockHeight != mnv.nBlockHeight) { + LogPrintf("CZnodeMan::ProcessVerifyReply -- ERROR: wrong nBlockHeight: requested=%d, received=%d, peer=%d\n", + mWeAskedForVerification[pnode->addr].nBlockHeight, mnv.nBlockHeight, pnode->id); + Misbehaving(pnode->id, 20); + return; + } + + uint256 blockHash; + if(!GetBlockHash(blockHash, mnv.nBlockHeight)) { + // this shouldn't happen... + LogPrintf("ZnodeMan::ProcessVerifyReply -- can't get block hash for unknown block height %d, peer=%d\n", mnv.nBlockHeight, pnode->id); + return; + } + +// // we already verified this address, why node is spamming? +// if(netfulfilledman.HasFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-done")) { +// LogPrintf("CZnodeMan::ProcessVerifyReply -- ERROR: already verified %s recently\n", pnode->addr.ToString()); +// Misbehaving(pnode->id, 20); +// return; +// } + + { + LOCK(cs); + + CZnode* prealZnode = NULL; + std::vector vpZnodesToBan; + std::vector::iterator it = vZnodes.begin(); + std::string strMessage1 = strprintf("%s%d%s", pnode->addr.ToString(), mnv.nonce, blockHash.ToString()); + while(it != vZnodes.end()) { + if(CAddress(it->addr, NODE_NETWORK) == pnode->addr) { + if(darkSendSigner.VerifyMessage(it->pubKeyZnode, mnv.vchSig1, strMessage1, strError)) { + // found it! + prealZnode = &(*it); + if(!it->IsPoSeVerified()) { + it->DecreasePoSeBanScore(); + } +// netfulfilledman.AddFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-done"); + + // we can only broadcast it if we are an activated znode + if(activeZnode.vin == CTxIn()) continue; + // update ... + mnv.addr = it->addr; + mnv.vin1 = it->vin; + mnv.vin2 = activeZnode.vin; + std::string strMessage2 = strprintf("%s%d%s%s%s", mnv.addr.ToString(), mnv.nonce, blockHash.ToString(), + mnv.vin1.prevout.ToStringShort(), mnv.vin2.prevout.ToStringShort()); + // ... and sign it + if(!darkSendSigner.SignMessage(strMessage2, mnv.vchSig2, activeZnode.keyZnode)) { + LogPrintf("ZnodeMan::ProcessVerifyReply -- SignMessage() failed\n"); + return; + } + + std::string strError; + + if(!darkSendSigner.VerifyMessage(activeZnode.pubKeyZnode, mnv.vchSig2, strMessage2, strError)) { + LogPrintf("ZnodeMan::ProcessVerifyReply -- VerifyMessage() failed, error: %s\n", strError); + return; + } + + mWeAskedForVerification[pnode->addr] = mnv; + mnv.Relay(); + + } else { + vpZnodesToBan.push_back(&(*it)); + } + } + ++it; + } + // no real znode found?... + if(!prealZnode) { + // this should never be the case normally, + // only if someone is trying to game the system in some way or smth like that + LogPrintf("CZnodeMan::ProcessVerifyReply -- ERROR: no real znode found for addr %s\n", pnode->addr.ToString()); + Misbehaving(pnode->id, 20); + return; + } + LogPrintf("CZnodeMan::ProcessVerifyReply -- verified real znode %s for addr %s\n", + prealZnode->vin.prevout.ToStringShort(), pnode->addr.ToString()); + // increase ban score for everyone else + BOOST_FOREACH(CZnode* pmn, vpZnodesToBan) { + pmn->IncreasePoSeBanScore(); + LogPrint("znode", "CZnodeMan::ProcessVerifyBroadcast -- increased PoSe ban score for %s addr %s, new score %d\n", + prealZnode->vin.prevout.ToStringShort(), pnode->addr.ToString(), pmn->nPoSeBanScore); + } + LogPrintf("CZnodeMan::ProcessVerifyBroadcast -- PoSe score increased for %d fake znodes, addr %s\n", + (int)vpZnodesToBan.size(), pnode->addr.ToString()); + } +} + +void CZnodeMan::ProcessVerifyBroadcast(CNode* pnode, const CZnodeVerification& mnv) +{ + std::string strError; + + if(mapSeenZnodeVerification.find(mnv.GetHash()) != mapSeenZnodeVerification.end()) { + // we already have one + return; + } + mapSeenZnodeVerification[mnv.GetHash()] = mnv; + + // we don't care about history + if(mnv.nBlockHeight < pCurrentBlockIndex->nHeight - MAX_POSE_BLOCKS) { + LogPrint("znode", "ZnodeMan::ProcessVerifyBroadcast -- Outdated: current block %d, verification block %d, peer=%d\n", + pCurrentBlockIndex->nHeight, mnv.nBlockHeight, pnode->id); + return; + } + + if(mnv.vin1.prevout == mnv.vin2.prevout) { + LogPrint("znode", "ZnodeMan::ProcessVerifyBroadcast -- ERROR: same vins %s, peer=%d\n", + mnv.vin1.prevout.ToStringShort(), pnode->id); + // that was NOT a good idea to cheat and verify itself, + // ban the node we received such message from + Misbehaving(pnode->id, 100); + return; + } + + uint256 blockHash; + if(!GetBlockHash(blockHash, mnv.nBlockHeight)) { + // this shouldn't happen... + LogPrintf("ZnodeMan::ProcessVerifyBroadcast -- Can't get block hash for unknown block height %d, peer=%d\n", mnv.nBlockHeight, pnode->id); + return; + } + + int nRank = GetZnodeRank(mnv.vin2, mnv.nBlockHeight, MIN_POSE_PROTO_VERSION); + + if (nRank == -1) { + LogPrint("znode", "CZnodeMan::ProcessVerifyBroadcast -- Can't calculate rank for znode %s\n", + mnv.vin2.prevout.ToStringShort()); + return; + } + + if(nRank > MAX_POSE_RANK) { + LogPrint("znode", "CZnodeMan::ProcessVerifyBroadcast -- Mastrernode %s is not in top %d, current rank %d, peer=%d\n", + mnv.vin2.prevout.ToStringShort(), (int)MAX_POSE_RANK, nRank, pnode->id); + return; + } + + { + LOCK(cs); + + std::string strMessage1 = strprintf("%s%d%s", mnv.addr.ToString(), mnv.nonce, blockHash.ToString()); + std::string strMessage2 = strprintf("%s%d%s%s%s", mnv.addr.ToString(), mnv.nonce, blockHash.ToString(), + mnv.vin1.prevout.ToStringShort(), mnv.vin2.prevout.ToStringShort()); + + CZnode* pmn1 = Find(mnv.vin1); + if(!pmn1) { + LogPrintf("CZnodeMan::ProcessVerifyBroadcast -- can't find znode1 %s\n", mnv.vin1.prevout.ToStringShort()); + return; + } + + CZnode* pmn2 = Find(mnv.vin2); + if(!pmn2) { + LogPrintf("CZnodeMan::ProcessVerifyBroadcast -- can't find znode2 %s\n", mnv.vin2.prevout.ToStringShort()); + return; + } + + if(pmn1->addr != mnv.addr) { + LogPrintf("CZnodeMan::ProcessVerifyBroadcast -- addr %s do not match %s\n", mnv.addr.ToString(), pnode->addr.ToString()); + return; + } + + if(darkSendSigner.VerifyMessage(pmn1->pubKeyZnode, mnv.vchSig1, strMessage1, strError)) { + LogPrintf("ZnodeMan::ProcessVerifyBroadcast -- VerifyMessage() for znode1 failed, error: %s\n", strError); + return; + } + + if(darkSendSigner.VerifyMessage(pmn2->pubKeyZnode, mnv.vchSig2, strMessage2, strError)) { + LogPrintf("ZnodeMan::ProcessVerifyBroadcast -- VerifyMessage() for znode2 failed, error: %s\n", strError); + return; + } + + if(!pmn1->IsPoSeVerified()) { + pmn1->DecreasePoSeBanScore(); + } + mnv.Relay(); + + LogPrintf("CZnodeMan::ProcessVerifyBroadcast -- verified znode %s for addr %s\n", + pmn1->vin.prevout.ToStringShort(), pnode->addr.ToString()); + + // increase ban score for everyone else with the same addr + int nCount = 0; + BOOST_FOREACH(CZnode& mn, vZnodes) { + if(mn.addr != mnv.addr || mn.vin.prevout == mnv.vin1.prevout) continue; + mn.IncreasePoSeBanScore(); + nCount++; + LogPrint("znode", "CZnodeMan::ProcessVerifyBroadcast -- increased PoSe ban score for %s addr %s, new score %d\n", + mn.vin.prevout.ToStringShort(), mn.addr.ToString(), mn.nPoSeBanScore); + } + LogPrintf("CZnodeMan::ProcessVerifyBroadcast -- PoSe score incresed for %d fake znodes, addr %s\n", + nCount, pnode->addr.ToString()); + } +} + +std::string CZnodeMan::ToString() const +{ + std::ostringstream info; + + info << "Znodes: " << (int)vZnodes.size() << + ", peers who asked us for Znode list: " << (int)mAskedUsForZnodeList.size() << + ", peers we asked for Znode list: " << (int)mWeAskedForZnodeList.size() << + ", entries in Znode list we asked for: " << (int)mWeAskedForZnodeListEntry.size() << + ", znode index size: " << indexZnodes.GetSize() << + ", nDsqCount: " << (int)nDsqCount; + + return info.str(); +} + +void CZnodeMan::UpdateZnodeList(CZnodeBroadcast mnb) +{ + LOCK(cs); + mapSeenZnodePing.insert(std::make_pair(mnb.lastPing.GetHash(), mnb.lastPing)); + mapSeenZnodeBroadcast.insert(std::make_pair(mnb.GetHash(), std::make_pair(GetTime(), mnb))); + + LogPrintf("CZnodeMan::UpdateZnodeList -- znode=%s addr=%s\n", mnb.vin.prevout.ToStringShort(), mnb.addr.ToString()); + + CZnode* pmn = Find(mnb.vin); + if(pmn == NULL) { + CZnode mn(mnb); + if(Add(mn)) { + znodeSync.AddedZnodeList(); + } + } else { + CZnodeBroadcast mnbOld = mapSeenZnodeBroadcast[CZnodeBroadcast(*pmn).GetHash()].second; + if(pmn->UpdateFromNewBroadcast(mnb)) { + znodeSync.AddedZnodeList(); + mapSeenZnodeBroadcast.erase(mnbOld.GetHash()); + } + } +} + +bool CZnodeMan::CheckMnbAndUpdateZnodeList(CNode* pfrom, CZnodeBroadcast mnb, int& nDos) +{ + // Need LOCK2 here to ensure consistent locking order because the SimpleCheck call below locks cs_main + LOCK2(cs_main, cs); + + nDos = 0; + LogPrint("znode", "CZnodeMan::CheckMnbAndUpdateZnodeList -- znode=%s\n", mnb.vin.prevout.ToStringShort()); + + uint256 hash = mnb.GetHash(); + if(mapSeenZnodeBroadcast.count(hash) && !mnb.fRecovery) { //seen + LogPrint("znode", "CZnodeMan::CheckMnbAndUpdateZnodeList -- znode=%s seen\n", mnb.vin.prevout.ToStringShort()); + // less then 2 pings left before this MN goes into non-recoverable state, bump sync timeout + if(GetTime() - mapSeenZnodeBroadcast[hash].first > ZNODE_NEW_START_REQUIRED_SECONDS - ZNODE_MIN_MNP_SECONDS * 2) { + LogPrint("znode", "CZnodeMan::CheckMnbAndUpdateZnodeList -- znode=%s seen update\n", mnb.vin.prevout.ToStringShort()); + mapSeenZnodeBroadcast[hash].first = GetTime(); + znodeSync.AddedZnodeList(); + } + // did we ask this node for it? + if(pfrom && IsMnbRecoveryRequested(hash) && GetTime() < mMnbRecoveryRequests[hash].first) { + LogPrint("znode", "CZnodeMan::CheckMnbAndUpdateZnodeList -- mnb=%s seen request\n", hash.ToString()); + if(mMnbRecoveryRequests[hash].second.count(pfrom->addr)) { + LogPrint("znode", "CZnodeMan::CheckMnbAndUpdateZnodeList -- mnb=%s seen request, addr=%s\n", hash.ToString(), pfrom->addr.ToString()); + // do not allow node to send same mnb multiple times in recovery mode + mMnbRecoveryRequests[hash].second.erase(pfrom->addr); + // does it have newer lastPing? + if(mnb.lastPing.sigTime > mapSeenZnodeBroadcast[hash].second.lastPing.sigTime) { + // simulate Check + CZnode mnTemp = CZnode(mnb); + mnTemp.Check(); + LogPrint("znode", "CZnodeMan::CheckMnbAndUpdateZnodeList -- mnb=%s seen request, addr=%s, better lastPing: %d min ago, projected mn state: %s\n", hash.ToString(), pfrom->addr.ToString(), (GetTime() - mnb.lastPing.sigTime)/60, mnTemp.GetStateString()); + if(mnTemp.IsValidStateForAutoStart(mnTemp.nActiveState)) { + // this node thinks it's a good one + LogPrint("znode", "CZnodeMan::CheckMnbAndUpdateZnodeList -- znode=%s seen good\n", mnb.vin.prevout.ToStringShort()); + mMnbRecoveryGoodReplies[hash].push_back(mnb); + } + } + } + } + return true; + } + mapSeenZnodeBroadcast.insert(std::make_pair(hash, std::make_pair(GetTime(), mnb))); + + LogPrint("znode", "CZnodeMan::CheckMnbAndUpdateZnodeList -- znode=%s new\n", mnb.vin.prevout.ToStringShort()); + + if(!mnb.SimpleCheck(nDos)) { + LogPrint("znode", "CZnodeMan::CheckMnbAndUpdateZnodeList -- SimpleCheck() failed, znode=%s\n", mnb.vin.prevout.ToStringShort()); + return false; + } + + // search Znode list + CZnode* pmn = Find(mnb.vin); + if(pmn) { + CZnodeBroadcast mnbOld = mapSeenZnodeBroadcast[CZnodeBroadcast(*pmn).GetHash()].second; + if(!mnb.Update(pmn, nDos)) { + LogPrint("znode", "CZnodeMan::CheckMnbAndUpdateZnodeList -- Update() failed, znode=%s\n", mnb.vin.prevout.ToStringShort()); + return false; + } + if(hash != mnbOld.GetHash()) { + mapSeenZnodeBroadcast.erase(mnbOld.GetHash()); + } + } else { + if(mnb.CheckOutpoint(nDos)) { + Add(mnb); + znodeSync.AddedZnodeList(); + // if it matches our Znode privkey... + if(fZNode && mnb.pubKeyZnode == activeZnode.pubKeyZnode) { + mnb.nPoSeBanScore = -ZNODE_POSE_BAN_MAX_SCORE; + if(mnb.nProtocolVersion == PROTOCOL_VERSION) { + // ... and PROTOCOL_VERSION, then we've been remotely activated ... + LogPrintf("CZnodeMan::CheckMnbAndUpdateZnodeList -- Got NEW Znode entry: znode=%s sigTime=%lld addr=%s\n", + mnb.vin.prevout.ToStringShort(), mnb.sigTime, mnb.addr.ToString()); + activeZnode.ManageState(); + } else { + // ... otherwise we need to reactivate our node, do not add it to the list and do not relay + // but also do not ban the node we get this message from + LogPrintf("CZnodeMan::CheckMnbAndUpdateZnodeList -- wrong PROTOCOL_VERSION, re-activate your MN: message nProtocolVersion=%d PROTOCOL_VERSION=%d\n", mnb.nProtocolVersion, PROTOCOL_VERSION); + return false; + } + } + mnb.Relay(); + } else { + LogPrintf("CZnodeMan::CheckMnbAndUpdateZnodeList -- Rejected Znode entry: %s addr=%s\n", mnb.vin.prevout.ToStringShort(), mnb.addr.ToString()); + return false; + } + } + + return true; +} + +void CZnodeMan::UpdateLastPaid() +{ + LOCK(cs); + + if(fLiteMode) return; + if(!pCurrentBlockIndex) return; + + static bool IsFirstRun = true; + // Do full scan on first run or if we are not a znode + // (MNs should update this info on every block, so limited scan should be enough for them) + int nMaxBlocksToScanBack = (IsFirstRun || !fZNode) ? mnpayments.GetStorageLimit() : LAST_PAID_SCAN_BLOCKS; + + // LogPrint("mnpayments", "CZnodeMan::UpdateLastPaid -- nHeight=%d, nMaxBlocksToScanBack=%d, IsFirstRun=%s\n", + // pCurrentBlockIndex->nHeight, nMaxBlocksToScanBack, IsFirstRun ? "true" : "false"); + + BOOST_FOREACH(CZnode& mn, vZnodes) { + mn.UpdateLastPaid(pCurrentBlockIndex, nMaxBlocksToScanBack); + } + + // every time is like the first time if winners list is not synced + IsFirstRun = !znodeSync.IsWinnersListSynced(); +} + +void CZnodeMan::CheckAndRebuildZnodeIndex() +{ + LOCK(cs); + + if(GetTime() - nLastIndexRebuildTime < MIN_INDEX_REBUILD_TIME) { + return; + } + + if(indexZnodes.GetSize() <= MAX_EXPECTED_INDEX_SIZE) { + return; + } + + if(indexZnodes.GetSize() <= int(vZnodes.size())) { + return; + } + + indexZnodesOld = indexZnodes; + indexZnodes.Clear(); + for(size_t i = 0; i < vZnodes.size(); ++i) { + indexZnodes.AddZnodeVIN(vZnodes[i].vin); + } + + fIndexRebuilt = true; + nLastIndexRebuildTime = GetTime(); +} + +void CZnodeMan::UpdateWatchdogVoteTime(const CTxIn& vin) +{ + LOCK(cs); + CZnode* pMN = Find(vin); + if(!pMN) { + return; + } + pMN->UpdateWatchdogVoteTime(); + nLastWatchdogVoteTime = GetTime(); +} + +bool CZnodeMan::IsWatchdogActive() +{ + LOCK(cs); + // Check if any znodes have voted recently, otherwise return false + return (GetTime() - nLastWatchdogVoteTime) <= ZNODE_WATCHDOG_MAX_SECONDS; +} + +//bool CZnodeMan::AddGovernanceVote(const CTxIn& vin, uint256 nGovernanceObjectHash) +//{ +// LOCK(cs); +// CZnode* pMN = Find(vin); +// if(!pMN) { +// return false; +// } +// pMN->AddGovernanceVote(nGovernanceObjectHash); +// return true; +//} + +//void CZnodeMan::RemoveGovernanceObject(uint256 nGovernanceObjectHash) +//{ +// LOCK(cs); +// BOOST_FOREACH(CZnode& mn, vZnodes) { +// mn.RemoveGovernanceObject(nGovernanceObjectHash); +// } +//} + +void CZnodeMan::CheckZnode(const CTxIn& vin, bool fForce) +{ + LOCK(cs); + CZnode* pMN = Find(vin); + if(!pMN) { + return; + } + pMN->Check(fForce); +} + +void CZnodeMan::CheckZnode(const CPubKey& pubKeyZnode, bool fForce) +{ + LOCK(cs); + CZnode* pMN = Find(pubKeyZnode); + if(!pMN) { + return; + } + pMN->Check(fForce); +} + +int CZnodeMan::GetZnodeState(const CTxIn& vin) +{ + LOCK(cs); + CZnode* pMN = Find(vin); + if(!pMN) { + return CZnode::ZNODE_NEW_START_REQUIRED; + } + return pMN->nActiveState; +} + +int CZnodeMan::GetZnodeState(const CPubKey& pubKeyZnode) +{ + LOCK(cs); + CZnode* pMN = Find(pubKeyZnode); + if(!pMN) { + return CZnode::ZNODE_NEW_START_REQUIRED; + } + return pMN->nActiveState; +} + +bool CZnodeMan::IsZnodePingedWithin(const CTxIn& vin, int nSeconds, int64_t nTimeToCheckAt) +{ + LOCK(cs); + CZnode* pMN = Find(vin); + if(!pMN) { + return false; + } + return pMN->IsPingedWithin(nSeconds, nTimeToCheckAt); +} + +void CZnodeMan::SetZnodeLastPing(const CTxIn& vin, const CZnodePing& mnp) +{ + LOCK(cs); + CZnode* pMN = Find(vin); + if(!pMN) { + return; + } + pMN->lastPing = mnp; + mapSeenZnodePing.insert(std::make_pair(mnp.GetHash(), mnp)); + + CZnodeBroadcast mnb(*pMN); + uint256 hash = mnb.GetHash(); + if(mapSeenZnodeBroadcast.count(hash)) { + mapSeenZnodeBroadcast[hash].second.lastPing = mnp; + } +} + +void CZnodeMan::UpdatedBlockTip(const CBlockIndex *pindex) +{ + pCurrentBlockIndex = pindex; + LogPrint("znode", "CZnodeMan::UpdatedBlockTip -- pCurrentBlockIndex->nHeight=%d\n", pCurrentBlockIndex->nHeight); + + CheckSameAddr(); + + if(fZNode) { + // normal wallet does not need to update this every block, doing update on rpc call should be enough + UpdateLastPaid(); + } +} + +void CZnodeMan::NotifyZnodeUpdates() +{ + // Avoid double locking + bool fZnodesAddedLocal = false; + bool fZnodesRemovedLocal = false; + { + LOCK(cs); + fZnodesAddedLocal = fZnodesAdded; + fZnodesRemovedLocal = fZnodesRemoved; + } + + if(fZnodesAddedLocal) { +// governance.CheckZnodeOrphanObjects(); +// governance.CheckZnodeOrphanVotes(); + } + if(fZnodesRemovedLocal) { +// governance.UpdateCachesAndClean(); + } + + LOCK(cs); + fZnodesAdded = false; + fZnodesRemoved = false; +} diff --git a/src/znodeman.h b/src/znodeman.h new file mode 100644 index 0000000000..a62fa2a6a4 --- /dev/null +++ b/src/znodeman.h @@ -0,0 +1,366 @@ +// Copyright (c) 2014-2017 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef ZNODEMAN_H +#define ZNODEMAN_H + +#include "znode.h" +#include "sync.h" + +using namespace std; + +class CZnodeMan; + +extern CZnodeMan mnodeman; + +/** + * Provides a forward and reverse index between MN vin's and integers. + * + * This mapping is normally add-only and is expected to be permanent + * It is only rebuilt if the size of the index exceeds the expected maximum number + * of MN's and the current number of known MN's. + * + * The external interface to this index is provided via delegation by CZnodeMan + */ +class CZnodeIndex +{ +public: // Types + typedef std::map index_m_t; + + typedef index_m_t::iterator index_m_it; + + typedef index_m_t::const_iterator index_m_cit; + + typedef std::map rindex_m_t; + + typedef rindex_m_t::iterator rindex_m_it; + + typedef rindex_m_t::const_iterator rindex_m_cit; + +private: + int nSize; + + index_m_t mapIndex; + + rindex_m_t mapReverseIndex; + +public: + CZnodeIndex(); + + int GetSize() const { + return nSize; + } + + /// Retrieve znode vin by index + bool Get(int nIndex, CTxIn& vinZnode) const; + + /// Get index of a znode vin + int GetZnodeIndex(const CTxIn& vinZnode) const; + + void AddZnodeVIN(const CTxIn& vinZnode); + + void Clear(); + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) + { + READWRITE(mapIndex); + if(ser_action.ForRead()) { + RebuildIndex(); + } + } + +private: + void RebuildIndex(); + +}; + +class CZnodeMan +{ +public: + typedef std::map index_m_t; + + typedef index_m_t::iterator index_m_it; + + typedef index_m_t::const_iterator index_m_cit; + +private: + static const int MAX_EXPECTED_INDEX_SIZE = 30000; + + /// Only allow 1 index rebuild per hour + static const int64_t MIN_INDEX_REBUILD_TIME = 3600; + + static const std::string SERIALIZATION_VERSION_STRING; + + static const int DSEG_UPDATE_SECONDS = 3 * 60 * 60; + + static const int LAST_PAID_SCAN_BLOCKS = 100; + + static const int MIN_POSE_PROTO_VERSION = 70203; + static const int MAX_POSE_CONNECTIONS = 10; + static const int MAX_POSE_RANK = 10; + static const int MAX_POSE_BLOCKS = 10; + + static const int MNB_RECOVERY_QUORUM_TOTAL = 10; + static const int MNB_RECOVERY_QUORUM_REQUIRED = 6; + static const int MNB_RECOVERY_MAX_ASK_ENTRIES = 10; + static const int MNB_RECOVERY_WAIT_SECONDS = 60; + static const int MNB_RECOVERY_RETRY_SECONDS = 3 * 60 * 60; + + + // critical section to protect the inner data structures + mutable CCriticalSection cs; + + // Keep track of current block index + const CBlockIndex *pCurrentBlockIndex; + + // map to hold all MNs + std::vector vZnodes; + // who's asked for the Znode list and the last time + std::map mAskedUsForZnodeList; + // who we asked for the Znode list and the last time + std::map mWeAskedForZnodeList; + // which Znodes we've asked for + std::map > mWeAskedForZnodeListEntry; + // who we asked for the znode verification + std::map mWeAskedForVerification; + + // these maps are used for znode recovery from ZNODE_NEW_START_REQUIRED state + std::map > > mMnbRecoveryRequests; + std::map > mMnbRecoveryGoodReplies; + std::list< std::pair > listScheduledMnbRequestConnections; + + int64_t nLastIndexRebuildTime; + + CZnodeIndex indexZnodes; + + CZnodeIndex indexZnodesOld; + + /// Set when index has been rebuilt, clear when read + bool fIndexRebuilt; + + /// Set when znodes are added, cleared when CGovernanceManager is notified + bool fZnodesAdded; + + /// Set when znodes are removed, cleared when CGovernanceManager is notified + bool fZnodesRemoved; + + std::vector vecDirtyGovernanceObjectHashes; + + int64_t nLastWatchdogVoteTime; + + friend class CZnodeSync; + +public: + // Keep track of all broadcasts I've seen + std::map > mapSeenZnodeBroadcast; + // Keep track of all pings I've seen + std::map mapSeenZnodePing; + // Keep track of all verifications I've seen + std::map mapSeenZnodeVerification; + // keep track of dsq count to prevent znodes from gaming darksend queue + int64_t nDsqCount; + + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + LOCK(cs); + std::string strVersion; + if(ser_action.ForRead()) { + READWRITE(strVersion); + } + else { + strVersion = SERIALIZATION_VERSION_STRING; + READWRITE(strVersion); + } + + READWRITE(vZnodes); + READWRITE(mAskedUsForZnodeList); + READWRITE(mWeAskedForZnodeList); + READWRITE(mWeAskedForZnodeListEntry); + READWRITE(mMnbRecoveryRequests); + READWRITE(mMnbRecoveryGoodReplies); + READWRITE(nLastWatchdogVoteTime); + READWRITE(nDsqCount); + + READWRITE(mapSeenZnodeBroadcast); + READWRITE(mapSeenZnodePing); + READWRITE(indexZnodes); + if(ser_action.ForRead() && (strVersion != SERIALIZATION_VERSION_STRING)) { + Clear(); + } + } + + CZnodeMan(); + + /// Add an entry + bool Add(CZnode &mn); + + /// Ask (source) node for mnb + void AskForMN(CNode *pnode, const CTxIn &vin); + void AskForMnb(CNode *pnode, const uint256 &hash); + + /// Check all Znodes + void Check(); + + /// Check all Znodes and remove inactive + void CheckAndRemove(); + + /// Clear Znode vector + void Clear(); + + /// Count Znodes filtered by nProtocolVersion. + /// Znode nProtocolVersion should match or be above the one specified in param here. + int CountZnodes(int nProtocolVersion = -1); + /// Count enabled Znodes filtered by nProtocolVersion. + /// Znode nProtocolVersion should match or be above the one specified in param here. + int CountEnabled(int nProtocolVersion = -1); + + /// Count Znodes by network type - NET_IPV4, NET_IPV6, NET_TOR + // int CountByIP(int nNetworkType); + + void DsegUpdate(CNode* pnode); + + /// Find an entry + CZnode* Find(const CScript &payee); + CZnode* Find(const CTxIn& vin); + CZnode* Find(const CPubKey& pubKeyZnode); + + /// Versions of Find that are safe to use from outside the class + bool Get(const CPubKey& pubKeyZnode, CZnode& znode); + bool Get(const CTxIn& vin, CZnode& znode); + + /// Retrieve znode vin by index + bool Get(int nIndex, CTxIn& vinZnode, bool& fIndexRebuiltOut) { + LOCK(cs); + fIndexRebuiltOut = fIndexRebuilt; + return indexZnodes.Get(nIndex, vinZnode); + } + + bool GetIndexRebuiltFlag() { + LOCK(cs); + return fIndexRebuilt; + } + + /// Get index of a znode vin + int GetZnodeIndex(const CTxIn& vinZnode) { + LOCK(cs); + return indexZnodes.GetZnodeIndex(vinZnode); + } + + /// Get old index of a znode vin + int GetZnodeIndexOld(const CTxIn& vinZnode) { + LOCK(cs); + return indexZnodesOld.GetZnodeIndex(vinZnode); + } + + /// Get znode VIN for an old index value + bool GetZnodeVinForIndexOld(int nZnodeIndex, CTxIn& vinZnodeOut) { + LOCK(cs); + return indexZnodesOld.Get(nZnodeIndex, vinZnodeOut); + } + + /// Get index of a znode vin, returning rebuild flag + int GetZnodeIndex(const CTxIn& vinZnode, bool& fIndexRebuiltOut) { + LOCK(cs); + fIndexRebuiltOut = fIndexRebuilt; + return indexZnodes.GetZnodeIndex(vinZnode); + } + + void ClearOldZnodeIndex() { + LOCK(cs); + indexZnodesOld.Clear(); + fIndexRebuilt = false; + } + + bool Has(const CTxIn& vin); + + znode_info_t GetZnodeInfo(const CTxIn& vin); + + znode_info_t GetZnodeInfo(const CPubKey& pubKeyZnode); + + /// Find an entry in the znode list that is next to be paid + CZnode* GetNextZnodeInQueueForPayment(int nBlockHeight, bool fFilterSigTime, int& nCount); + /// Same as above but use current block height + CZnode* GetNextZnodeInQueueForPayment(bool fFilterSigTime, int& nCount); + + /// Find a random entry + CZnode* FindRandomNotInVec(const std::vector &vecToExclude, int nProtocolVersion = -1); + + std::vector GetFullZnodeVector() { return vZnodes; } + + std::vector > GetZnodeRanks(int nBlockHeight = -1, int nMinProtocol=0); + int GetZnodeRank(const CTxIn &vin, int nBlockHeight, int nMinProtocol=0, bool fOnlyActive=true); + CZnode* GetZnodeByRank(int nRank, int nBlockHeight, int nMinProtocol=0, bool fOnlyActive=true); + + void ProcessZnodeConnections(); + std::pair > PopScheduledMnbRequestConnection(); + + void ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); + + void DoFullVerificationStep(); + void CheckSameAddr(); + bool SendVerifyRequest(const CAddress& addr, const std::vector& vSortedByAddr); + void SendVerifyReply(CNode* pnode, CZnodeVerification& mnv); + void ProcessVerifyReply(CNode* pnode, CZnodeVerification& mnv); + void ProcessVerifyBroadcast(CNode* pnode, const CZnodeVerification& mnv); + + /// Return the number of (unique) Znodes + int size() { return vZnodes.size(); } + + std::string ToString() const; + + /// Update znode list and maps using provided CZnodeBroadcast + void UpdateZnodeList(CZnodeBroadcast mnb); + /// Perform complete check and only then update list and maps + bool CheckMnbAndUpdateZnodeList(CNode* pfrom, CZnodeBroadcast mnb, int& nDos); + bool IsMnbRecoveryRequested(const uint256& hash) { return mMnbRecoveryRequests.count(hash); } + + void UpdateLastPaid(); + + void CheckAndRebuildZnodeIndex(); + + void AddDirtyGovernanceObjectHash(const uint256& nHash) + { + LOCK(cs); + vecDirtyGovernanceObjectHashes.push_back(nHash); + } + + std::vector GetAndClearDirtyGovernanceObjectHashes() + { + LOCK(cs); + std::vector vecTmp = vecDirtyGovernanceObjectHashes; + vecDirtyGovernanceObjectHashes.clear(); + return vecTmp;; + } + + bool IsWatchdogActive(); + void UpdateWatchdogVoteTime(const CTxIn& vin); + bool AddGovernanceVote(const CTxIn& vin, uint256 nGovernanceObjectHash); + void RemoveGovernanceObject(uint256 nGovernanceObjectHash); + + void CheckZnode(const CTxIn& vin, bool fForce = false); + void CheckZnode(const CPubKey& pubKeyZnode, bool fForce = false); + + int GetZnodeState(const CTxIn& vin); + int GetZnodeState(const CPubKey& pubKeyZnode); + + bool IsZnodePingedWithin(const CTxIn& vin, int nSeconds, int64_t nTimeToCheckAt = -1); + void SetZnodeLastPing(const CTxIn& vin, const CZnodePing& mnp); + + void UpdatedBlockTip(const CBlockIndex *pindex); + + /** + * Called to notify CGovernanceManager that the znode index has been updated. + * Must be called while not holding the CZnodeMan::cs mutex + */ + void NotifyZnodeUpdates(); + +}; + +#endif From 0b15c5c05d7e89823f3524f45a705c0e86cde76c Mon Sep 17 00:00:00 2001 From: "snguyen.ntu@gmail.com" Date: Mon, 13 Nov 2017 12:11:34 +0700 Subject: [PATCH 03/35] Fix constants at GetNextWork and reset diff at testnet. Fix missing headers. multiple definition of 'netfulfilledman' (ubuntu). add flat-database for caching masternode. fix ConnectNode (add fConnectZnode condition). add ConnectNodeDash --- configure.ac | 6 +- contrib/seeds/generate-seeds.py | 4 +- contrib/seeds/nodes_main.txt | 14 +- contrib/seeds/nodes_test.txt | 2 +- depends/packages/libevent.mk | 11 +- depends/patches/libevent/reuseaddr.patch | 21 + src/Makefile.am | 1 + src/activeznode.cpp | 16 +- src/chainparams.cpp | 69 +- src/chainparamsseeds.h | 17 +- src/consensus/consensus.h | 6 +- src/darksend.cpp | 1193 +++++++-------- src/darksend.h | 14 +- src/definition.h | 2 +- src/flat-database.h | 229 +++ src/init.cpp | 137 +- src/key.cpp | 1 + src/libzerocoin/Coin.cpp | 132 +- src/libzerocoin/Coin.h | 16 +- src/libzerocoin/CoinSpend.cpp | 78 +- src/libzerocoin/CoinSpend.h | 14 +- src/libzerocoin/ParamGeneration.cpp | 24 +- src/libzerocoin/ParamGeneration.h | 2 +- .../SerialNumberSignatureOfKnowledge.cpp | 11 +- .../SerialNumberSignatureOfKnowledge.h | 13 +- src/libzerocoin/SpendMetaData.cpp | 2 +- src/libzerocoin/SpendMetaData.h | 4 +- src/libzerocoin/Tutorial.cpp | 13 +- src/libzerocoin/Zerocoin.h | 9 + src/libzerocoin/bitcoin_bignum/bignum.h | 147 +- src/main.cpp | 703 ++++++--- src/main.h | 25 + src/miner.cpp | 23 +- src/net.cpp | 1070 ++++++-------- src/net.h | 2 +- src/pow.cpp | 32 +- src/primitives/block.cpp | 2 +- src/primitives/block.h | 4 + src/primitives/precomputed_hash.h | 2 +- src/primitives/transaction.cpp | 7 +- src/primitives/transaction.h | 8 +- src/protocol.cpp | 2 + src/protocol.h | 3 + src/pubkey.cpp | 1 + src/qt/bitcoingui.cpp | 128 +- src/random.cpp | 18 + src/rpc/mining.cpp | 6 +- src/rpc/rpcznode.cpp | 13 +- src/serialize.h | 20 + src/wallet/rpcwallet.cpp | 36 + src/wallet/wallet.cpp | 1296 ++++++++++------- src/wallet/wallet.h | 24 +- src/znode-payments.cpp | 97 +- src/znode-sync.cpp | 62 +- src/znode.cpp | 2 +- src/znode.h | 1 + src/znodeman.cpp | 56 +- 57 files changed, 3424 insertions(+), 2427 deletions(-) create mode 100644 depends/patches/libevent/reuseaddr.patch create mode 100644 src/flat-database.h diff --git a/configure.ac b/configure.ac index e9739ad4f3..f97c897da9 100644 --- a/configure.ac +++ b/configure.ac @@ -2,8 +2,8 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 0) define(_CLIENT_VERSION_MINOR, 13) -define(_CLIENT_VERSION_REVISION, 2) -define(_CLIENT_VERSION_BUILD, 8) +define(_CLIENT_VERSION_REVISION, 4) +define(_CLIENT_VERSION_BUILD, 0) define(_CLIENT_VERSION_IS_RELEASE, true) define(_COPYRIGHT_YEAR, 2017) define(_COPYRIGHT_HOLDERS,[The %s developers]) @@ -1122,7 +1122,7 @@ if test x$need_bundled_univalue = xyes; then AC_CONFIG_SUBDIRS([src/univalue]) fi -ac_configure_args="${ac_configure_args} --disable-shared --with-pic --with-bignum=no --enable-module-recovery" +ac_configure_args="${ac_configure_args} --disable-shared --with-pic --with-bignum=no --enable-module-recovery --enable-experimental --enable-module-ecdh" AC_CONFIG_SUBDIRS([src/secp256k1]) AC_OUTPUT diff --git a/contrib/seeds/generate-seeds.py b/contrib/seeds/generate-seeds.py index 9c4ec09e94..a533365a35 100755 --- a/contrib/seeds/generate-seeds.py +++ b/contrib/seeds/generate-seeds.py @@ -27,7 +27,7 @@ } static SeedSpec6 pnSeed6_test[]={ ... - } + }i These should be pasted into `src/chainparamsseeds.h`. ''' @@ -130,7 +130,7 @@ def main(): process_nodes(g, f, 'pnSeed6_main', 8168) g.write('\n') with open(os.path.join(indir,'nodes_test.txt'),'r') as f: - process_nodes(g, f, 'pnSeed6_test', 28168) + process_nodes(g, f, 'pnSeed6_test', 18168) g.write('#endif // BITCOIN_CHAINPARAMSSEEDS_H\n') if __name__ == '__main__': diff --git a/contrib/seeds/nodes_main.txt b/contrib/seeds/nodes_main.txt index 300fff0420..1e3584074e 100644 --- a/contrib/seeds/nodes_main.txt +++ b/contrib/seeds/nodes_main.txt @@ -1,5 +1,9 @@ -52.187.41.88:8168 -52.166.11.108:8168 -40.76.72.210:8168 -47.52.0.193:8168 -52.178.34.114:8168 \ No newline at end of file +45.77.205.5:8168 +45.32.232.195:8168 +104.238.172.164:8168 +45.63.91.151:8168 +45.77.184.187:8168 +45.77.51.98:8168 +45.77.61.203:8168 +45.76.206.15:8168 +45.76.149.126:8168 diff --git a/contrib/seeds/nodes_test.txt b/contrib/seeds/nodes_test.txt index 172c5f86c2..b5413728b3 100644 --- a/contrib/seeds/nodes_test.txt +++ b/contrib/seeds/nodes_test.txt @@ -1,5 +1,5 @@ # List of fixed seed nodes for testnet -52.175.244.22:28168 +#52.175.244.22:28168 52.232.105.188:28168 13.76.210.1:28168 diff --git a/depends/packages/libevent.mk b/depends/packages/libevent.mk index b7f74907fc..2e9be1e98c 100644 --- a/depends/packages/libevent.mk +++ b/depends/packages/libevent.mk @@ -1,8 +1,13 @@ package=libevent -$(package)_version=2.1.8 -$(package)_download_path=https://github.com/libevent/libevent/releases/download/release-2.1.8-stable +$(package)_version=2.0.22 +$(package)_download_path=https://github.com/libevent/libevent/releases/download/release-2.0.22-stable $(package)_file_name=$(package)-$($(package)_version)-stable.tar.gz -$(package)_sha256_hash=965cc5a8bb46ce4199a47e9b2c9e1cae3b137e8356ffdad6d94d3b9069b71dc2 +$(package)_sha256_hash=71c2c49f0adadacfdbe6332a372c38cf9c8b7895bb73dabeaa53cdcc1d4e1fa3 +$(package)_patches=reuseaddr.patch + +define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/reuseaddr.patch +endef define $(package)_set_vars $(package)_config_opts=--disable-shared --disable-openssl --disable-libevent-regress diff --git a/depends/patches/libevent/reuseaddr.patch b/depends/patches/libevent/reuseaddr.patch new file mode 100644 index 0000000000..58695c11f5 --- /dev/null +++ b/depends/patches/libevent/reuseaddr.patch @@ -0,0 +1,21 @@ +--- old/evutil.c 2015-08-28 19:26:23.488765923 -0400 ++++ new/evutil.c 2015-08-28 19:27:41.392767019 -0400 +@@ -321,15 +321,16 @@ + int + evutil_make_listen_socket_reuseable(evutil_socket_t sock) + { +-#ifndef WIN32 + int one = 1; ++#ifndef WIN32 + /* REUSEADDR on Unix means, "don't hang on to this address after the + * listener is closed." On Windows, though, it means "don't keep other + * processes from binding to this address while we're using it. */ + return setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*) &one, + (ev_socklen_t)sizeof(one)); + #else +- return 0; ++ return setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*) &one, ++ (ev_socklen_t)sizeof(one)); + #endif + } + diff --git a/src/Makefile.am b/src/Makefile.am index 6c86091ff4..e05bca824f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -281,6 +281,7 @@ libbitcoin_consensus_a_SOURCES = \ consensus/params.h \ consensus/validation.h \ hash.cpp \ + flat-database.h \ hash.h \ prevector.h \ crypto/scrypt.h \ diff --git a/src/activeznode.cpp b/src/activeznode.cpp index 1b477a570e..ad6c25583e 100644 --- a/src/activeznode.cpp +++ b/src/activeznode.cpp @@ -202,12 +202,12 @@ void CActiveZnode::ManageStateInitial() { LogPrintf("CActiveZnode::ManageStateInitial -- Checking inbound connection to '%s'\n", service.ToString()); //TODO -// if (!ConnectNodeDash(CAddress(service, NODE_NETWORK), NULL, true)) { -// nState = ACTIVE_ZNODE_NOT_CAPABLE; -// strNotCapableReason = "Could not connect to " + service.ToString(); -// LogPrintf("CActiveZnode::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); -// return; -// } + if (!ConnectNode(CAddress(service, NODE_NETWORK), NULL, false, true)) { + nState = ACTIVE_ZNODE_NOT_CAPABLE; + strNotCapableReason = "Could not connect to " + service.ToString(); + LogPrintf("CActiveZnode::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); + return; + } // Default to REMOTE eType = ZNODE_REMOTE; @@ -223,7 +223,7 @@ void CActiveZnode::ManageStateInitial() { return; } - if (pwalletMain->GetBalance() < 1000 * COIN) { + if (pwalletMain->GetBalance() < ZNODE_COIN_REQUIRED * COIN) { LogPrintf("CActiveZnode::ManageStateInitial -- %s: Wallet balance is < 1000 DASH\n", GetStateString()); return; } @@ -309,7 +309,7 @@ void CActiveZnode::ManageStateLocal() { CZnodeBroadcast mnb; std::string strError; if (!CZnodeBroadcast::Create(vin, service, keyCollateral, pubKeyCollateral, keyZnode, - pubKeyZnode, strError, mnb)) { + pubKeyZnode, strError, mnb)) { nState = ACTIVE_ZNODE_NOT_CAPABLE; strNotCapableReason = "Error creating mastenode broadcast: " + strError; LogPrintf("CActiveZnode::ManageStateLocal -- %s: %s\n", GetStateString(), strNotCapableReason); diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 7d89175066..8d362cc2fe 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -19,8 +19,7 @@ #include "arith_uint256.h" -static CBlock -CreateGenesisBlock(const char *pszTimestamp, const CScript &genesisOutputScript, uint32_t nTime, uint32_t nNonce, +static CBlock CreateGenesisBlock(const char *pszTimestamp, const CScript &genesisOutputScript, uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount &genesisReward, std::vector extraNonce) { CMutableTransaction txNew; @@ -176,18 +175,11 @@ class CMainParams : public CChainParams { vSeeds.push_back(CDNSSeedData("singapore.zcoin.io", "singapore.zcoin.io", false)); vSeeds.push_back(CDNSSeedData("nyc.zcoin.io", "nyc.zcoin.io", false)); // Note that of those with the service bits flag, most only support a subset of possible options - base58Prefixes[PUBKEY_ADDRESS] = std::vector < unsigned - char > (1, 82); - base58Prefixes[SCRIPT_ADDRESS] = std::vector < unsigned - char > (1, 7); - base58Prefixes[SECRET_KEY] = std::vector < unsigned - char > (1, 210); - base58Prefixes[EXT_PUBLIC_KEY] = - boost::assign::list_of(0x04)(0x88)(0xB2)(0x1E).convert_to_container < std::vector < unsigned - char > > (); - base58Prefixes[EXT_SECRET_KEY] = - boost::assign::list_of(0x04)(0x88)(0xAD)(0xE4).convert_to_container < std::vector < unsigned - char > > (); + base58Prefixes[PUBKEY_ADDRESS] = std::vector < unsigned char > (1, 82); + base58Prefixes[SCRIPT_ADDRESS] = std::vector < unsigned char > (1, 7); + base58Prefixes[SECRET_KEY] = std::vector < unsigned char > (1, 210); + base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x88)(0xB2)(0x1E).convert_to_container < std::vector < unsigned char > > (); + base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x88)(0xAD)(0xE4).convert_to_container < std::vector < unsigned char > > (); vFixedSeeds = std::vector(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main)); @@ -256,7 +248,7 @@ class CTestNetParams : public CChainParams { // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000000708f98bf623f02e"); // Maternode params - consensus.nZnodePaymentsStartBlock = 10000; // not true, but it's ok as long as it's less then n + consensus.nZnodePaymentsStartBlock = 100; // not true, but it's ok as long as it's less then n consensus.nZnodePaymentsIncreaseBlock = 46000; consensus.nZnodePaymentsIncreasePeriod = 576; consensus.nSuperblockStartBlock = 61000; @@ -274,7 +266,7 @@ class CTestNetParams : public CChainParams { pchMessageStart[1] = 0xfc; pchMessageStart[2] = 0xbe; pchMessageStart[3] = 0xea; - nDefaultPort = 28168; + nDefaultPort = 18168; nPruneAfterHeight = 1000; /** * btzc: testnet params @@ -299,8 +291,9 @@ class CTestNetParams : public CChainParams { vSeeds.clear(); // nodes with support for servicebits filtering should be at the top // zcoin test seeds - vSeeds.push_back(CDNSSeedData("beta1.zcoin.io", "beta1.zcoin.io", false)); - vSeeds.push_back(CDNSSeedData("beta2.zcoin.io", "beta2.zcoin.io", false)); +// vSeeds.push_back(CDNSSeedData("beta1.zcoin.io", "beta1.zcoin.io", false)); +// vSeeds.push_back(CDNSSeedData("beta2.zcoin.io", "beta2.zcoin.io", false)); + vSeeds.push_back(CDNSSeedData("54.165.247.127", "54.165.247.127", false)); // vSeeds.push_back(CDNSSeedData("testnetbitcoin.jonasschnelli.ch", "testnet-seed.bitcoin.jonasschnelli.ch", true)); @@ -308,18 +301,11 @@ class CTestNetParams : public CChainParams { // vSeeds.push_back(CDNSSeedData("bluematt.me", "testnet-seed.bluematt.me")); // vSeeds.push_back(CDNSSeedData("bitcoin.schildbach.de", "testnet-seed.bitcoin.schildbach.de")); - base58Prefixes[PUBKEY_ADDRESS] = std::vector < unsigned - char > (1, 65); - base58Prefixes[SCRIPT_ADDRESS] = std::vector < unsigned - char > (1, 178); - base58Prefixes[SECRET_KEY] = std::vector < unsigned - char > (1, 185); - base58Prefixes[EXT_PUBLIC_KEY] = - boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container < std::vector < unsigned - char > > (); - base58Prefixes[EXT_SECRET_KEY] = - boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container < std::vector < unsigned - char > > (); + base58Prefixes[PUBKEY_ADDRESS] = std::vector < unsigned char > (1, 65); + base58Prefixes[SCRIPT_ADDRESS] = std::vector < unsigned char > (1, 178); + base58Prefixes[SECRET_KEY] = std::vector < unsigned char > (1, 185); + base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container < std::vector < unsigned char > > (); + base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container < std::vector < unsigned char > > (); vFixedSeeds = std::vector(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test)); fMiningRequiresPeers = true; @@ -332,9 +318,9 @@ class CTestNetParams : public CChainParams { checkpointData = (CCheckpointData) { boost::assign::map_list_of (0, uint256S("0x")), - 1414776313, - 0, - 100.0 + 1414776313, + 0, + 100.0 }; } @@ -422,18 +408,11 @@ class CRegTestParams : public CChainParams { 0, 0 }; - base58Prefixes[PUBKEY_ADDRESS] = std::vector < unsigned - char > (1, 65); - base58Prefixes[SCRIPT_ADDRESS] = std::vector < unsigned - char > (1, 178); - base58Prefixes[SECRET_KEY] = std::vector < unsigned - char > (1, 239); - base58Prefixes[EXT_PUBLIC_KEY] = - boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container < std::vector < unsigned - char > > (); - base58Prefixes[EXT_SECRET_KEY] = - boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container < std::vector < unsigned - char > > (); + base58Prefixes[PUBKEY_ADDRESS] = std::vector < unsigned char > (1, 65); + base58Prefixes[SCRIPT_ADDRESS] = std::vector < unsigned char > (1, 178); + base58Prefixes[SECRET_KEY] = std::vector < unsigned char > (1, 239); + base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container < std::vector < unsigned char > > (); + base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container < std::vector < unsigned char > > (); } void UpdateBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout) { diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h index aca114bfa7..4b4f6f1aee 100644 --- a/src/chainparamsseeds.h +++ b/src/chainparamsseeds.h @@ -8,15 +8,18 @@ * IPv4 as well as onion addresses are wrapped inside a IPv6 address accordingly. */ static SeedSpec6 pnSeed6_main[] = { - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0xbb,0x29,0x58}, 8168}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0xa6,0x0b,0x6c}, 8168}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x28,0x4c,0x48,0xd2}, 8168}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2f,0x34,0x00,0xc1}, 8168}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0xb2,0x22,0x72}, 8168} + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x4d,0xcd,0x05}, 8168}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x20,0xe8,0xc3}, 8168}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x68,0xee,0xac,0xa4}, 8168}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x3f,0x5b,0x97}, 8168}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x4d,0xb8,0xbb}, 8168}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x4d,0x33,0x62}, 8168}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x4d,0x3d,0xcb}, 8168}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x4c,0xce,0x0f}, 8168}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x4c,0x95,0x7e}, 8168} }; static SeedSpec6 pnSeed6_test[] = { - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0xe8,0x69,0xbc}, 28168}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x0d,0x4c,0xd2,0x01}, 28168} + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0xaf,0xf4,0x16}, 18168} }; #endif // BITCOIN_CHAINPARAMSSEEDS_H \ No newline at end of file diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index 23a75c964b..6e8e3322da 100644 --- a/src/consensus/consensus.h +++ b/src/consensus/consensus.h @@ -19,8 +19,8 @@ static const int HF_ZNODE_HEIGHT = 65000; static const int HF_LYRA2VAR_HEIGHT_TESTNET = 10; static const int HF_LYRA2_HEIGHT_TESTNET = 25; // for consistent purpose since the algo hash is so low -static const int HF_LYRA2Z_HEIGHT_TESTNET = 30; -static const int HF_ZNODE_HEIGHT_TESTNET = 1000; +static const int HF_LYRA2Z_HEIGHT_TESTNET = 30; +static const int HF_ZNODE_HEIGHT_TESTNET = 500; //static const int HF_MTP_HEIGHT_TESTNET = 30; static const int HF_ZEROSPEND_FIX = 22000; @@ -32,7 +32,7 @@ static const unsigned int MAX_BLOCK_WEIGHT = 2000000; /** The maximum allowed size for a block excluding witness data, in bytes (network rule) */ static const unsigned int MAX_BLOCK_BASE_SIZE = 2000000; /** The maximum allowed number of signature check operations in a block (network rule) */ -static const int64_t MAX_BLOCK_SIGOPS_COST = 40000; +static const int64_t MAX_BLOCK_SIGOPS_COST = 400000; /** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */ static const int COINBASE_MATURITY = 100; diff --git a/src/darksend.cpp b/src/darksend.cpp index 14e533bc41..f08f0b312f 100644 --- a/src/darksend.cpp +++ b/src/darksend.cpp @@ -27,29 +27,28 @@ bool fPrivateSendMultiSession = DEFAULT_PRIVATESEND_MULTISESSION; CDarksendPool darkSendPool; CDarkSendSigner darkSendSigner; -std::map mapDarksendBroadcastTxes; -std::vector vecPrivateSendDenominations; +std::map mapDarksendBroadcastTxes; +std::vector vecPrivateSendDenominations; -void CDarksendPool::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) -{ - if(fLiteMode) return; // ignore all Dash related functionality - if(!znodeSync.IsBlockchainSynced()) return; +void CDarksendPool::ProcessMessage(CNode *pfrom, std::string &strCommand, CDataStream &vRecv) { + if (fLiteMode) return; // ignore all Dash related functionality + if (!znodeSync.IsBlockchainSynced()) return; - if(strCommand == NetMsgType::DSACCEPT) { + if (strCommand == NetMsgType::DSACCEPT) { - if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { + if (pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { LogPrintf("DSACCEPT -- incompatible version! nVersion: %d\n", pfrom->nVersion); PushStatus(pfrom, STATUS_REJECTED, ERR_VERSION); return; } - if(!fZNode) { + if (!fZNode) { LogPrintf("DSACCEPT -- not a Znode!\n"); PushStatus(pfrom, STATUS_REJECTED, ERR_NOT_A_MN); return; } - if(IsSessionReady()) { + if (IsSessionReady()) { // too many users in this session already, reject new ones LogPrintf("DSACCEPT -- queue is already full!\n"); PushStatus(pfrom, STATUS_ACCEPTED, ERR_QUEUE_FULL); @@ -62,15 +61,14 @@ void CDarksendPool::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataS LogPrint("privatesend", "DSACCEPT -- nDenom %d (%s) txCollateral %s", nDenom, GetDenominationsToString(nDenom), txCollateral.ToString()); - CZnode* pmn = mnodeman.Find(activeZnode.vin); - if(pmn == NULL) { + CZnode *pmn = mnodeman.Find(activeZnode.vin); + if (pmn == NULL) { PushStatus(pfrom, STATUS_REJECTED, ERR_MN_LIST); return; } - if(vecSessionCollaterals.size() == 0 && pmn->nLastDsq != 0 && - pmn->nLastDsq + mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION)/5 > mnodeman.nDsqCount) - { + if (vecSessionCollaterals.size() == 0 && pmn->nLastDsq != 0 && + pmn->nLastDsq + mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION) / 5 > mnodeman.nDsqCount) { LogPrintf("DSACCEPT -- last dsq too recent, must wait: addr=%s\n", pfrom->addr.ToString()); PushStatus(pfrom, STATUS_REJECTED, ERR_RECENT); return; @@ -78,9 +76,9 @@ void CDarksendPool::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataS PoolMessage nMessageID = MSG_NOERR; - bool fResult = nSessionID == 0 ? CreateNewSession(nDenom, txCollateral, nMessageID) - : AddUserToExistingSession(nDenom, txCollateral, nMessageID); - if(fResult) { + bool fResult = nSessionID == 0 ? CreateNewSession(nDenom, txCollateral, nMessageID) + : AddUserToExistingSession(nDenom, txCollateral, nMessageID); + if (fResult) { LogPrintf("DSACCEPT -- is compatible, please submit!\n"); PushStatus(pfrom, STATUS_ACCEPTED, nMessageID); return; @@ -90,11 +88,11 @@ void CDarksendPool::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataS return; } - } else if(strCommand == NetMsgType::DSQUEUE) { + } else if (strCommand == NetMsgType::DSQUEUE) { TRY_LOCK(cs_darksend, lockRecv); - if(!lockRecv) return; + if (!lockRecv) return; - if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { + if (pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { LogPrint("privatesend", "DSQUEUE -- incompatible version! nVersion: %d\n", pfrom->nVersion); return; } @@ -103,8 +101,9 @@ void CDarksendPool::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataS vRecv >> dsq; // process every dsq only once - BOOST_FOREACH(CDarksendQueue q, vecDarksendQueue) { - if(q == dsq) { + BOOST_FOREACH(CDarksendQueue + q, vecDarksendQueue) { + if (q == dsq) { // LogPrint("privatesend", "DSQUEUE -- %s seen\n", dsq.ToString()); return; } @@ -112,42 +111,43 @@ void CDarksendPool::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataS LogPrint("privatesend", "DSQUEUE -- %s new\n", dsq.ToString()); - if(dsq.IsExpired() || dsq.nTime > GetTime() + PRIVATESEND_QUEUE_TIMEOUT) return; + if (dsq.IsExpired() || dsq.nTime > GetTime() + PRIVATESEND_QUEUE_TIMEOUT) return; - CZnode* pmn = mnodeman.Find(dsq.vin); - if(pmn == NULL) return; + CZnode *pmn = mnodeman.Find(dsq.vin); + if (pmn == NULL) return; - if(!dsq.CheckSignature(pmn->pubKeyZnode)) { + if (!dsq.CheckSignature(pmn->pubKeyZnode)) { // we probably have outdated info mnodeman.AskForMN(pfrom, dsq.vin); return; } // if the queue is ready, submit if we can - if(dsq.fReady) { - if(!pSubmittedToZnode) return; - if((CNetAddr)pSubmittedToZnode->addr != (CNetAddr)pmn->addr) { + if (dsq.fReady) { + if (!pSubmittedToZnode) return; + if ((CNetAddr) pSubmittedToZnode->addr != (CNetAddr) pmn->addr) { LogPrintf("DSQUEUE -- message doesn't match current Znode: pSubmittedToZnode=%s, addr=%s\n", pSubmittedToZnode->addr.ToString(), pmn->addr.ToString()); return; } - if(nState == POOL_STATE_QUEUE) { + if (nState == POOL_STATE_QUEUE) { LogPrint("privatesend", "DSQUEUE -- PrivateSend queue (%s) is ready on znode %s\n", dsq.ToString(), pmn->addr.ToString()); SubmitDenominate(); } } else { - BOOST_FOREACH(CDarksendQueue q, vecDarksendQueue) { - if(q.vin == dsq.vin) { + BOOST_FOREACH(CDarksendQueue + q, vecDarksendQueue) { + if (q.vin == dsq.vin) { // no way same mn can send another "not yet ready" dsq this soon LogPrint("privatesend", "DSQUEUE -- Znode %s is sending WAY too many dsq messages\n", pmn->addr.ToString()); return; } } - int nThreshold = pmn->nLastDsq + mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION)/5; + int nThreshold = pmn->nLastDsq + mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION) / 5; LogPrint("privatesend", "DSQUEUE -- nLastDsq: %d threshold: %d nDsqCount: %d\n", pmn->nLastDsq, nThreshold, mnodeman.nDsqCount); //don't allow a few nodes to dominate the queuing process - if(pmn->nLastDsq != 0 && nThreshold > mnodeman.nDsqCount) { + if (pmn->nLastDsq != 0 && nThreshold > mnodeman.nDsqCount) { LogPrint("privatesend", "DSQUEUE -- Znode %s is sending too many dsq messages\n", pmn->addr.ToString()); return; } @@ -156,29 +156,29 @@ void CDarksendPool::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataS pmn->fAllowMixingTx = true; LogPrint("privatesend", "DSQUEUE -- new PrivateSend queue (%s) from znode %s\n", dsq.ToString(), pmn->addr.ToString()); - if(pSubmittedToZnode && pSubmittedToZnode->vin.prevout == dsq.vin.prevout) { + if (pSubmittedToZnode && pSubmittedToZnode->vin.prevout == dsq.vin.prevout) { dsq.fTried = true; } vecDarksendQueue.push_back(dsq); dsq.Relay(); } - } else if(strCommand == NetMsgType::DSVIN) { + } else if (strCommand == NetMsgType::DSVIN) { - if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { + if (pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { LogPrintf("DSVIN -- incompatible version! nVersion: %d\n", pfrom->nVersion); PushStatus(pfrom, STATUS_REJECTED, ERR_VERSION); return; } - if(!fZNode) { + if (!fZNode) { LogPrintf("DSVIN -- not a Znode!\n"); PushStatus(pfrom, STATUS_REJECTED, ERR_NOT_A_MN); return; } //do we have enough users in the current session? - if(!IsSessionReady()) { + if (!IsSessionReady()) { LogPrintf("DSVIN -- session not complete!\n"); PushStatus(pfrom, STATUS_REJECTED, ERR_SESSION); return; @@ -190,7 +190,7 @@ void CDarksendPool::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataS LogPrint("privatesend", "DSVIN -- txCollateral %s", entry.txCollateral.ToString()); //do we have the same denominations as the current session? - if(!IsOutputsCompatibleWithSessionDenom(entry.vecTxDSOut)) { + if (!IsOutputsCompatibleWithSessionDenom(entry.vecTxDSOut)) { LogPrintf("DSVIN -- not compatible with existing transactions!\n"); PushStatus(pfrom, STATUS_REJECTED, ERR_EXISTING_TX); return; @@ -203,31 +203,33 @@ void CDarksendPool::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataS CMutableTransaction tx; - BOOST_FOREACH(const CTxOut txout, entry.vecTxDSOut) { + BOOST_FOREACH( + const CTxOut txout, entry.vecTxDSOut) { nValueOut += txout.nValue; tx.vout.push_back(txout); - if(txout.scriptPubKey.size() != 25) { + if (txout.scriptPubKey.size() != 25) { LogPrintf("DSVIN -- non-standard pubkey detected! scriptPubKey=%s\n", ScriptToAsmStr(txout.scriptPubKey)); PushStatus(pfrom, STATUS_REJECTED, ERR_NON_STANDARD_PUBKEY); return; } - if(!txout.scriptPubKey.IsNormalPaymentScript()) { + if (!txout.scriptPubKey.IsNormalPaymentScript()) { LogPrintf("DSVIN -- invalid script! scriptPubKey=%s\n", ScriptToAsmStr(txout.scriptPubKey)); PushStatus(pfrom, STATUS_REJECTED, ERR_INVALID_SCRIPT); return; } } - BOOST_FOREACH(const CTxIn txin, entry.vecTxDSIn) { + BOOST_FOREACH( + const CTxIn txin, entry.vecTxDSIn) { tx.vin.push_back(txin); LogPrint("privatesend", "DSVIN -- txin=%s\n", txin.ToString()); CTransaction txPrev; uint256 hash; - if(GetTransaction(txin.prevout.hash, txPrev, Params().GetConsensus(), hash, true)) { - if(txPrev.vout.size() > txin.prevout.n) + if (GetTransaction(txin.prevout.hash, txPrev, Params().GetConsensus(), hash, true)) { + if (txPrev.vout.size() > txin.prevout.n) nValueIn += txPrev.vout[txin.prevout.n].nValue; } else { LogPrintf("DSVIN -- missing input! tx=%s", tx.ToString()); @@ -236,7 +238,7 @@ void CDarksendPool::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataS } } - if(nValueIn > PRIVATESEND_POOL_MAX) { + if (nValueIn > PRIVATESEND_POOL_MAX) { LogPrintf("DSVIN -- more than PrivateSend pool max! nValueIn: %lld, tx=%s", nValueIn, tx.ToString()); PushStatus(pfrom, STATUS_REJECTED, ERR_MAXIMUM); return; @@ -244,7 +246,7 @@ void CDarksendPool::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataS // Allow lowest denom (at max) as a a fee. Normally shouldn't happen though. // TODO: Or do not allow fees at all? - if(nValueIn - nValueOut > vecPrivateSendDenominations.back()) { + if (nValueIn - nValueOut > vecPrivateSendDenominations.back()) { LogPrintf("DSVIN -- fees are too high! fees: %lld, tx=%s", nValueIn - nValueOut, tx.ToString()); PushStatus(pfrom, STATUS_REJECTED, ERR_FEES); return; @@ -253,8 +255,8 @@ void CDarksendPool::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataS { LOCK(cs_main); CValidationState validationState; - mempool.PrioritiseTransaction(tx.GetHash(), tx.GetHash().ToString(), 1000, 0.1*COIN); - if(!AcceptToMemoryPool(mempool, validationState, CTransaction(tx), true, false, NULL, false, true, true)) { + mempool.PrioritiseTransaction(tx.GetHash(), tx.GetHash().ToString(), 1000, 0.1 * COIN); + if (!AcceptToMemoryPool(mempool, validationState, CTransaction(tx), true, false, NULL, false, true, true)) { LogPrintf("DSVIN -- transaction not valid! tx=%s", tx.ToString()); PushStatus(pfrom, STATUS_REJECTED, ERR_INVALID_TX); return; @@ -264,7 +266,7 @@ void CDarksendPool::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataS PoolMessage nMessageID = MSG_NOERR; - if(AddEntry(entry, nMessageID)) { + if (AddEntry(entry, nMessageID)) { PushStatus(pfrom, STATUS_ACCEPTED, nMessageID); CheckPool(); RelayStatus(STATUS_ACCEPTED); @@ -273,20 +275,20 @@ void CDarksendPool::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataS SetNull(); } - } else if(strCommand == NetMsgType::DSSTATUSUPDATE) { + } else if (strCommand == NetMsgType::DSSTATUSUPDATE) { - if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { + if (pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { LogPrintf("DSSTATUSUPDATE -- incompatible version! nVersion: %d\n", pfrom->nVersion); return; } - if(fZNode) { + if (fZNode) { // LogPrintf("DSSTATUSUPDATE -- Can't run on a Znode!\n"); return; } - if(!pSubmittedToZnode) return; - if((CNetAddr)pSubmittedToZnode->addr != (CNetAddr)pfrom->addr) { + if (!pSubmittedToZnode) return; + if ((CNetAddr) pSubmittedToZnode->addr != (CNetAddr) pfrom->addr) { //LogPrintf("DSSTATUSUPDATE -- message doesn't match current Znode: pSubmittedToZnode %s addr %s\n", pSubmittedToZnode->addr.ToString(), pfrom->addr.ToString()); return; } @@ -299,52 +301,53 @@ void CDarksendPool::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataS vRecv >> nMsgSessionID >> nMsgState >> nMsgEntriesCount >> nMsgStatusUpdate >> nMsgMessageID; LogPrint("privatesend", "DSSTATUSUPDATE -- nMsgSessionID %d nMsgState: %d nEntriesCount: %d nMsgStatusUpdate: %d nMsgMessageID %d\n", - nMsgSessionID, nMsgState, nEntriesCount, nMsgStatusUpdate, nMsgMessageID); + nMsgSessionID, nMsgState, nEntriesCount, nMsgStatusUpdate, nMsgMessageID); - if(nMsgState < POOL_STATE_MIN || nMsgState > POOL_STATE_MAX) { + if (nMsgState < POOL_STATE_MIN || nMsgState > POOL_STATE_MAX) { LogPrint("privatesend", "DSSTATUSUPDATE -- nMsgState is out of bounds: %d\n", nMsgState); return; } - if(nMsgStatusUpdate < STATUS_REJECTED || nMsgStatusUpdate > STATUS_ACCEPTED) { + if (nMsgStatusUpdate < STATUS_REJECTED || nMsgStatusUpdate > STATUS_ACCEPTED) { LogPrint("privatesend", "DSSTATUSUPDATE -- nMsgStatusUpdate is out of bounds: %d\n", nMsgStatusUpdate); return; } - if(nMsgMessageID < MSG_POOL_MIN || nMsgMessageID > MSG_POOL_MAX) { + if (nMsgMessageID < MSG_POOL_MIN || nMsgMessageID > MSG_POOL_MAX) { LogPrint("privatesend", "DSSTATUSUPDATE -- nMsgMessageID is out of bounds: %d\n", nMsgMessageID); return; } LogPrint("privatesend", "DSSTATUSUPDATE -- GetMessageByID: %s\n", GetMessageByID(PoolMessage(nMsgMessageID))); - if(!CheckPoolStateUpdate(PoolState(nMsgState), nMsgEntriesCount, PoolStatusUpdate(nMsgStatusUpdate), PoolMessage(nMsgMessageID), nMsgSessionID)) { + if (!CheckPoolStateUpdate(PoolState(nMsgState), nMsgEntriesCount, PoolStatusUpdate(nMsgStatusUpdate), PoolMessage(nMsgMessageID), nMsgSessionID)) { LogPrint("privatesend", "DSSTATUSUPDATE -- CheckPoolStateUpdate failed\n"); } - } else if(strCommand == NetMsgType::DSSIGNFINALTX) { + } else if (strCommand == NetMsgType::DSSIGNFINALTX) { - if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { + if (pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { LogPrintf("DSSIGNFINALTX -- incompatible version! nVersion: %d\n", pfrom->nVersion); return; } - if(!fZNode) { + if (!fZNode) { LogPrintf("DSSIGNFINALTX -- not a Znode!\n"); return; } - std::vector vecTxIn; + std::vector vecTxIn; vRecv >> vecTxIn; LogPrint("privatesend", "DSSIGNFINALTX -- vecTxIn.size() %s\n", vecTxIn.size()); int nTxInIndex = 0; - int nTxInsCount = (int)vecTxIn.size(); + int nTxInsCount = (int) vecTxIn.size(); - BOOST_FOREACH(const CTxIn txin, vecTxIn) { + BOOST_FOREACH( + const CTxIn txin, vecTxIn) { nTxInIndex++; - if(!AddScriptSig(txin)) { + if (!AddScriptSig(txin)) { LogPrint("privatesend", "DSSIGNFINALTX -- AddScriptSig() failed at %d/%d, session: %d\n", nTxInIndex, nTxInsCount, nSessionID); RelayStatus(STATUS_REJECTED); return; @@ -354,20 +357,20 @@ void CDarksendPool::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataS // all is good CheckPool(); - } else if(strCommand == NetMsgType::DSFINALTX) { + } else if (strCommand == NetMsgType::DSFINALTX) { - if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { + if (pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { LogPrintf("DSFINALTX -- incompatible version! nVersion: %d\n", pfrom->nVersion); return; } - if(fZNode) { + if (fZNode) { // LogPrintf("DSFINALTX -- Can't run on a Znode!\n"); return; } - if(!pSubmittedToZnode) return; - if((CNetAddr)pSubmittedToZnode->addr != (CNetAddr)pfrom->addr) { + if (!pSubmittedToZnode) return; + if ((CNetAddr) pSubmittedToZnode->addr != (CNetAddr) pfrom->addr) { //LogPrintf("DSFINALTX -- message doesn't match current Znode: pSubmittedToZnode %s addr %s\n", pSubmittedToZnode->addr.ToString(), pfrom->addr.ToString()); return; } @@ -376,7 +379,7 @@ void CDarksendPool::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataS CTransaction txNew; vRecv >> nMsgSessionID >> txNew; - if(nSessionID != nMsgSessionID) { + if (nSessionID != nMsgSessionID) { LogPrint("privatesend", "DSFINALTX -- message doesn't match current PrivateSend session: nSessionID: %d nMsgSessionID: %d\n", nSessionID, nMsgSessionID); return; } @@ -386,20 +389,20 @@ void CDarksendPool::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataS //check to see if input is spent already? (and probably not confirmed) SignFinalTransaction(txNew, pfrom); - } else if(strCommand == NetMsgType::DSCOMPLETE) { + } else if (strCommand == NetMsgType::DSCOMPLETE) { - if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { + if (pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { LogPrintf("DSCOMPLETE -- incompatible version! nVersion: %d\n", pfrom->nVersion); return; } - if(fZNode) { + if (fZNode) { // LogPrintf("DSCOMPLETE -- Can't run on a Znode!\n"); return; } - if(!pSubmittedToZnode) return; - if((CNetAddr)pSubmittedToZnode->addr != (CNetAddr)pfrom->addr) { + if (!pSubmittedToZnode) return; + if ((CNetAddr) pSubmittedToZnode->addr != (CNetAddr) pfrom->addr) { LogPrint("privatesend", "DSCOMPLETE -- message doesn't match current Znode: pSubmittedToZnode=%s addr=%s\n", pSubmittedToZnode->addr.ToString(), pfrom->addr.ToString()); return; } @@ -408,12 +411,12 @@ void CDarksendPool::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataS int nMsgMessageID; vRecv >> nMsgSessionID >> nMsgMessageID; - if(nMsgMessageID < MSG_POOL_MIN || nMsgMessageID > MSG_POOL_MAX) { + if (nMsgMessageID < MSG_POOL_MIN || nMsgMessageID > MSG_POOL_MAX) { LogPrint("privatesend", "DSCOMPLETE -- nMsgMessageID is out of bounds: %d\n", nMsgMessageID); return; } - if(nSessionID != nMsgSessionID) { + if (nSessionID != nMsgSessionID) { LogPrint("privatesend", "DSCOMPLETE -- message doesn't match current PrivateSend session: nSessionID: %d nMsgSessionID: %d\n", darkSendPool.nSessionID, nMsgSessionID); return; } @@ -424,8 +427,7 @@ void CDarksendPool::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataS } } -void CDarksendPool::InitDenominations() -{ +void CDarksendPool::InitDenominations() { vecPrivateSendDenominations.clear(); /* Denominations @@ -439,17 +441,16 @@ void CDarksendPool::InitDenominations() /* Disabled vecPrivateSendDenominations.push_back( (100 * COIN)+100000 ); */ - vecPrivateSendDenominations.push_back( (10 * COIN)+10000 ); - vecPrivateSendDenominations.push_back( (1 * COIN)+1000 ); - vecPrivateSendDenominations.push_back( (.1 * COIN)+100 ); - vecPrivateSendDenominations.push_back( (.01 * COIN)+10 ); + vecPrivateSendDenominations.push_back((10 * COIN) + 10000); + vecPrivateSendDenominations.push_back((1 * COIN) + 1000); + vecPrivateSendDenominations.push_back((.1 * COIN) + 100); + vecPrivateSendDenominations.push_back((.01 * COIN) + 10); /* Disabled till we need them vecPrivateSendDenominations.push_back( (.001 * COIN)+1 ); */ } -void CDarksendPool::ResetPool() -{ +void CDarksendPool::ResetPool() { nCachedLastSuccessBlock = 0; txMyCollateral = CMutableTransaction(); vecZnodesUsed.clear(); @@ -457,8 +458,7 @@ void CDarksendPool::ResetPool() SetNull(); } -void CDarksendPool::SetNull() -{ +void CDarksendPool::SetNull() { // MN side vecSessionCollaterals.clear(); @@ -480,77 +480,85 @@ void CDarksendPool::SetNull() // // Unlock coins after mixing fails or succeeds // -void CDarksendPool::UnlockCoins() -{ - while(true) { +void CDarksendPool::UnlockCoins() { + while (true) { TRY_LOCK(pwalletMain->cs_wallet, lockWallet); - if(!lockWallet) {MilliSleep(50); continue;} - BOOST_FOREACH(COutPoint outpoint, vecOutPointLocked) - pwalletMain->UnlockCoin(outpoint); + if (!lockWallet) { + MilliSleep(50); + continue; + } + BOOST_FOREACH(COutPoint + outpoint, vecOutPointLocked) + pwalletMain->UnlockCoin(outpoint); break; } vecOutPointLocked.clear(); } -std::string CDarksendPool::GetStateString() const -{ - switch(nState) { - case POOL_STATE_IDLE: return "IDLE"; - case POOL_STATE_QUEUE: return "QUEUE"; - case POOL_STATE_ACCEPTING_ENTRIES: return "ACCEPTING_ENTRIES"; - case POOL_STATE_SIGNING: return "SIGNING"; - case POOL_STATE_ERROR: return "ERROR"; - case POOL_STATE_SUCCESS: return "SUCCESS"; - default: return "UNKNOWN"; +std::string CDarksendPool::GetStateString() const { + switch (nState) { + case POOL_STATE_IDLE: + return "IDLE"; + case POOL_STATE_QUEUE: + return "QUEUE"; + case POOL_STATE_ACCEPTING_ENTRIES: + return "ACCEPTING_ENTRIES"; + case POOL_STATE_SIGNING: + return "SIGNING"; + case POOL_STATE_ERROR: + return "ERROR"; + case POOL_STATE_SUCCESS: + return "SUCCESS"; + default: + return "UNKNOWN"; } } -std::string CDarksendPool::GetStatus() -{ +std::string CDarksendPool::GetStatus() { static int nStatusMessageProgress = 0; nStatusMessageProgress += 10; std::string strSuffix = ""; - if((pCurrentBlockIndex && pCurrentBlockIndex->nHeight - nCachedLastSuccessBlock < nMinBlockSpacing) || !znodeSync.IsBlockchainSynced()) + if ((pCurrentBlockIndex && pCurrentBlockIndex->nHeight - nCachedLastSuccessBlock < nMinBlockSpacing) || !znodeSync.IsBlockchainSynced()) return strAutoDenomResult; - switch(nState) { + switch (nState) { case POOL_STATE_IDLE: return _("PrivateSend is idle."); case POOL_STATE_QUEUE: - if( nStatusMessageProgress % 70 <= 30) strSuffix = "."; - else if(nStatusMessageProgress % 70 <= 50) strSuffix = ".."; - else if(nStatusMessageProgress % 70 <= 70) strSuffix = "..."; + if (nStatusMessageProgress % 70 <= 30) strSuffix = "."; + else if (nStatusMessageProgress % 70 <= 50) strSuffix = ".."; + else if (nStatusMessageProgress % 70 <= 70) strSuffix = "..."; return strprintf(_("Submitted to znode, waiting in queue %s"), strSuffix);; case POOL_STATE_ACCEPTING_ENTRIES: - if(nEntriesCount == 0) { + if (nEntriesCount == 0) { nStatusMessageProgress = 0; return strAutoDenomResult; - } else if(fLastEntryAccepted) { - if(nStatusMessageProgress % 10 > 8) { + } else if (fLastEntryAccepted) { + if (nStatusMessageProgress % 10 > 8) { fLastEntryAccepted = false; nStatusMessageProgress = 0; } return _("PrivateSend request complete:") + " " + _("Your transaction was accepted into the pool!"); } else { - if( nStatusMessageProgress % 70 <= 40) return strprintf(_("Submitted following entries to znode: %u / %d"), nEntriesCount, GetMaxPoolTransactions()); - else if(nStatusMessageProgress % 70 <= 50) strSuffix = "."; - else if(nStatusMessageProgress % 70 <= 60) strSuffix = ".."; - else if(nStatusMessageProgress % 70 <= 70) strSuffix = "..."; + if (nStatusMessageProgress % 70 <= 40) return strprintf(_("Submitted following entries to znode: %u / %d"), nEntriesCount, GetMaxPoolTransactions()); + else if (nStatusMessageProgress % 70 <= 50) strSuffix = "."; + else if (nStatusMessageProgress % 70 <= 60) strSuffix = ".."; + else if (nStatusMessageProgress % 70 <= 70) strSuffix = "..."; return strprintf(_("Submitted to znode, waiting for more entries ( %u / %d ) %s"), nEntriesCount, GetMaxPoolTransactions(), strSuffix); } case POOL_STATE_SIGNING: - if( nStatusMessageProgress % 70 <= 40) return _("Found enough users, signing ..."); - else if(nStatusMessageProgress % 70 <= 50) strSuffix = "."; - else if(nStatusMessageProgress % 70 <= 60) strSuffix = ".."; - else if(nStatusMessageProgress % 70 <= 70) strSuffix = "..."; + if (nStatusMessageProgress % 70 <= 40) return _("Found enough users, signing ..."); + else if (nStatusMessageProgress % 70 <= 50) strSuffix = "."; + else if (nStatusMessageProgress % 70 <= 60) strSuffix = ".."; + else if (nStatusMessageProgress % 70 <= 70) strSuffix = "..."; return strprintf(_("Found enough users, signing ( waiting %s )"), strSuffix); case POOL_STATE_ERROR: return _("PrivateSend request incomplete:") + " " + strLastMessage + " " + _("Will retry..."); case POOL_STATE_SUCCESS: return _("PrivateSend request complete:") + " " + strLastMessage; - default: + default: return strprintf(_("Unknown state: id = %u"), nState); } } @@ -558,20 +566,19 @@ std::string CDarksendPool::GetStatus() // // Check the mixing progress and send client updates if a Znode // -void CDarksendPool::CheckPool() -{ - if(fZNode) { +void CDarksendPool::CheckPool() { + if (fZNode) { LogPrint("privatesend", "CDarksendPool::CheckPool -- entries count %lu\n", GetEntriesCount()); // If entries are full, create finalized transaction - if(nState == POOL_STATE_ACCEPTING_ENTRIES && GetEntriesCount() >= GetMaxPoolTransactions()) { + if (nState == POOL_STATE_ACCEPTING_ENTRIES && GetEntriesCount() >= GetMaxPoolTransactions()) { LogPrint("privatesend", "CDarksendPool::CheckPool -- FINALIZE TRANSACTIONS\n"); CreateFinalTransaction(); return; } // If we have all of the signatures, try to compile the transaction - if(nState == POOL_STATE_SIGNING && IsSignaturesComplete()) { + if (nState == POOL_STATE_SIGNING && IsSignaturesComplete()) { LogPrint("privatesend", "CDarksendPool::CheckPool -- SIGNING\n"); CommitFinalTransaction(); return; @@ -579,26 +586,27 @@ void CDarksendPool::CheckPool() } // reset if we're here for 10 seconds - if((nState == POOL_STATE_ERROR || nState == POOL_STATE_SUCCESS) && GetTimeMillis() - nTimeLastSuccessfulStep >= 10000) { + if ((nState == POOL_STATE_ERROR || nState == POOL_STATE_SUCCESS) && GetTimeMillis() - nTimeLastSuccessfulStep >= 10000) { LogPrint("privatesend", "CDarksendPool::CheckPool -- timeout, RESETTING\n"); UnlockCoins(); SetNull(); } } -void CDarksendPool::CreateFinalTransaction() -{ +void CDarksendPool::CreateFinalTransaction() { LogPrint("privatesend", "CDarksendPool::CreateFinalTransaction -- FINALIZE TRANSACTIONS\n"); CMutableTransaction txNew; // make our new transaction - for(int i = 0; i < GetEntriesCount(); i++) { - BOOST_FOREACH(const CTxDSOut& txdsout, vecEntries[i].vecTxDSOut) - txNew.vout.push_back(txdsout); + for (int i = 0; i < GetEntriesCount(); i++) { + BOOST_FOREACH( + const CTxDSOut &txdsout, vecEntries[i].vecTxDSOut) + txNew.vout.push_back(txdsout); - BOOST_FOREACH(const CTxDSIn& txdsin, vecEntries[i].vecTxDSIn) - txNew.vin.push_back(txdsin); + BOOST_FOREACH( + const CTxDSIn &txdsin, vecEntries[i].vecTxDSIn) + txNew.vin.push_back(txdsin); } // BIP69 https://github.com/kristovatlas/bips/blob/master/bip-0069.mediawiki @@ -613,9 +621,8 @@ void CDarksendPool::CreateFinalTransaction() SetState(POOL_STATE_SIGNING); } -void CDarksendPool::CommitFinalTransaction() -{ - if(!fZNode) return; // check and relay final tx only on znode +void CDarksendPool::CommitFinalTransaction() { + if (!fZNode) return; // check and relay final tx only on znode CTransaction finalTransaction = CTransaction(finalMutableTransaction); uint256 hashTx = finalTransaction.GetHash(); @@ -626,9 +633,8 @@ void CDarksendPool::CommitFinalTransaction() // See if the transaction is valid TRY_LOCK(cs_main, lockMain); CValidationState validationState; - mempool.PrioritiseTransaction(hashTx, hashTx.ToString(), 1000, 0.1*COIN); - if(!lockMain || !AcceptToMemoryPool(mempool, validationState, finalTransaction, true, false, NULL, false, true, true)) - { + mempool.PrioritiseTransaction(hashTx, hashTx.ToString(), 1000, 0.1 * COIN); + if (!lockMain || !AcceptToMemoryPool(mempool, validationState, finalTransaction, true, false, NULL, false, true, true)) { LogPrintf("CDarksendPool::CommitFinalTransaction -- AcceptToMemoryPool() error: Transaction not valid\n"); SetNull(); // not much we can do in this case, just notify clients @@ -640,7 +646,7 @@ void CDarksendPool::CommitFinalTransaction() LogPrintf("CDarksendPool::CommitFinalTransaction -- CREATING DSTX\n"); // create and sign znode dstx transaction - if(!mapDarksendBroadcastTxes.count(hashTx)) { + if (!mapDarksendBroadcastTxes.count(hashTx)) { CDarksendBroadcastTx dstx(finalTransaction, activeZnode.vin, GetAdjustedTime()); dstx.Sign(); mapDarksendBroadcastTxes.insert(std::make_pair(hashTx, dstx)); @@ -674,35 +680,38 @@ void CDarksendPool::CommitFinalTransaction() // transaction for the client to be able to enter the pool. This transaction is kept by the Znode // until the transaction is either complete or fails. // -void CDarksendPool::ChargeFees() -{ - if(!fZNode) return; +void CDarksendPool::ChargeFees() { + if (!fZNode) return; //we don't need to charge collateral for every offence. - if(GetRandInt(100) > 33) return; + if (GetRandInt(100) > 33) return; - std::vector vecOffendersCollaterals; + std::vector vecOffendersCollaterals; - if(nState == POOL_STATE_ACCEPTING_ENTRIES) { - BOOST_FOREACH(const CTransaction& txCollateral, vecSessionCollaterals) { + if (nState == POOL_STATE_ACCEPTING_ENTRIES) { + BOOST_FOREACH( + const CTransaction &txCollateral, vecSessionCollaterals) { bool fFound = false; - BOOST_FOREACH(const CDarkSendEntry& entry, vecEntries) - if(entry.txCollateral == txCollateral) - fFound = true; + BOOST_FOREACH( + const CDarkSendEntry &entry, vecEntries) + if (entry.txCollateral == txCollateral) + fFound = true; // This queue entry didn't send us the promised transaction - if(!fFound) { + if (!fFound) { LogPrintf("CDarksendPool::ChargeFees -- found uncooperative node (didn't send transaction), found offence\n"); vecOffendersCollaterals.push_back(txCollateral); } } } - if(nState == POOL_STATE_SIGNING) { + if (nState == POOL_STATE_SIGNING) { // who didn't sign? - BOOST_FOREACH(const CDarkSendEntry entry, vecEntries) { - BOOST_FOREACH(const CTxDSIn txdsin, entry.vecTxDSIn) { - if(!txdsin.fHasSig) { + BOOST_FOREACH( + const CDarkSendEntry entry, vecEntries) { + BOOST_FOREACH( + const CTxDSIn txdsin, entry.vecTxDSIn) { + if (!txdsin.fHasSig) { LogPrintf("CDarksendPool::ChargeFees -- found uncooperative node (didn't sign), found offence\n"); vecOffendersCollaterals.push_back(entry.txCollateral); } @@ -711,26 +720,26 @@ void CDarksendPool::ChargeFees() } // no offences found - if(vecOffendersCollaterals.empty()) return; + if (vecOffendersCollaterals.empty()) return; //mostly offending? Charge sometimes - if((int)vecOffendersCollaterals.size() >= Params().PoolMaxTransactions() - 1 && GetRandInt(100) > 33) return; + if ((int) vecOffendersCollaterals.size() >= Params().PoolMaxTransactions() - 1 && GetRandInt(100) > 33) return; //everyone is an offender? That's not right - if((int)vecOffendersCollaterals.size() >= Params().PoolMaxTransactions()) return; + if ((int) vecOffendersCollaterals.size() >= Params().PoolMaxTransactions()) return; //charge one of the offenders randomly std::random_shuffle(vecOffendersCollaterals.begin(), vecOffendersCollaterals.end()); - if(nState == POOL_STATE_ACCEPTING_ENTRIES || nState == POOL_STATE_SIGNING) { + if (nState == POOL_STATE_ACCEPTING_ENTRIES || nState == POOL_STATE_SIGNING) { LogPrintf("CDarksendPool::ChargeFees -- found uncooperative node (didn't %s transaction), charging fees: %s\n", - (nState == POOL_STATE_SIGNING) ? "sign" : "send", vecOffendersCollaterals[0].ToString()); + (nState == POOL_STATE_SIGNING) ? "sign" : "send", vecOffendersCollaterals[0].ToString()); LOCK(cs_main); CValidationState state; bool fMissingInputs; - if(!AcceptToMemoryPool(mempool, state, vecOffendersCollaterals[0],true, false, &fMissingInputs, false, true)) { + if (!AcceptToMemoryPool(mempool, state, vecOffendersCollaterals[0], true, false, &fMissingInputs, false, true)) { // should never really happen LogPrintf("CDarksendPool::ChargeFees -- ERROR: AcceptToMemoryPool failed!\n"); } else { @@ -751,21 +760,21 @@ void CDarksendPool::ChargeFees() stop these kinds of attacks 1 in 10 successful transactions are charged. This adds up to a cost of 0.001DRK per transaction on average. */ -void CDarksendPool::ChargeRandomFees() -{ - if(!fZNode) return; +void CDarksendPool::ChargeRandomFees() { + if (!fZNode) return; LOCK(cs_main); - BOOST_FOREACH(const CTransaction& txCollateral, vecSessionCollaterals) { + BOOST_FOREACH( + const CTransaction &txCollateral, vecSessionCollaterals) { - if(GetRandInt(100) > 10) return; + if (GetRandInt(100) > 10) return; LogPrintf("CDarksendPool::ChargeRandomFees -- charging random fees, txCollateral=%s", txCollateral.ToString()); CValidationState state; bool fMissingInputs; - if(!AcceptToMemoryPool(mempool, state, txCollateral,true, false, &fMissingInputs, false, true)) { + if (!AcceptToMemoryPool(mempool, state, txCollateral, true, false, &fMissingInputs, false, true)) { // should never really happen LogPrintf("CDarksendPool::ChargeRandomFees -- ERROR: AcceptToMemoryPool failed!\n"); } else { @@ -777,27 +786,26 @@ void CDarksendPool::ChargeRandomFees() // // Check for various timeouts (queue objects, mixing, etc) // -void CDarksendPool::CheckTimeout() -{ +void CDarksendPool::CheckTimeout() { { TRY_LOCK(cs_darksend, lockDS); - if(!lockDS) return; // it's ok to fail here, we run this quite frequently + if (!lockDS) return; // it's ok to fail here, we run this quite frequently // check mixing queue objects for timeouts std::vector::iterator it = vecDarksendQueue.begin(); - while(it != vecDarksendQueue.end()) { - if((*it).IsExpired()) { + while (it != vecDarksendQueue.end()) { + if ((*it).IsExpired()) { LogPrint("privatesend", "CDarksendPool::CheckTimeout -- Removing expired queue (%s)\n", (*it).ToString()); it = vecDarksendQueue.erase(it); } else ++it; } } - if(!fEnablePrivateSend && !fZNode) return; + if (!fEnablePrivateSend && !fZNode) return; // catching hanging sessions - if(!fZNode) { - switch(nState) { + if (!fZNode) { + switch (nState) { case POOL_STATE_ERROR: LogPrint("privatesend", "CDarksendPool::CheckTimeout -- Pool error -- Running CheckPool\n"); CheckPool(); @@ -813,11 +821,11 @@ void CDarksendPool::CheckTimeout() int nLagTime = fZNode ? 0 : 10000; // if we're the client, give the server a few extra seconds before resetting. int nTimeout = (nState == POOL_STATE_SIGNING) ? PRIVATESEND_SIGNING_TIMEOUT : PRIVATESEND_QUEUE_TIMEOUT; - bool fTimeout = GetTimeMillis() - nTimeLastSuccessfulStep >= nTimeout*1000 + nLagTime; + bool fTimeout = GetTimeMillis() - nTimeLastSuccessfulStep >= nTimeout * 1000 + nLagTime; - if(nState != POOL_STATE_IDLE && fTimeout) { + if (nState != POOL_STATE_IDLE && fTimeout) { LogPrint("privatesend", "CDarksendPool::CheckTimeout -- %s timed out (%ds) -- restting\n", - (nState == POOL_STATE_SIGNING) ? "Signing" : "Session", nTimeout); + (nState == POOL_STATE_SIGNING) ? "Signing" : "Session", nTimeout); ChargeFees(); UnlockCoins(); SetNull(); @@ -831,11 +839,10 @@ void CDarksendPool::CheckTimeout() After receiving multiple dsa messages, the queue will switch to "accepting entries" which is the active state right before merging the transaction */ -void CDarksendPool::CheckForCompleteQueue() -{ - if(!fEnablePrivateSend && !fZNode) return; +void CDarksendPool::CheckForCompleteQueue() { + if (!fEnablePrivateSend && !fZNode) return; - if(nState == POOL_STATE_QUEUE && IsSessionReady()) { + if (nState == POOL_STATE_QUEUE && IsSessionReady()) { SetState(POOL_STATE_ACCEPTING_ENTRIES); CDarksendQueue dsq(nSessionDenom, activeZnode.vin, GetTime(), true); @@ -846,8 +853,7 @@ void CDarksendPool::CheckForCompleteQueue() } // Check to make sure a given input matches an input in the pool and its scriptSig is valid -bool CDarksendPool::IsInputScriptSigValid(const CTxIn& txin) -{ +bool CDarksendPool::IsInputScriptSigValid(const CTxIn &txin) { CMutableTransaction txNew; txNew.vin.clear(); txNew.vout.clear(); @@ -856,15 +862,18 @@ bool CDarksendPool::IsInputScriptSigValid(const CTxIn& txin) int nTxInIndex = -1; CScript sigPubKey = CScript(); - BOOST_FOREACH(CDarkSendEntry& entry, vecEntries) { + BOOST_FOREACH(CDarkSendEntry & entry, vecEntries) + { - BOOST_FOREACH(const CTxDSOut& txdsout, entry.vecTxDSOut) - txNew.vout.push_back(txdsout); + BOOST_FOREACH( + const CTxDSOut &txdsout, entry.vecTxDSOut) + txNew.vout.push_back(txdsout); - BOOST_FOREACH(const CTxDSIn& txdsin, entry.vecTxDSIn) { + BOOST_FOREACH( + const CTxDSIn &txdsin, entry.vecTxDSIn) { txNew.vin.push_back(txdsin); - if(txdsin.prevout == txin.prevout) { + if (txdsin.prevout == txin.prevout) { nTxInIndex = i; sigPubKey = txdsin.prevPubKey; } @@ -872,10 +881,10 @@ bool CDarksendPool::IsInputScriptSigValid(const CTxIn& txin) } } - if(nTxInIndex >= 0) { //might have to do this one input at a time? + if (nTxInIndex >= 0) { //might have to do this one input at a time? txNew.vin[nTxInIndex].scriptSig = txin.scriptSig; - const CAmount& amount = txNew.vout[nTxInIndex].nValue; - LogPrint("privatesend", "CDarksendPool::IsInputScriptSigValid -- verifying scriptSig %s\n", ScriptToAsmStr(txin.scriptSig).substr(0,24)); + const CAmount &amount = txNew.vout[nTxInIndex].nValue; + LogPrint("privatesend", "CDarksendPool::IsInputScriptSigValid -- verifying scriptSig %s\n", ScriptToAsmStr(txin.scriptSig).substr(0, 24)); // if(!VerifyScript(txNew.vin[nTxInIndex].scriptSig, sigPubKey, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, MutableTransactionSignatureChecker(&txNew, nTxInIndex, amount))) { // LogPrint("privatesend", "CDarksendPool::IsInputScriptSigValid -- VerifyScript() failed on input %d\n", nTxInIndex); // return false; @@ -890,42 +899,43 @@ bool CDarksendPool::IsInputScriptSigValid(const CTxIn& txin) } // check to make sure the collateral provided by the client is valid -bool CDarksendPool::IsCollateralValid(const CTransaction& txCollateral) -{ - if(txCollateral.vout.empty()) return false; - if(txCollateral.nLockTime != 0) return false; +bool CDarksendPool::IsCollateralValid(const CTransaction &txCollateral) { + if (txCollateral.vout.empty()) return false; + if (txCollateral.nLockTime != 0) return false; CAmount nValueIn = 0; CAmount nValueOut = 0; bool fMissingTx = false; - BOOST_FOREACH(const CTxOut txout, txCollateral.vout) { + BOOST_FOREACH( + const CTxOut txout, txCollateral.vout) { nValueOut += txout.nValue; - if(!txout.scriptPubKey.IsNormalPaymentScript()) { + if (!txout.scriptPubKey.IsNormalPaymentScript()) { LogPrintf ("CDarksendPool::IsCollateralValid -- Invalid Script, txCollateral=%s", txCollateral.ToString()); return false; } } - BOOST_FOREACH(const CTxIn txin, txCollateral.vin) { + BOOST_FOREACH( + const CTxIn txin, txCollateral.vin) { CTransaction txPrev; uint256 hash; - if(GetTransaction(txin.prevout.hash, txPrev, Params().GetConsensus(), hash, true)) { - if(txPrev.vout.size() > txin.prevout.n) + if (GetTransaction(txin.prevout.hash, txPrev, Params().GetConsensus(), hash, true)) { + if (txPrev.vout.size() > txin.prevout.n) nValueIn += txPrev.vout[txin.prevout.n].nValue; } else { fMissingTx = true; } } - if(fMissingTx) { + if (fMissingTx) { LogPrint("privatesend", "CDarksendPool::IsCollateralValid -- Unknown inputs in collateral transaction, txCollateral=%s", txCollateral.ToString()); return false; } //collateral transactions are required to pay out PRIVATESEND_COLLATERAL as a fee to the miners - if(nValueIn - nValueOut < PRIVATESEND_COLLATERAL) { + if (nValueIn - nValueOut < PRIVATESEND_COLLATERAL) { LogPrint("privatesend", "CDarksendPool::IsCollateralValid -- did not include enough fees in transaction: fees: %d, txCollateral=%s", nValueOut - nValueIn, txCollateral.ToString()); return false; } @@ -935,7 +945,7 @@ bool CDarksendPool::IsCollateralValid(const CTransaction& txCollateral) { LOCK(cs_main); CValidationState validationState; - if(!AcceptToMemoryPool(mempool, validationState, txCollateral, true, false, NULL, false, true, true)) { + if (!AcceptToMemoryPool(mempool, validationState, txCollateral, true, false, NULL, false, true, true)) { LogPrint("privatesend", "CDarksendPool::IsCollateralValid -- didn't pass AcceptToMemoryPool()\n"); return false; } @@ -948,35 +958,38 @@ bool CDarksendPool::IsCollateralValid(const CTransaction& txCollateral) // // Add a clients transaction to the pool // -bool CDarksendPool::AddEntry(const CDarkSendEntry& entryNew, PoolMessage& nMessageIDRet) -{ - if(!fZNode) return false; +bool CDarksendPool::AddEntry(const CDarkSendEntry &entryNew, PoolMessage &nMessageIDRet) { + if (!fZNode) return false; - BOOST_FOREACH(CTxIn txin, entryNew.vecTxDSIn) { - if(txin.prevout.IsNull()) { + BOOST_FOREACH(CTxIn + txin, entryNew.vecTxDSIn) { + if (txin.prevout.IsNull()) { LogPrint("privatesend", "CDarksendPool::AddEntry -- input not valid!\n"); nMessageIDRet = ERR_INVALID_INPUT; return false; } } - if(!IsCollateralValid(entryNew.txCollateral)) { + if (!IsCollateralValid(entryNew.txCollateral)) { LogPrint("privatesend", "CDarksendPool::AddEntry -- collateral not valid!\n"); nMessageIDRet = ERR_INVALID_COLLATERAL; return false; } - if(GetEntriesCount() >= GetMaxPoolTransactions()) { + if (GetEntriesCount() >= GetMaxPoolTransactions()) { LogPrint("privatesend", "CDarksendPool::AddEntry -- entries is full!\n"); nMessageIDRet = ERR_ENTRIES_FULL; return false; } - BOOST_FOREACH(CTxIn txin, entryNew.vecTxDSIn) { + BOOST_FOREACH(CTxIn + txin, entryNew.vecTxDSIn) { LogPrint("privatesend", "looking for txin -- %s\n", txin.ToString()); - BOOST_FOREACH(const CDarkSendEntry& entry, vecEntries) { - BOOST_FOREACH(const CTxDSIn& txdsin, entry.vecTxDSIn) { - if(txdsin.prevout == txin.prevout) { + BOOST_FOREACH( + const CDarkSendEntry &entry, vecEntries) { + BOOST_FOREACH( + const CTxDSIn &txdsin, entry.vecTxDSIn) { + if (txdsin.prevout == txin.prevout) { LogPrint("privatesend", "CDarksendPool::AddEntry -- found in txin\n"); nMessageIDRet = ERR_ALREADY_HAVE; return false; @@ -994,50 +1007,53 @@ bool CDarksendPool::AddEntry(const CDarkSendEntry& entryNew, PoolMessage& nMessa return true; } -bool CDarksendPool::AddScriptSig(const CTxIn& txinNew) -{ - LogPrint("privatesend", "CDarksendPool::AddScriptSig -- scriptSig=%s\n", ScriptToAsmStr(txinNew.scriptSig).substr(0,24)); +bool CDarksendPool::AddScriptSig(const CTxIn &txinNew) { + LogPrint("privatesend", "CDarksendPool::AddScriptSig -- scriptSig=%s\n", ScriptToAsmStr(txinNew.scriptSig).substr(0, 24)); - BOOST_FOREACH(const CDarkSendEntry& entry, vecEntries) { - BOOST_FOREACH(const CTxDSIn& txdsin, entry.vecTxDSIn) { - if(txdsin.scriptSig == txinNew.scriptSig) { + BOOST_FOREACH( + const CDarkSendEntry &entry, vecEntries) { + BOOST_FOREACH( + const CTxDSIn &txdsin, entry.vecTxDSIn) { + if (txdsin.scriptSig == txinNew.scriptSig) { LogPrint("privatesend", "CDarksendPool::AddScriptSig -- already exists\n"); return false; } } } - if(!IsInputScriptSigValid(txinNew)) { + if (!IsInputScriptSigValid(txinNew)) { LogPrint("privatesend", "CDarksendPool::AddScriptSig -- Invalid scriptSig\n"); return false; } - LogPrint("privatesend", "CDarksendPool::AddScriptSig -- scriptSig=%s new\n", ScriptToAsmStr(txinNew.scriptSig).substr(0,24)); + LogPrint("privatesend", "CDarksendPool::AddScriptSig -- scriptSig=%s new\n", ScriptToAsmStr(txinNew.scriptSig).substr(0, 24)); - BOOST_FOREACH(CTxIn& txin, finalMutableTransaction.vin) { - if(txinNew.prevout == txin.prevout && txin.nSequence == txinNew.nSequence) { + BOOST_FOREACH(CTxIn & txin, finalMutableTransaction.vin) + { + if (txinNew.prevout == txin.prevout && txin.nSequence == txinNew.nSequence) { txin.scriptSig = txinNew.scriptSig; txin.prevPubKey = txinNew.prevPubKey; - LogPrint("privatesend", "CDarksendPool::AddScriptSig -- adding to finalMutableTransaction, scriptSig=%s\n", ScriptToAsmStr(txinNew.scriptSig).substr(0,24)); + LogPrint("privatesend", "CDarksendPool::AddScriptSig -- adding to finalMutableTransaction, scriptSig=%s\n", ScriptToAsmStr(txinNew.scriptSig).substr(0, 24)); } } - for(int i = 0; i < GetEntriesCount(); i++) { - if(vecEntries[i].AddScriptSig(txinNew)) { - LogPrint("privatesend", "CDarksendPool::AddScriptSig -- adding to entries, scriptSig=%s\n", ScriptToAsmStr(txinNew.scriptSig).substr(0,24)); + for (int i = 0; i < GetEntriesCount(); i++) { + if (vecEntries[i].AddScriptSig(txinNew)) { + LogPrint("privatesend", "CDarksendPool::AddScriptSig -- adding to entries, scriptSig=%s\n", ScriptToAsmStr(txinNew.scriptSig).substr(0, 24)); return true; } } - LogPrintf("CDarksendPool::AddScriptSig -- Couldn't set sig!\n" ); + LogPrintf("CDarksendPool::AddScriptSig -- Couldn't set sig!\n"); return false; } // Check to make sure everything is signed -bool CDarksendPool::IsSignaturesComplete() -{ - BOOST_FOREACH(const CDarkSendEntry& entry, vecEntries) - BOOST_FOREACH(const CTxDSIn& txdsin, entry.vecTxDSIn) - if(!txdsin.fHasSig) return false; +bool CDarksendPool::IsSignaturesComplete() { + BOOST_FOREACH( + const CDarkSendEntry &entry, vecEntries) + BOOST_FOREACH( + const CTxDSIn &txdsin, entry.vecTxDSIn) + if (!txdsin.fHasSig) return false; return true; } @@ -1046,34 +1062,35 @@ bool CDarksendPool::IsSignaturesComplete() // Execute a mixing denomination via a Znode. // This is only ran from clients // -bool CDarksendPool::SendDenominate(const std::vector& vecTxIn, const std::vector& vecTxOut) -{ - if(fZNode) { +bool CDarksendPool::SendDenominate(const std::vector &vecTxIn, const std::vector &vecTxOut) { + if (fZNode) { LogPrintf("CDarksendPool::SendDenominate -- PrivateSend from a Znode is not supported currently.\n"); return false; } - if(txMyCollateral == CMutableTransaction()) { + if (txMyCollateral == CMutableTransaction()) { LogPrintf("CDarksendPool:SendDenominate -- PrivateSend collateral not set\n"); return false; } // lock the funds we're going to use - BOOST_FOREACH(CTxIn txin, txMyCollateral.vin) - vecOutPointLocked.push_back(txin.prevout); + BOOST_FOREACH(CTxIn + txin, txMyCollateral.vin) + vecOutPointLocked.push_back(txin.prevout); - BOOST_FOREACH(CTxIn txin, vecTxIn) - vecOutPointLocked.push_back(txin.prevout); + BOOST_FOREACH(CTxIn + txin, vecTxIn) + vecOutPointLocked.push_back(txin.prevout); // we should already be connected to a Znode - if(!nSessionID) { + if (!nSessionID) { LogPrintf("CDarksendPool::SendDenominate -- No Znode has been selected yet.\n"); UnlockCoins(); SetNull(); return false; } - if(!CheckDiskSpace()) { + if (!CheckDiskSpace()) { UnlockCoins(); SetNull(); fEnablePrivateSend = false; @@ -1091,21 +1108,23 @@ bool CDarksendPool::SendDenominate(const std::vector& vecTxIn, const std: CValidationState validationState; CMutableTransaction tx; - BOOST_FOREACH(const CTxIn& txin, vecTxIn) { + BOOST_FOREACH( + const CTxIn &txin, vecTxIn) { LogPrint("privatesend", "CDarksendPool::SendDenominate -- txin=%s\n", txin.ToString()); tx.vin.push_back(txin); } - BOOST_FOREACH(const CTxOut& txout, vecTxOut) { + BOOST_FOREACH( + const CTxOut &txout, vecTxOut) { LogPrint("privatesend", "CDarksendPool::SendDenominate -- txout=%s\n", txout.ToString()); tx.vout.push_back(txout); } LogPrintf("CDarksendPool::SendDenominate -- Submitting partial tx %s", tx.ToString()); - mempool.PrioritiseTransaction(tx.GetHash(), tx.GetHash().ToString(), 1000, 0.1*COIN); + mempool.PrioritiseTransaction(tx.GetHash(), tx.GetHash().ToString(), 1000, 0.1 * COIN); TRY_LOCK(cs_main, lockMain); - if(!lockMain || !AcceptToMemoryPool(mempool, validationState, CTransaction(tx), true, false, NULL, false, true, true)) { + if (!lockMain || !AcceptToMemoryPool(mempool, validationState, CTransaction(tx), true, false, NULL, false, true, true)) { LogPrintf("CDarksendPool::SendDenominate -- AcceptToMemoryPool() failed! tx=%s", tx.ToString()); UnlockCoins(); SetNull(); @@ -1123,17 +1142,16 @@ bool CDarksendPool::SendDenominate(const std::vector& vecTxIn, const std: } // Incoming message from Znode updating the progress of mixing -bool CDarksendPool::CheckPoolStateUpdate(PoolState nStateNew, int nEntriesCountNew, PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID, int nSessionIDNew) -{ - if(fZNode) return false; +bool CDarksendPool::CheckPoolStateUpdate(PoolState nStateNew, int nEntriesCountNew, PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID, int nSessionIDNew) { + if (fZNode) return false; // do not update state when mixing client state is one of these - if(nState == POOL_STATE_IDLE || nState == POOL_STATE_ERROR || nState == POOL_STATE_SUCCESS) return false; + if (nState == POOL_STATE_IDLE || nState == POOL_STATE_ERROR || nState == POOL_STATE_SUCCESS) return false; strAutoDenomResult = _("Znode:") + " " + GetMessageByID(nMessageID); // if rejected at any state - if(nStatusUpdate == STATUS_REJECTED) { + if (nStatusUpdate == STATUS_REJECTED) { LogPrintf("CDarksendPool::CheckPoolStateUpdate -- entry is rejected by Znode\n"); UnlockCoins(); SetNull(); @@ -1142,15 +1160,14 @@ bool CDarksendPool::CheckPoolStateUpdate(PoolState nStateNew, int nEntriesCountN return true; } - if(nStatusUpdate == STATUS_ACCEPTED && nState == nStateNew) { - if(nStateNew == POOL_STATE_QUEUE && nSessionID == 0 && nSessionIDNew != 0) { + if (nStatusUpdate == STATUS_ACCEPTED && nState == nStateNew) { + if (nStateNew == POOL_STATE_QUEUE && nSessionID == 0 && nSessionIDNew != 0) { // new session id should be set only in POOL_STATE_QUEUE state nSessionID = nSessionIDNew; nTimeLastSuccessfulStep = GetTimeMillis(); LogPrintf("CDarksendPool::CheckPoolStateUpdate -- set nSessionID to %d\n", nSessionID); return true; - } - else if(nStateNew == POOL_STATE_ACCEPTING_ENTRIES && nEntriesCount != nEntriesCountNew) { + } else if (nStateNew == POOL_STATE_ACCEPTING_ENTRIES && nEntriesCount != nEntriesCountNew) { nEntriesCount = nEntriesCountNew; nTimeLastSuccessfulStep = GetTimeMillis(); fLastEntryAccepted = true; @@ -1168,50 +1185,53 @@ bool CDarksendPool::CheckPoolStateUpdate(PoolState nStateNew, int nEntriesCountN // 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(const CTransaction& finalTransactionNew, CNode* pnode) -{ - if(fZNode || pnode == NULL) return false; +bool CDarksendPool::SignFinalTransaction(const CTransaction &finalTransactionNew, CNode *pnode) { + if (fZNode || pnode == NULL) return false; finalMutableTransaction = finalTransactionNew; LogPrintf("CDarksendPool::SignFinalTransaction -- finalMutableTransaction=%s", finalMutableTransaction.ToString()); - std::vector sigs; + std::vector sigs; //make sure my inputs/outputs are present, otherwise refuse to sign - BOOST_FOREACH(const CDarkSendEntry entry, vecEntries) { - BOOST_FOREACH(const CTxDSIn txdsin, entry.vecTxDSIn) { + BOOST_FOREACH( + const CDarkSendEntry entry, vecEntries) { + BOOST_FOREACH( + const CTxDSIn txdsin, entry.vecTxDSIn) { /* Sign my transaction and all outputs */ int nMyInputIndex = -1; CScript prevPubKey = CScript(); CTxIn txin = CTxIn(); - for(unsigned int i = 0; i < finalMutableTransaction.vin.size(); i++) { - if(finalMutableTransaction.vin[i] == txdsin) { + for (unsigned int i = 0; i < finalMutableTransaction.vin.size(); i++) { + if (finalMutableTransaction.vin[i] == txdsin) { nMyInputIndex = i; prevPubKey = txdsin.prevPubKey; txin = txdsin; } } - if(nMyInputIndex >= 0) { //might have to do this one input at a time? + if (nMyInputIndex >= 0) { //might have to do this one input at a time? int nFoundOutputsCount = 0; CAmount nValue1 = 0; CAmount nValue2 = 0; - for(unsigned int i = 0; i < finalMutableTransaction.vout.size(); i++) { - BOOST_FOREACH(const CTxOut& txout, entry.vecTxDSOut) { - if(finalMutableTransaction.vout[i] == txout) { + for (unsigned int i = 0; i < finalMutableTransaction.vout.size(); i++) { + BOOST_FOREACH( + const CTxOut &txout, entry.vecTxDSOut) { + if (finalMutableTransaction.vout[i] == txout) { nFoundOutputsCount++; nValue1 += finalMutableTransaction.vout[i].nValue; } } } - BOOST_FOREACH(const CTxOut txout, entry.vecTxDSOut) - nValue2 += txout.nValue; + BOOST_FOREACH( + const CTxOut txout, entry.vecTxDSOut) + nValue2 += txout.nValue; int nTargetOuputsCount = entry.vecTxDSOut.size(); - if(nFoundOutputsCount < nTargetOuputsCount || nValue1 != nValue2) { + if (nFoundOutputsCount < nTargetOuputsCount || 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::SignFinalTransaction -- My entries are not correct! Refusing to sign: nFoundOutputsCount: %d, nTargetOuputsCount: %d\n", nFoundOutputsCount, nTargetOuputsCount); @@ -1221,21 +1241,21 @@ bool CDarksendPool::SignFinalTransaction(const CTransaction& finalTransactionNew return false; } - const CKeyStore& keystore = *pwalletMain; + const CKeyStore &keystore = *pwalletMain; LogPrint("privatesend", "CDarksendPool::SignFinalTransaction -- Signing my input %i\n", nMyInputIndex); - if(!SignSignature(keystore, prevPubKey, finalMutableTransaction, nMyInputIndex, NULL, int(SIGHASH_ALL|SIGHASH_ANYONECANPAY))) { // changes scriptSig + if (!SignSignature(keystore, prevPubKey, finalMutableTransaction, nMyInputIndex, NULL, int(SIGHASH_ALL | SIGHASH_ANYONECANPAY))) { // changes scriptSig LogPrint("privatesend", "CDarksendPool::SignFinalTransaction -- Unable to sign my own transaction!\n"); // not sure what to do here, it will timeout...? } sigs.push_back(finalMutableTransaction.vin[nMyInputIndex]); - LogPrint("privatesend", "CDarksendPool::SignFinalTransaction -- nMyInputIndex: %d, sigs.size(): %d, scriptSig=%s\n", nMyInputIndex, (int)sigs.size(), ScriptToAsmStr(finalMutableTransaction.vin[nMyInputIndex].scriptSig)); + LogPrint("privatesend", "CDarksendPool::SignFinalTransaction -- nMyInputIndex: %d, sigs.size(): %d, scriptSig=%s\n", nMyInputIndex, (int) sigs.size(), ScriptToAsmStr(finalMutableTransaction.vin[nMyInputIndex].scriptSig)); } } } - if(sigs.empty()) { + if (sigs.empty()) { LogPrintf("CDarksendPool::SignFinalTransaction -- can't sign anything!\n"); UnlockCoins(); SetNull(); @@ -1252,12 +1272,11 @@ bool CDarksendPool::SignFinalTransaction(const CTransaction& finalTransactionNew return true; } -void CDarksendPool::NewBlock() -{ +void CDarksendPool::NewBlock() { static int64_t nTimeNewBlockReceived = 0; //we we're processing lots of blocks, we'll just leave - if(GetTime() - nTimeNewBlockReceived < 10) return; + if (GetTime() - nTimeNewBlockReceived < 10) return; nTimeNewBlockReceived = GetTime(); LogPrint("privatesend", "CDarksendPool::NewBlock\n"); @@ -1265,11 +1284,10 @@ void CDarksendPool::NewBlock() } // mixing transaction was completed (failed or successful) -void CDarksendPool::CompletedTransaction(PoolMessage nMessageID) -{ - if(fZNode) return; +void CDarksendPool::CompletedTransaction(PoolMessage nMessageID) { + if (fZNode) return; - if(nMessageID == MSG_SUCCESS) { + if (nMessageID == MSG_SUCCESS) { LogPrintf("CompletedTransaction -- success\n"); nCachedLastSuccessBlock = pCurrentBlockIndex->nHeight; } else { @@ -1283,18 +1301,17 @@ void CDarksendPool::CompletedTransaction(PoolMessage nMessageID) // // Passively run mixing in the background to anonymize funds based on the given configuration. // -bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) -{ - if(!fEnablePrivateSend || fZNode || !pCurrentBlockIndex) return false; - if(!pwalletMain || pwalletMain->IsLocked(true)) return false; - if(nState != POOL_STATE_IDLE) return false; +bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) { + if (!fEnablePrivateSend || fZNode || !pCurrentBlockIndex) return false; + if (!pwalletMain || pwalletMain->IsLocked(true)) return false; + if (nState != POOL_STATE_IDLE) return false; - if(!znodeSync.IsZnodeListSynced()) { + if (!znodeSync.IsZnodeListSynced()) { strAutoDenomResult = _("Can't mix while sync in progress."); return false; } - switch(nWalletBackups) { + switch (nWalletBackups) { case 0: LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating -- Automatic backups disabled, no mixing available.\n"); strAutoDenomResult = _("Automatic backups disabled") + ", " + _("no mixing available."); @@ -1317,29 +1334,29 @@ bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) return false; } - if(pwalletMain->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_STOP) { + if (pwalletMain->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_STOP) { // We should never get here via mixing itself but probably smth else is still actively using keypool LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating -- Very low number of keys left: %d, no mixing available.\n", pwalletMain->nKeysLeftSinceAutoBackup); strAutoDenomResult = strprintf(_("Very low number of keys left: %d") + ", " + _("no mixing available."), pwalletMain->nKeysLeftSinceAutoBackup); // It's getting really dangerous, stop mixing fEnablePrivateSend = false; return false; - } else if(pwalletMain->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_WARNING) { + } else if (pwalletMain->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_WARNING) { // Low number of keys left but it's still more or less safe to continue LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating -- Very low number of keys left: %d\n", pwalletMain->nKeysLeftSinceAutoBackup); strAutoDenomResult = strprintf(_("Very low number of keys left: %d"), pwalletMain->nKeysLeftSinceAutoBackup); - if(fCreateAutoBackups) { + if (fCreateAutoBackups) { LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating -- Trying to create new backup.\n"); std::string warningString; std::string errorString; - if(!AutoBackupWallet(pwalletMain, "", warningString, errorString)) { - if(!warningString.empty()) { + if (!AutoBackupWallet(pwalletMain, "", warningString, errorString)) { + if (!warningString.empty()) { // There were some issues saving backup but yet more or less safe to continue LogPrintf("CDarksendPool::DoAutomaticDenominating -- WARNING! Something went wrong on automatic backup: %s\n", warningString); } - if(!errorString.empty()) { + if (!errorString.empty()) { // Things are really broken LogPrintf("CDarksendPool::DoAutomaticDenominating -- ERROR! Failed to create automatic backup: %s\n", errorString); strAutoDenomResult = strprintf(_("ERROR! Failed to create automatic backup") + ": %s", errorString); @@ -1354,29 +1371,29 @@ bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating -- Keys left since latest backup: %d\n", pwalletMain->nKeysLeftSinceAutoBackup); - if(GetEntriesCount() > 0) { + if (GetEntriesCount() > 0) { strAutoDenomResult = _("Mixing in progress..."); return false; } TRY_LOCK(cs_darksend, lockDS); - if(!lockDS) { + if (!lockDS) { strAutoDenomResult = _("Lock is already in place."); return false; } - if(!fDryRun && pwalletMain->IsLocked(true)) { + if (!fDryRun && pwalletMain->IsLocked(true)) { strAutoDenomResult = _("Wallet is locked."); return false; } - if(!fPrivateSendMultiSession && pCurrentBlockIndex->nHeight - nCachedLastSuccessBlock < nMinBlockSpacing) { + if (!fPrivateSendMultiSession && pCurrentBlockIndex->nHeight - nCachedLastSuccessBlock < nMinBlockSpacing) { LogPrintf("CDarksendPool::DoAutomaticDenominating -- Last successful PrivateSend action was too recent\n"); strAutoDenomResult = _("Last successful PrivateSend action was too recent."); return false; } - if(mnodeman.size() == 0) { + if (mnodeman.size() == 0) { LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating -- No Znodes detected\n"); strAutoDenomResult = _("No Znodes detected."); return false; @@ -1385,16 +1402,16 @@ bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) CAmount nValueMin = vecPrivateSendDenominations.back(); // if there are no confirmed DS collateral inputs yet - if(!pwalletMain->HasCollateralInputs()) { + if (!pwalletMain->HasCollateralInputs()) { // should have some additional amount for them - nValueMin += PRIVATESEND_COLLATERAL*4; + nValueMin += PRIVATESEND_COLLATERAL * 4; } // including denoms but applying some restrictions CAmount nBalanceNeedsAnonymized = pwalletMain->GetNeedsToBeAnonymizedBalance(nValueMin); // anonymizable balance is way too small - if(nBalanceNeedsAnonymized < nValueMin) { + if (nBalanceNeedsAnonymized < nValueMin) { LogPrintf("CDarksendPool::DoAutomaticDenominating -- Not enough funds to anonymize\n"); strAutoDenomResult = _("Not enough funds to anonymize."); return false; @@ -1408,26 +1425,26 @@ bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) CAmount nBalanceDenominated = nBalanceDenominatedConf + nBalanceDenominatedUnconf; LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating -- nValueMin: %f, nBalanceNeedsAnonymized: %f, nBalanceAnonimizableNonDenom: %f, nBalanceDenominatedConf: %f, nBalanceDenominatedUnconf: %f, nBalanceDenominated: %f\n", - (float)nValueMin/COIN, - (float)nBalanceNeedsAnonymized/COIN, - (float)nBalanceAnonimizableNonDenom/COIN, - (float)nBalanceDenominatedConf/COIN, - (float)nBalanceDenominatedUnconf/COIN, - (float)nBalanceDenominated/COIN); + (float) nValueMin / COIN, + (float) nBalanceNeedsAnonymized / COIN, + (float) nBalanceAnonimizableNonDenom / COIN, + (float) nBalanceDenominatedConf / COIN, + (float) nBalanceDenominatedUnconf / COIN, + (float) nBalanceDenominated / COIN); - if(fDryRun) return true; + if (fDryRun) return true; // Check if we have should create more denominated inputs i.e. // there are funds to denominate and denominated balance does not exceed // max amount to mix yet. - if(nBalanceAnonimizableNonDenom >= nValueMin + PRIVATESEND_COLLATERAL && nBalanceDenominated < nPrivateSendAmount*COIN) + if (nBalanceAnonimizableNonDenom >= nValueMin + PRIVATESEND_COLLATERAL && nBalanceDenominated < nPrivateSendAmount * COIN) return CreateDenominated(); //check if we have the collateral sized inputs - if(!pwalletMain->HasCollateralInputs()) + if (!pwalletMain->HasCollateralInputs()) return !pwalletMain->HasCollateralInputs(false) && MakeCollateralAmounts(); - if(nSessionID) { + if (nSessionID) { strAutoDenomResult = _("Mixing in progress..."); return false; } @@ -1438,7 +1455,7 @@ bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) SetNull(); // should be no unconfirmed denoms in non-multi-session mode - if(!fPrivateSendMultiSession && nBalanceDenominatedUnconf > 0) { + if (!fPrivateSendMultiSession && nBalanceDenominatedUnconf > 0) { LogPrintf("CDarksendPool::DoAutomaticDenominating -- Found unconfirmed denominated outputs, will wait till they confirm to continue.\n"); strAutoDenomResult = _("Found unconfirmed denominated outputs, will wait till they confirm to continue."); return false; @@ -1446,15 +1463,15 @@ bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) //check our collateral and create new if needed std::string strReason; - if(txMyCollateral == CMutableTransaction()) { - if(!pwalletMain->CreateCollateralTransaction(txMyCollateral, strReason)) { + if (txMyCollateral == CMutableTransaction()) { + if (!pwalletMain->CreateCollateralTransaction(txMyCollateral, strReason)) { LogPrintf("CDarksendPool::DoAutomaticDenominating -- create collateral error:%s\n", strReason); return false; } } else { - if(!IsCollateralValid(txMyCollateral)) { + if (!IsCollateralValid(txMyCollateral)) { LogPrintf("CDarksendPool::DoAutomaticDenominating -- invalid collateral, recreating...\n"); - if(!pwalletMain->CreateCollateralTransaction(txMyCollateral, strReason)) { + if (!pwalletMain->CreateCollateralTransaction(txMyCollateral, strReason)) { LogPrintf("CDarksendPool::DoAutomaticDenominating -- create collateral error: %s\n", strReason); return false; } @@ -1466,35 +1483,36 @@ bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) // If we've used 90% of the Znode list then drop the oldest first ~30% int nThreshold_high = nMnCountEnabled * 0.9; int nThreshold_low = nThreshold_high * 0.7; - LogPrint("privatesend", "Checking vecZnodesUsed: size: %d, threshold: %d\n", (int)vecZnodesUsed.size(), nThreshold_high); + LogPrint("privatesend", "Checking vecZnodesUsed: size: %d, threshold: %d\n", (int) vecZnodesUsed.size(), nThreshold_high); - if((int)vecZnodesUsed.size() > nThreshold_high) { + if ((int) vecZnodesUsed.size() > nThreshold_high) { vecZnodesUsed.erase(vecZnodesUsed.begin(), vecZnodesUsed.begin() + vecZnodesUsed.size() - nThreshold_low); - LogPrint("privatesend", " vecZnodesUsed: new size: %d, threshold: %d\n", (int)vecZnodesUsed.size(), nThreshold_high); + LogPrint("privatesend", " vecZnodesUsed: new size: %d, threshold: %d\n", (int) vecZnodesUsed.size(), nThreshold_high); } bool fUseQueue = GetRandInt(100) > 33; // don't use the queues all of the time for mixing unless we are a liquidity provider - if(nLiquidityProvider || fUseQueue) { + if (nLiquidityProvider || fUseQueue) { // Look through the queues and see if anything matches - BOOST_FOREACH(CDarksendQueue& dsq, vecDarksendQueue) { + BOOST_FOREACH(CDarksendQueue & dsq, vecDarksendQueue) + { // only try each queue once - if(dsq.fTried) continue; + if (dsq.fTried) continue; dsq.fTried = true; - if(dsq.IsExpired()) continue; + if (dsq.IsExpired()) continue; - CZnode* pmn = mnodeman.Find(dsq.vin); - if(pmn == NULL) { + CZnode *pmn = mnodeman.Find(dsq.vin); + if (pmn == NULL) { LogPrintf("CDarksendPool::DoAutomaticDenominating -- dsq znode is not in znode list, znode=%s\n", dsq.vin.prevout.ToStringShort()); continue; } - if(pmn->nProtocolVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) continue; + if (pmn->nProtocolVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) continue; std::vector vecBits; - if(!GetDenominationsBits(dsq.nDenom, vecBits)) { + if (!GetDenominationsBits(dsq.nDenom, vecBits)) { // incompatible denom continue; } @@ -1506,23 +1524,23 @@ bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) LogPrint("privatesend", "CDarksendPool::DoAutomaticDenominating -- found valid queue: %s\n", dsq.ToString()); CAmount nValueInTmp = 0; - std::vector vecTxInTmp; - std::vector vCoinsTmp; + std::vector vecTxInTmp; + std::vector vCoinsTmp; // Try to match their denominations if possible, select at least 1 denominations - if(!pwalletMain->SelectCoinsByDenominations(dsq.nDenom, vecPrivateSendDenominations[vecBits.front()], nBalanceNeedsAnonymized, vecTxInTmp, vCoinsTmp, nValueInTmp, 0, nPrivateSendRounds)) { + if (!pwalletMain->SelectCoinsByDenominations(dsq.nDenom, vecPrivateSendDenominations[vecBits.front()], nBalanceNeedsAnonymized, vecTxInTmp, vCoinsTmp, nValueInTmp, 0, nPrivateSendRounds)) { LogPrintf("CDarksendPool::DoAutomaticDenominating -- Couldn't match denominations %d %d (%s)\n", vecBits.front(), dsq.nDenom, GetDenominationsToString(dsq.nDenom)); continue; } vecZnodesUsed.push_back(dsq.vin); - CNode* pnodeFound = NULL; + CNode *pnodeFound = NULL; { LOCK(cs_vNodes); pnodeFound = FindNode(pmn->addr); - if(pnodeFound) { - if(pnodeFound->fDisconnect) { + if (pnodeFound) { + if (pnodeFound->fDisconnect) { continue; } else { pnodeFound->AddRef(); @@ -1532,18 +1550,18 @@ bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) LogPrintf("CDarksendPool::DoAutomaticDenominating -- attempt to connect to znode from queue, addr=%s\n", pmn->addr.ToString()); // connect to Znode and submit the queue request - CNode* pnode = (pnodeFound && pnodeFound->fZnode) ? pnodeFound : ConnectNodeDash(CAddress(pmn->addr, NODE_NETWORK), NULL, true); - if(pnode) { + CNode *pnode = (pnodeFound && pnodeFound->fZnode) ? pnodeFound : ConnectNode(CAddress(pmn->addr, NODE_NETWORK), NULL, false, true); + if (pnode) { pSubmittedToZnode = pmn; nSessionDenom = dsq.nDenom; pnode->PushMessage(NetMsgType::DSACCEPT, nSessionDenom, txMyCollateral); LogPrintf("CDarksendPool::DoAutomaticDenominating -- connected (from queue), sending DSACCEPT: nSessionDenom: %d (%s), addr=%s\n", - nSessionDenom, GetDenominationsToString(nSessionDenom), pnode->addr.ToString()); + nSessionDenom, GetDenominationsToString(nSessionDenom), pnode->addr.ToString()); strAutoDenomResult = _("Mixing in progress..."); SetState(POOL_STATE_QUEUE); nTimeLastSuccessfulStep = GetTimeMillis(); - if(pnodeFound) { + if (pnodeFound) { pnodeFound->Release(); } return true; @@ -1556,14 +1574,14 @@ bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) } // do not initiate queue if we are a liquidity provider to avoid useless inter-mixing - if(nLiquidityProvider) return false; + if (nLiquidityProvider) return false; int nTries = 0; // ** find the coins we'll use - std::vector vecTxIn; + std::vector vecTxIn; CAmount nValueInTmp = 0; - if(!pwalletMain->SelectCoinsDark(nValueMin, nBalanceNeedsAnonymized, vecTxIn, nValueInTmp, 0, nPrivateSendRounds)) { + if (!pwalletMain->SelectCoinsDark(nValueMin, nBalanceNeedsAnonymized, vecTxIn, nValueInTmp, 0, nPrivateSendRounds)) { // this should never happen LogPrintf("CDarksendPool::DoAutomaticDenominating -- Can't mix: no compatible inputs found!\n"); strAutoDenomResult = _("Can't mix: no compatible inputs found!"); @@ -1571,30 +1589,30 @@ bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) } // otherwise, try one randomly - while(nTries < 10) { - CZnode* pmn = mnodeman.FindRandomNotInVec(vecZnodesUsed, MIN_PRIVATESEND_PEER_PROTO_VERSION); - if(pmn == NULL) { + while (nTries < 10) { + CZnode *pmn = mnodeman.FindRandomNotInVec(vecZnodesUsed, MIN_PRIVATESEND_PEER_PROTO_VERSION); + if (pmn == NULL) { LogPrintf("CDarksendPool::DoAutomaticDenominating -- Can't find random znode!\n"); strAutoDenomResult = _("Can't find random Znode."); return false; } vecZnodesUsed.push_back(pmn->vin); - if(pmn->nLastDsq != 0 && pmn->nLastDsq + nMnCountEnabled/5 > mnodeman.nDsqCount) { + if (pmn->nLastDsq != 0 && pmn->nLastDsq + nMnCountEnabled / 5 > mnodeman.nDsqCount) { LogPrintf("CDarksendPool::DoAutomaticDenominating -- Too early to mix on this znode!" - " znode=%s addr=%s nLastDsq=%d CountEnabled/5=%d nDsqCount=%d\n", - pmn->vin.prevout.ToStringShort(), pmn->addr.ToString(), pmn->nLastDsq, - nMnCountEnabled/5, mnodeman.nDsqCount); + " znode=%s addr=%s nLastDsq=%d CountEnabled/5=%d nDsqCount=%d\n", + pmn->vin.prevout.ToStringShort(), pmn->addr.ToString(), pmn->nLastDsq, + nMnCountEnabled / 5, mnodeman.nDsqCount); nTries++; continue; } - CNode* pnodeFound = NULL; + CNode *pnodeFound = NULL; { LOCK(cs_vNodes); pnodeFound = FindNode(pmn->addr); - if(pnodeFound) { - if(pnodeFound->fDisconnect) { + if (pnodeFound) { + if (pnodeFound->fDisconnect) { nTries++; continue; } else { @@ -1604,25 +1622,25 @@ bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) } LogPrintf("CDarksendPool::DoAutomaticDenominating -- attempt %d connection to Znode %s\n", nTries, pmn->addr.ToString()); - CNode* pnode = (pnodeFound && pnodeFound->fZnode) ? pnodeFound : ConnectNodeDash(CAddress(pmn->addr, NODE_NETWORK), NULL, true); - if(pnode) { + CNode *pnode = (pnodeFound && pnodeFound->fZnode) ? pnodeFound : ConnectNode(CAddress(pmn->addr, NODE_NETWORK), NULL, false, true); + if (pnode) { LogPrintf("CDarksendPool::DoAutomaticDenominating -- connected, addr=%s\n", pmn->addr.ToString()); pSubmittedToZnode = pmn; - std::vector vecAmounts; + std::vector vecAmounts; pwalletMain->ConvertList(vecTxIn, vecAmounts); // try to get a single random denom out of vecAmounts - while(nSessionDenom == 0) { + while (nSessionDenom == 0) { nSessionDenom = GetDenominationsByAmounts(vecAmounts); } pnode->PushMessage(NetMsgType::DSACCEPT, nSessionDenom, txMyCollateral); LogPrintf("CDarksendPool::DoAutomaticDenominating -- connected, sending DSACCEPT, nSessionDenom: %d (%s)\n", - nSessionDenom, GetDenominationsToString(nSessionDenom)); + nSessionDenom, GetDenominationsToString(nSessionDenom)); strAutoDenomResult = _("Mixing in progress..."); SetState(POOL_STATE_QUEUE); nTimeLastSuccessfulStep = GetTimeMillis(); - if(pnodeFound) { + if (pnodeFound) { pnodeFound->Release(); } return true; @@ -1637,16 +1655,15 @@ bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) return false; } -bool CDarksendPool::SubmitDenominate() -{ +bool CDarksendPool::SubmitDenominate() { std::string strError; - std::vector vecTxInRet; - std::vector vecTxOutRet; + std::vector vecTxInRet; + std::vector vecTxOutRet; // Submit transaction to the pool if we get here // Try to use only inputs with the same number of rounds starting from lowest number of rounds possible - for(int i = 0; i < nPrivateSendRounds; i++) { - if(PrepareDenominate(i, i+1, strError, vecTxInRet, vecTxOutRet)) { + for (int i = 0; i < nPrivateSendRounds; i++) { + if (PrepareDenominate(i, i + 1, strError, vecTxInRet, vecTxOutRet)) { LogPrintf("CDarksendPool::SubmitDenominate -- Running PrivateSend denominate for %d rounds, success\n", i); return SendDenominate(vecTxInRet, vecTxOutRet); } @@ -1654,7 +1671,7 @@ bool CDarksendPool::SubmitDenominate() } // We failed? That's strange but let's just make final attempt and try to mix everything - if(PrepareDenominate(0, nPrivateSendRounds, strError, vecTxInRet, vecTxOutRet)) { + if (PrepareDenominate(0, nPrivateSendRounds, strError, vecTxInRet, vecTxOutRet)) { LogPrintf("CDarksendPool::SubmitDenominate -- Running PrivateSend denominate for all rounds, success\n"); return SendDenominate(vecTxInRet, vecTxOutRet); } @@ -1665,8 +1682,7 @@ bool CDarksendPool::SubmitDenominate() return false; } -bool CDarksendPool::PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, std::vector& vecTxInRet, std::vector& vecTxOutRet) -{ +bool CDarksendPool::PrepareDenominate(int nMinRounds, int nMaxRounds, std::string &strErrorRet, std::vector &vecTxInRet, std::vector &vecTxOutRet) { if (pwalletMain->IsLocked(true)) { strErrorRet = "Wallet locked, unable to create transaction!"; return false; @@ -1682,8 +1698,8 @@ bool CDarksendPool::PrepareDenominate(int nMinRounds, int nMaxRounds, std::strin vecTxOutRet.clear(); // ** find the coins we'll use - std::vector vecTxIn; - std::vector vCoins; + std::vector vecTxIn; + std::vector vCoins; CAmount nValueIn = 0; CReserveKey reservekey(pwalletMain); @@ -1703,11 +1719,12 @@ bool CDarksendPool::PrepareDenominate(int nMinRounds, int nMaxRounds, std::strin return false; } - LogPrintf("CDarksendPool::PrepareDenominate -- max value: %f\n", (double)nValueIn/COIN); + LogPrintf("CDarksendPool::PrepareDenominate -- max value: %f\n", (double) nValueIn / COIN); { LOCK(pwalletMain->cs_wallet); - BOOST_FOREACH(CTxIn txin, vecTxIn) { + BOOST_FOREACH(CTxIn + txin, vecTxIn) { pwalletMain->LockCoin(txin.prevout); } } @@ -1721,7 +1738,8 @@ bool CDarksendPool::PrepareDenominate(int nMinRounds, int nMaxRounds, std::strin int nStepsMax = 5 + GetRandInt(5); while (nStep < nStepsMax) { - BOOST_FOREACH(int nBit, vecBits) { + BOOST_FOREACH(int + nBit, vecBits) { CAmount nValueDenom = vecPrivateSendDenominations[nBit]; if (nValueLeft - nValueDenom < 0) continue; @@ -1758,14 +1776,15 @@ bool CDarksendPool::PrepareDenominate(int nMinRounds, int nMaxRounds, std::strin ++it2; } } - if(nValueLeft == 0) break; + if (nValueLeft == 0) break; nStep++; } { // unlock unused coins LOCK(pwalletMain->cs_wallet); - BOOST_FOREACH(CTxIn txin, vecTxIn) { + BOOST_FOREACH(CTxIn + txin, vecTxIn) { pwalletMain->UnlockCoin(txin.prevout); } } @@ -1773,7 +1792,8 @@ bool CDarksendPool::PrepareDenominate(int nMinRounds, int nMaxRounds, std::strin if (GetDenominations(vecTxOutRet) != nSessionDenom) { // unlock used coins on failure LOCK(pwalletMain->cs_wallet); - BOOST_FOREACH(CTxIn txin, vecTxInRet) { + BOOST_FOREACH(CTxIn + txin, vecTxInRet) { pwalletMain->UnlockCoin(txin.prevout); } strErrorRet = "Can't make current denominated outputs"; @@ -1785,16 +1805,16 @@ bool CDarksendPool::PrepareDenominate(int nMinRounds, int nMaxRounds, std::strin } // Create collaterals by looping through inputs grouped by addresses -bool CDarksendPool::MakeCollateralAmounts() -{ - std::vector vecTally; - if(!pwalletMain->SelectCoinsGrouppedByAddresses(vecTally, false)) { +bool CDarksendPool::MakeCollateralAmounts() { + std::vector vecTally; + if (!pwalletMain->SelectCoinsGrouppedByAddresses(vecTally, false)) { LogPrint("privatesend", "CDarksendPool::MakeCollateralAmounts -- SelectCoinsGrouppedByAddresses can't find any inputs!\n"); return false; } - BOOST_FOREACH(CompactTallyItem& item, vecTally) { - if(!MakeCollateralAmounts(item)) continue; + BOOST_FOREACH(CompactTallyItem & item, vecTally) + { + if (!MakeCollateralAmounts(item)) continue; return true; } @@ -1803,13 +1823,12 @@ bool CDarksendPool::MakeCollateralAmounts() } // Split up large inputs or create fee sized inputs -bool CDarksendPool::MakeCollateralAmounts(const CompactTallyItem& tallyItem) -{ +bool CDarksendPool::MakeCollateralAmounts(const CompactTallyItem &tallyItem) { CWalletTx wtx; CAmount nFeeRet = 0; int nChangePosRet = -1; std::string strFail = ""; - std::vector vecSend; + std::vector vecSend; // make our collateral address CReserveKey reservekeyCollateral(pwalletMain); @@ -1821,7 +1840,7 @@ bool CDarksendPool::MakeCollateralAmounts(const CompactTallyItem& tallyItem) assert(reservekeyCollateral.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked scriptCollateral = GetScriptForDestination(vchPubKey.GetID()); - vecSend.push_back((CRecipient){scriptCollateral, PRIVATESEND_COLLATERAL*4, false}); + vecSend.push_back((CRecipient) {scriptCollateral, PRIVATESEND_COLLATERAL * 4, false}); // try to use non-denominated and not mn-like funds first, select them explicitly CCoinControl coinControl; @@ -1829,12 +1848,13 @@ bool CDarksendPool::MakeCollateralAmounts(const CompactTallyItem& tallyItem) coinControl.fAllowWatchOnly = false; // send change to the same address so that we were able create more denoms out of it later coinControl.destChange = tallyItem.address.Get(); - BOOST_FOREACH(const CTxIn& txin, tallyItem.vecTxIn) - coinControl.Select(txin.prevout); + BOOST_FOREACH( + const CTxIn &txin, tallyItem.vecTxIn) + coinControl.Select(txin.prevout); //TODO //bool fSuccess = pwalletMain->CreateTransaction(vecSend, wtx, reservekeyChange, nFeeRet, nChangePosRet, strFail, &coinControl, true, ONLY_NONDENOMINATED_NOT1000IFMN); bool fSuccess = false; - if(!fSuccess) { + if (!fSuccess) { // 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 LogPrintf("CDarksendPool::MakeCollateralAmounts -- ONLY_NONDENOMINATED_NOT1000IFMN Error: %s\n", strFail); @@ -1842,7 +1862,7 @@ bool CDarksendPool::MakeCollateralAmounts(const CompactTallyItem& tallyItem) //TODO // fSuccess = pwalletMain->CreateTransaction(vecSend, wtx, reservekeyChange, nFeeRet, nChangePosRet, strFail, coinControlNull, true, ONLY_NOT1000IFMN); fSuccess = false; - if(!fSuccess) { + if (!fSuccess) { LogPrintf("CDarksendPool::MakeCollateralAmounts -- ONLY_NOT1000IFMN Error: %s\n", strFail); reservekeyCollateral.ReturnKey(); return false; @@ -1854,7 +1874,7 @@ bool CDarksendPool::MakeCollateralAmounts(const CompactTallyItem& tallyItem) LogPrintf("CDarksendPool::MakeCollateralAmounts -- txid=%s\n", wtx.GetHash().GetHex()); // use the same nCachedLastSuccessBlock as for DS mixinx to prevent race - if(!pwalletMain->CommitTransaction(wtx, reservekeyChange)) { + if (!pwalletMain->CommitTransaction(wtx, reservekeyChange)) { LogPrintf("CDarksendPool::MakeCollateralAmounts -- CommitTransaction failed!\n"); return false; } @@ -1865,18 +1885,18 @@ bool CDarksendPool::MakeCollateralAmounts(const CompactTallyItem& tallyItem) } // Create denominations by looping through inputs grouped by addresses -bool CDarksendPool::CreateDenominated() -{ - std::vector vecTally; - if(!pwalletMain->SelectCoinsGrouppedByAddresses(vecTally)) { +bool CDarksendPool::CreateDenominated() { + std::vector vecTally; + if (!pwalletMain->SelectCoinsGrouppedByAddresses(vecTally)) { LogPrint("privatesend", "CDarksendPool::CreateDenominated -- SelectCoinsGrouppedByAddresses can't find any inputs!\n"); return false; } bool fCreateMixingCollaterals = !pwalletMain->HasCollateralInputs(); - BOOST_FOREACH(CompactTallyItem& item, vecTally) { - if(!CreateDenominated(item, fCreateMixingCollaterals)) continue; + BOOST_FOREACH(CompactTallyItem & item, vecTally) + { + if (!CreateDenominated(item, fCreateMixingCollaterals)) continue; return true; } @@ -1885,13 +1905,12 @@ bool CDarksendPool::CreateDenominated() } // Create denominations -bool CDarksendPool::CreateDenominated(const CompactTallyItem& tallyItem, bool fCreateMixingCollaterals) -{ - std::vector vecSend; +bool CDarksendPool::CreateDenominated(const CompactTallyItem &tallyItem, bool fCreateMixingCollaterals) { + std::vector vecSend; CAmount nValueLeft = tallyItem.nAmount; nValueLeft -= PRIVATESEND_COLLATERAL; // leave some room for fees - LogPrintf("CreateDenominated0 nValueLeft: %f\n", (float)nValueLeft/COIN); + LogPrintf("CreateDenominated0 nValueLeft: %f\n", (float) nValueLeft / COIN); // make our collateral address CReserveKey reservekeyCollateral(pwalletMain); @@ -1902,9 +1921,9 @@ bool CDarksendPool::CreateDenominated(const CompactTallyItem& tallyItem, bool fC // ****** Add collateral outputs ************ / - if(fCreateMixingCollaterals) { - vecSend.push_back((CRecipient){scriptCollateral, PRIVATESEND_COLLATERAL*4, false}); - nValueLeft -= PRIVATESEND_COLLATERAL*4; + if (fCreateMixingCollaterals) { + vecSend.push_back((CRecipient) {scriptCollateral, PRIVATESEND_COLLATERAL * 4, false}); + nValueLeft -= PRIVATESEND_COLLATERAL * 4; } // ****** Add denoms ************ / @@ -1917,18 +1936,19 @@ bool CDarksendPool::CreateDenominated(const CompactTallyItem& tallyItem, bool fC bool fSkip = true; do { - BOOST_REVERSE_FOREACH(CAmount nDenomValue, vecPrivateSendDenominations) { + BOOST_REVERSE_FOREACH(CAmount + nDenomValue, vecPrivateSendDenominations) { - if(fSkip) { + if (fSkip) { // Note: denoms are skipped if there are already DENOMS_COUNT_MAX of them // and there are still larger denoms which can be used for mixing // check skipped denoms - if(IsDenomSkipped(nDenomValue)) continue; + if (IsDenomSkipped(nDenomValue)) continue; // find new denoms to skip if any (ignore the largest one) - if(nDenomValue != vecPrivateSendDenominations[0] && pwalletMain->CountInputsWithAmount(nDenomValue) > DENOMS_COUNT_MAX) { - strAutoDenomResult = strprintf(_("Too many %f denominations, removing."), (float)nDenomValue/COIN); + if (nDenomValue != vecPrivateSendDenominations[0] && pwalletMain->CountInputsWithAmount(nDenomValue) > DENOMS_COUNT_MAX) { + strAutoDenomResult = strprintf(_("Too many %f denominations, removing."), (float) nDenomValue / COIN); LogPrintf("CDarksendPool::CreateDenominated -- %s\n", strAutoDenomResult); vecDenominationsSkipped.push_back(nDenomValue); continue; @@ -1938,7 +1958,7 @@ bool CDarksendPool::CreateDenominated(const CompactTallyItem& tallyItem, bool fC int nOutputs = 0; // add each output up to 10 times until it can't be added again - while(nValueLeft - nDenomValue >= 0 && nOutputs <= 10) { + while (nValueLeft - nDenomValue >= 0 && nOutputs <= 10) { CScript scriptDenom; CPubKey vchPubKey; //use a unique change address @@ -1947,22 +1967,22 @@ bool CDarksendPool::CreateDenominated(const CompactTallyItem& tallyItem, bool fC // TODO: do not keep reservekeyDenom here reservekeyDenom.KeepKey(); - vecSend.push_back((CRecipient){ scriptDenom, nDenomValue, false }); + vecSend.push_back((CRecipient) {scriptDenom, nDenomValue, false}); //increment outputs and subtract denomination amount nOutputs++; nValueLeft -= nDenomValue; - LogPrintf("CreateDenominated1: nOutputsTotal: %d, nOutputs: %d, nValueLeft: %f\n", nOutputsTotal, nOutputs, (float)nValueLeft/COIN); + LogPrintf("CreateDenominated1: nOutputsTotal: %d, nOutputs: %d, nValueLeft: %f\n", nOutputsTotal, nOutputs, (float) nValueLeft / COIN); } nOutputsTotal += nOutputs; - if(nValueLeft == 0) break; + if (nValueLeft == 0) break; } - LogPrintf("CreateDenominated2: nOutputsTotal: %d, nValueLeft: %f\n", nOutputsTotal, (float)nValueLeft/COIN); + LogPrintf("CreateDenominated2: nOutputsTotal: %d, nValueLeft: %f\n", nOutputsTotal, (float) nValueLeft / COIN); // if there were no outputs added, start over without skipping fSkip = !fSkip; } while (nOutputsTotal == 0 && !fSkip); - LogPrintf("CreateDenominated3: nOutputsTotal: %d, nValueLeft: %f\n", nOutputsTotal, (float)nValueLeft/COIN); + LogPrintf("CreateDenominated3: nOutputsTotal: %d, nValueLeft: %f\n", nOutputsTotal, (float) nValueLeft / COIN); // if we have anything left over, it will be automatically send back as change - there is no need to send it manually @@ -1971,8 +1991,9 @@ bool CDarksendPool::CreateDenominated(const CompactTallyItem& tallyItem, bool fC coinControl.fAllowWatchOnly = false; // send change to the same address so that we were able create more denoms out of it later coinControl.destChange = tallyItem.address.Get(); - BOOST_FOREACH(const CTxIn& txin, tallyItem.vecTxIn) - coinControl.Select(txin.prevout); + BOOST_FOREACH( + const CTxIn &txin, tallyItem.vecTxIn) + coinControl.Select(txin.prevout); CWalletTx wtx; CAmount nFeeRet = 0; @@ -1983,7 +2004,7 @@ bool CDarksendPool::CreateDenominated(const CompactTallyItem& tallyItem, bool fC //TODO // bool fSuccess = pwalletMain->CreateTransaction(vecSend, wtx, reservekeyChange, nFeeRet, nChangePosRet, strFail, &coinControl, true, ONLY_NONDENOMINATED_NOT1000IFMN); bool fSuccess = false; - if(!fSuccess) { + if (!fSuccess) { LogPrintf("CDarksendPool::CreateDenominated -- Error: %s\n", strFail); // TODO: return reservekeyDenom here reservekeyCollateral.ReturnKey(); @@ -1993,7 +2014,7 @@ bool CDarksendPool::CreateDenominated(const CompactTallyItem& tallyItem, bool fC // TODO: keep reservekeyDenom here reservekeyCollateral.KeepKey(); - if(!pwalletMain->CommitTransaction(wtx, reservekeyChange)) { + if (!pwalletMain->CommitTransaction(wtx, reservekeyChange)) { LogPrintf("CDarksendPool::CreateDenominated -- CommitTransaction failed!\n"); return false; } @@ -2005,32 +2026,31 @@ bool CDarksendPool::CreateDenominated(const CompactTallyItem& tallyItem, bool fC return true; } -bool CDarksendPool::IsOutputsCompatibleWithSessionDenom(const std::vector& vecTxDSOut) -{ - if(GetDenominations(vecTxDSOut) == 0) return false; +bool CDarksendPool::IsOutputsCompatibleWithSessionDenom(const std::vector &vecTxDSOut) { + if (GetDenominations(vecTxDSOut) == 0) return false; - BOOST_FOREACH(const CDarkSendEntry entry, vecEntries) { + BOOST_FOREACH( + const CDarkSendEntry entry, vecEntries) { LogPrintf("CDarksendPool::IsOutputsCompatibleWithSessionDenom -- vecTxDSOut denom %d, entry.vecTxDSOut denom %d\n", GetDenominations(vecTxDSOut), GetDenominations(entry.vecTxDSOut)); - if(GetDenominations(vecTxDSOut) != GetDenominations(entry.vecTxDSOut)) return false; + if (GetDenominations(vecTxDSOut) != GetDenominations(entry.vecTxDSOut)) return false; } return true; } -bool CDarksendPool::IsAcceptableDenomAndCollateral(int nDenom, CTransaction txCollateral, PoolMessage& nMessageIDRet) -{ - if(!fZNode) return false; +bool CDarksendPool::IsAcceptableDenomAndCollateral(int nDenom, CTransaction txCollateral, PoolMessage &nMessageIDRet) { + if (!fZNode) return false; // is denom even smth legit? std::vector vecBits; - if(!GetDenominationsBits(nDenom, vecBits)) { + if (!GetDenominationsBits(nDenom, vecBits)) { LogPrint("privatesend", "CDarksendPool::IsAcceptableDenomAndCollateral -- denom not valid!\n"); nMessageIDRet = ERR_DENOM; return false; } // check collateral - if(!fUnitTest && !IsCollateralValid(txCollateral)) { + if (!fUnitTest && !IsCollateralValid(txCollateral)) { LogPrint("privatesend", "CDarksendPool::IsAcceptableDenomAndCollateral -- collateral not valid!\n"); nMessageIDRet = ERR_INVALID_COLLATERAL; return false; @@ -2039,30 +2059,29 @@ bool CDarksendPool::IsAcceptableDenomAndCollateral(int nDenom, CTransaction txCo return true; } -bool CDarksendPool::CreateNewSession(int nDenom, CTransaction txCollateral, PoolMessage& nMessageIDRet) -{ - if(!fZNode || nSessionID != 0) return false; +bool CDarksendPool::CreateNewSession(int nDenom, CTransaction txCollateral, PoolMessage &nMessageIDRet) { + if (!fZNode || nSessionID != 0) return false; // new session can only be started in idle mode - if(nState != POOL_STATE_IDLE) { + if (nState != POOL_STATE_IDLE) { nMessageIDRet = ERR_MODE; LogPrintf("CDarksendPool::CreateNewSession -- incompatible mode: nState=%d\n", nState); return false; } - if(!IsAcceptableDenomAndCollateral(nDenom, txCollateral, nMessageIDRet)) { + if (!IsAcceptableDenomAndCollateral(nDenom, txCollateral, nMessageIDRet)) { return false; } // start new session nMessageIDRet = MSG_NOERR; - nSessionID = GetRandInt(999999)+1; + nSessionID = GetRandInt(999999) + 1; nSessionDenom = nDenom; SetState(POOL_STATE_QUEUE); nTimeLastSuccessfulStep = GetTimeMillis(); - if(!fUnitTest) { + if (!fUnitTest) { //broadcast that I'm accepting entries, only if it's the first entry through CDarksendQueue dsq(nDenom, activeZnode.vin, GetTime(), false); LogPrint("privatesend", "CDarksendPool::CreateNewSession -- signing and relaying new queue: %s\n", dsq.ToString()); @@ -2073,29 +2092,28 @@ bool CDarksendPool::CreateNewSession(int nDenom, CTransaction txCollateral, Pool vecSessionCollaterals.push_back(txCollateral); LogPrintf("CDarksendPool::CreateNewSession -- new session created, nSessionID: %d nSessionDenom: %d (%s) vecSessionCollaterals.size(): %d\n", - nSessionID, nSessionDenom, GetDenominationsToString(nSessionDenom), vecSessionCollaterals.size()); + nSessionID, nSessionDenom, GetDenominationsToString(nSessionDenom), vecSessionCollaterals.size()); return true; } -bool CDarksendPool::AddUserToExistingSession(int nDenom, CTransaction txCollateral, PoolMessage& nMessageIDRet) -{ - if(!fZNode || nSessionID == 0 || IsSessionReady()) return false; +bool CDarksendPool::AddUserToExistingSession(int nDenom, CTransaction txCollateral, PoolMessage &nMessageIDRet) { + if (!fZNode || nSessionID == 0 || IsSessionReady()) return false; - if(!IsAcceptableDenomAndCollateral(nDenom, txCollateral, nMessageIDRet)) { + if (!IsAcceptableDenomAndCollateral(nDenom, txCollateral, nMessageIDRet)) { return false; } // we only add new users to an existing session when we are in queue mode - if(nState != POOL_STATE_QUEUE) { + if (nState != POOL_STATE_QUEUE) { nMessageIDRet = ERR_MODE; LogPrintf("CDarksendPool::AddUserToExistingSession -- incompatible mode: nState=%d\n", nState); return false; } - if(nDenom != nSessionDenom) { + if (nDenom != nSessionDenom) { LogPrintf("CDarksendPool::AddUserToExistingSession -- incompatible denom %d (%s) != nSessionDenom %d (%s)\n", - nDenom, GetDenominationsToString(nDenom), nSessionDenom, GetDenominationsToString(nSessionDenom)); + nDenom, GetDenominationsToString(nDenom), nSessionDenom, GetDenominationsToString(nSessionDenom)); nMessageIDRet = ERR_DENOM; return false; } @@ -2107,7 +2125,7 @@ bool CDarksendPool::AddUserToExistingSession(int nDenom, CTransaction txCollater vecSessionCollaterals.push_back(txCollateral); LogPrintf("CDarksendPool::AddUserToExistingSession -- new user accepted, nSessionID: %d nSessionDenom: %d (%s) vecSessionCollaterals.size(): %d\n", - nSessionID, nSessionDenom, GetDenominationsToString(nSessionDenom), vecSessionCollaterals.size()); + nSessionID, nSessionDenom, GetDenominationsToString(nSessionDenom), vecSessionCollaterals.size()); return true; } @@ -2122,34 +2140,33 @@ bool CDarksendPool::AddUserToExistingSession(int nDenom, CTransaction txCollater bit 4 and so on - out-of-bounds none of above - non-denom */ -std::string CDarksendPool::GetDenominationsToString(int nDenom) -{ +std::string CDarksendPool::GetDenominationsToString(int nDenom) { std::string strDenom = ""; int nMaxDenoms = vecPrivateSendDenominations.size(); - if(nDenom >= (1 << nMaxDenoms)) { + if (nDenom >= (1 << nMaxDenoms)) { return "out-of-bounds"; } for (int i = 0; i < nMaxDenoms; ++i) { - if(nDenom & (1 << i)) { + if (nDenom & (1 << i)) { strDenom += (strDenom.empty() ? "" : "+") + FormatMoney(vecPrivateSendDenominations[i]); } } - if(strDenom.empty()) { + if (strDenom.empty()) { return "non-denom"; } return strDenom; } -int CDarksendPool::GetDenominations(const std::vector& vecTxDSOut) -{ - std::vector vecTxOut; +int CDarksendPool::GetDenominations(const std::vector &vecTxDSOut) { + std::vector vecTxOut; - BOOST_FOREACH(CTxDSOut out, vecTxDSOut) - vecTxOut.push_back(out); + BOOST_FOREACH(CTxDSOut + out, vecTxDSOut) + vecTxOut.push_back(out); return GetDenominations(vecTxOut); } @@ -2163,40 +2180,42 @@ int CDarksendPool::GetDenominations(const std::vector& vecTxDSOut) .1 - bit 3 non-denom - 0, all bits off */ -int CDarksendPool::GetDenominations(const std::vector& vecTxOut, bool fSingleRandomDenom) -{ - std::vector > vecDenomUsed; +int CDarksendPool::GetDenominations(const std::vector &vecTxOut, bool fSingleRandomDenom) { + std::vector > vecDenomUsed; // make a list of denominations, with zero uses - BOOST_FOREACH(CAmount nDenomValue, vecPrivateSendDenominations) - vecDenomUsed.push_back(std::make_pair(nDenomValue, 0)); + BOOST_FOREACH(CAmount + nDenomValue, vecPrivateSendDenominations) + vecDenomUsed.push_back(std::make_pair(nDenomValue, 0)); // look for denominations and update uses to 1 - BOOST_FOREACH(CTxOut txout, vecTxOut) { + BOOST_FOREACH(CTxOut + txout, vecTxOut) { bool found = false; - BOOST_FOREACH (PAIRTYPE(CAmount, int)& s, vecDenomUsed) { - if(txout.nValue == s.first) { + BOOST_FOREACH(PAIRTYPE(CAmount, int) &s, vecDenomUsed) + { + if (txout.nValue == s.first) { s.second = 1; found = true; } } - if(!found) return 0; + if (!found) return 0; } int nDenom = 0; int c = 0; // if the denomination is used, shift the bit on - BOOST_FOREACH (PAIRTYPE(CAmount, int)& s, vecDenomUsed) { + BOOST_FOREACH(PAIRTYPE(CAmount, int) &s, vecDenomUsed) + { int bit = (fSingleRandomDenom ? GetRandInt(2) : 1) & s.second; nDenom |= bit << c++; - if(fSingleRandomDenom && bit) break; // use just one random denomination + if (fSingleRandomDenom && bit) break; // use just one random denomination } return nDenom; } -bool CDarksendPool::GetDenominationsBits(int nDenom, std::vector &vecBitsRet) -{ +bool CDarksendPool::GetDenominationsBits(int nDenom, std::vector &vecBitsRet) { // ( bit on if present, 4 denominations example ) // bit 0 - 100DASH+1 // bit 1 - 10DASH+1 @@ -2205,12 +2224,12 @@ bool CDarksendPool::GetDenominationsBits(int nDenom, std::vector &vecBitsRe int nMaxDenoms = vecPrivateSendDenominations.size(); - if(nDenom >= (1 << nMaxDenoms)) return false; + if (nDenom >= (1 << nMaxDenoms)) return false; vecBitsRet.clear(); for (int i = 0; i < nMaxDenoms; ++i) { - if(nDenom & (1 << i)) { + if (nDenom & (1 << i)) { vecBitsRet.push_back(i); } } @@ -2218,12 +2237,12 @@ bool CDarksendPool::GetDenominationsBits(int nDenom, std::vector &vecBitsRe return !vecBitsRet.empty(); } -int CDarksendPool::GetDenominationsByAmounts(const std::vector& vecAmount) -{ +int CDarksendPool::GetDenominationsByAmounts(const std::vector &vecAmount) { CScript scriptTmp = CScript(); - std::vector vecTxOut; + std::vector vecTxOut; - BOOST_REVERSE_FOREACH(CAmount nAmount, vecAmount) { + BOOST_REVERSE_FOREACH(CAmount + nAmount, vecAmount) { CTxOut txout(nAmount, scriptTmp); vecTxOut.push_back(txout); } @@ -2231,55 +2250,75 @@ int CDarksendPool::GetDenominationsByAmounts(const std::vector& vecAmou return GetDenominations(vecTxOut, true); } -std::string CDarksendPool::GetMessageByID(PoolMessage nMessageID) -{ +std::string CDarksendPool::GetMessageByID(PoolMessage nMessageID) { switch (nMessageID) { - case ERR_ALREADY_HAVE: return _("Already have that input."); - case ERR_DENOM: return _("No matching denominations found for mixing."); - case ERR_ENTRIES_FULL: return _("Entries are full."); - case ERR_EXISTING_TX: return _("Not compatible with existing transactions."); - case ERR_FEES: return _("Transaction fees are too high."); - case ERR_INVALID_COLLATERAL: return _("Collateral not valid."); - case ERR_INVALID_INPUT: return _("Input is not valid."); - case ERR_INVALID_SCRIPT: return _("Invalid script detected."); - case ERR_INVALID_TX: return _("Transaction not valid."); - case ERR_MAXIMUM: return _("Value more than PrivateSend pool maximum allows."); - case ERR_MN_LIST: return _("Not in the Znode list."); - case ERR_MODE: return _("Incompatible mode."); - case ERR_NON_STANDARD_PUBKEY: return _("Non-standard public key detected."); - case ERR_NOT_A_MN: return _("This is not a Znode."); - case ERR_QUEUE_FULL: return _("Znode queue is full."); - case ERR_RECENT: return _("Last PrivateSend was too recent."); - case ERR_SESSION: return _("Session not complete!"); - case ERR_MISSING_TX: return _("Missing input transaction information."); - case ERR_VERSION: return _("Incompatible version."); - case MSG_NOERR: return _("No errors detected."); - case MSG_SUCCESS: return _("Transaction created successfully."); - case MSG_ENTRIES_ADDED: return _("Your entries added successfully."); - default: return _("Unknown response."); + case ERR_ALREADY_HAVE: + return _("Already have that input."); + case ERR_DENOM: + return _("No matching denominations found for mixing."); + case ERR_ENTRIES_FULL: + return _("Entries are full."); + case ERR_EXISTING_TX: + return _("Not compatible with existing transactions."); + case ERR_FEES: + return _("Transaction fees are too high."); + case ERR_INVALID_COLLATERAL: + return _("Collateral not valid."); + case ERR_INVALID_INPUT: + return _("Input is not valid."); + case ERR_INVALID_SCRIPT: + return _("Invalid script detected."); + case ERR_INVALID_TX: + return _("Transaction not valid."); + case ERR_MAXIMUM: + return _("Value more than PrivateSend pool maximum allows."); + case ERR_MN_LIST: + return _("Not in the Znode list."); + case ERR_MODE: + return _("Incompatible mode."); + case ERR_NON_STANDARD_PUBKEY: + return _("Non-standard public key detected."); + case ERR_NOT_A_MN: + return _("This is not a Znode."); + case ERR_QUEUE_FULL: + return _("Znode queue is full."); + case ERR_RECENT: + return _("Last PrivateSend was too recent."); + case ERR_SESSION: + return _("Session not complete!"); + case ERR_MISSING_TX: + return _("Missing input transaction information."); + case ERR_VERSION: + return _("Incompatible version."); + case MSG_NOERR: + return _("No errors detected."); + case MSG_SUCCESS: + return _("Transaction created successfully."); + case MSG_ENTRIES_ADDED: + return _("Your entries added successfully."); + default: + return _("Unknown response."); } } -bool CDarkSendSigner::IsVinAssociatedWithPubkey(const CTxIn& txin, const CPubKey& pubkey) -{ +bool CDarkSendSigner::IsVinAssociatedWithPubkey(const CTxIn &txin, const CPubKey &pubkey) { CScript payee; payee = GetScriptForDestination(pubkey.GetID()); CTransaction tx; uint256 hash; - if(GetTransaction(txin.prevout.hash, tx, Params().GetConsensus(), hash, true)) { + if (GetTransaction(txin.prevout.hash, tx, Params().GetConsensus(), hash, true)) { BOOST_FOREACH(CTxOut out, tx.vout) - if(out.nValue == 1000*COIN && out.scriptPubKey == payee) return true; + if (out.nValue == ZNODE_COIN_REQUIRED * COIN && out.scriptPubKey == payee) return true; } return false; } -bool CDarkSendSigner::GetKeysFromSecret(std::string strSecret, CKey& keyRet, CPubKey& pubkeyRet) -{ +bool CDarkSendSigner::GetKeysFromSecret(std::string strSecret, CKey &keyRet, CPubKey &pubkeyRet) { CBitcoinSecret vchSecret; - if(!vchSecret.SetString(strSecret)) return false; + if (!vchSecret.SetString(strSecret)) return false; keyRet = vchSecret.GetKey(); pubkeyRet = keyRet.GetPubKey(); @@ -2287,8 +2326,7 @@ bool CDarkSendSigner::GetKeysFromSecret(std::string strSecret, CKey& keyRet, CPu return true; } -bool CDarkSendSigner::SignMessage(std::string strMessage, std::vector& vchSigRet, CKey key) -{ +bool CDarkSendSigner::SignMessage(std::string strMessage, std::vector &vchSigRet, CKey key) { CHashWriter ss(SER_GETHASH, 0); ss << strMessageMagic; ss << strMessage; @@ -2296,33 +2334,32 @@ bool CDarkSendSigner::SignMessage(std::string strMessage, std::vector& vchSig, std::string strMessage, std::string& strErrorRet) -{ +bool CDarkSendSigner::VerifyMessage(CPubKey pubkey, const std::vector &vchSig, std::string strMessage, std::string &strErrorRet) { CHashWriter ss(SER_GETHASH, 0); ss << strMessageMagic; ss << strMessage; CPubKey pubkeyFromSig; - if(!pubkeyFromSig.RecoverCompact(ss.GetHash(), vchSig)) { + if (!pubkeyFromSig.RecoverCompact(ss.GetHash(), vchSig)) { strErrorRet = "Error recovering public key."; return false; } - if(pubkeyFromSig.GetID() != pubkey.GetID()) { + if (pubkeyFromSig.GetID() != pubkey.GetID()) { strErrorRet = strprintf("Keys don't match: pubkey=%s, pubkeyFromSig=%s, strMessage=%s, vchSig=%s", - pubkey.GetID().ToString(), pubkeyFromSig.GetID().ToString(), strMessage, - EncodeBase64(&vchSig[0], vchSig.size())); + pubkey.GetID().ToString(), pubkeyFromSig.GetID().ToString(), strMessage, + EncodeBase64(&vchSig[0], vchSig.size())); return false; } return true; } -bool CDarkSendEntry::AddScriptSig(const CTxIn& txin) -{ - BOOST_FOREACH(CTxDSIn& txdsin, vecTxDSIn) { - if(txdsin.prevout == txin.prevout && txdsin.nSequence == txin.nSequence) { - if(txdsin.fHasSig) return false; +bool CDarkSendEntry::AddScriptSig(const CTxIn &txin) { + BOOST_FOREACH(CTxDSIn & txdsin, vecTxDSIn) + { + if (txdsin.prevout == txin.prevout && txdsin.nSequence == txin.nSequence) { + if (txdsin.fHasSig) return false; txdsin.scriptSig = txin.scriptSig; txdsin.prevPubKey = txin.prevPubKey; @@ -2335,13 +2372,12 @@ bool CDarkSendEntry::AddScriptSig(const CTxIn& txin) return false; } -bool CDarksendQueue::Sign() -{ - if(!fZNode) return false; +bool CDarksendQueue::Sign() { + if (!fZNode) return false; std::string strMessage = vin.ToString() + boost::lexical_cast(nDenom) + boost::lexical_cast(nTime) + boost::lexical_cast(fReady); - if(!darkSendSigner.SignMessage(strMessage, vchSig, activeZnode.keyZnode)) { + if (!darkSendSigner.SignMessage(strMessage, vchSig, activeZnode.keyZnode)) { LogPrintf("CDarksendQueue::Sign -- SignMessage() failed, %s\n", ToString()); return false; } @@ -2349,12 +2385,11 @@ bool CDarksendQueue::Sign() return CheckSignature(activeZnode.pubKeyZnode); } -bool CDarksendQueue::CheckSignature(const CPubKey& pubKeyZnode) -{ +bool CDarksendQueue::CheckSignature(const CPubKey &pubKeyZnode) { std::string strMessage = vin.ToString() + boost::lexical_cast(nDenom) + boost::lexical_cast(nTime) + boost::lexical_cast(fReady); std::string strError = ""; - if(!darkSendSigner.VerifyMessage(pubKeyZnode, vchSig, strMessage, strError)) { + if (!darkSendSigner.VerifyMessage(pubKeyZnode, vchSig, strMessage, strError)) { LogPrintf("CDarksendQueue::CheckSignature -- Got bad Znode queue signature: %s; error: %s\n", ToString(), strError); return false; } @@ -2362,24 +2397,22 @@ bool CDarksendQueue::CheckSignature(const CPubKey& pubKeyZnode) return true; } -bool CDarksendQueue::Relay() -{ - std::vector vNodesCopy = CopyNodeVector(); - BOOST_FOREACH(CNode* pnode, vNodesCopy) - if(pnode->nVersion >= MIN_PRIVATESEND_PEER_PROTO_VERSION) +bool CDarksendQueue::Relay() { + std::vector < CNode * > vNodesCopy = CopyNodeVector(); + BOOST_FOREACH(CNode * pnode, vNodesCopy) + if (pnode->nVersion >= MIN_PRIVATESEND_PEER_PROTO_VERSION) pnode->PushMessage(NetMsgType::DSQUEUE, (*this)); ReleaseNodeVector(vNodesCopy); return true; } -bool CDarksendBroadcastTx::Sign() -{ - if(!fZNode) return false; +bool CDarksendBroadcastTx::Sign() { + if (!fZNode) return false; std::string strMessage = tx.GetHash().ToString() + boost::lexical_cast(sigTime); - if(!darkSendSigner.SignMessage(strMessage, vchSig, activeZnode.keyZnode)) { + if (!darkSendSigner.SignMessage(strMessage, vchSig, activeZnode.keyZnode)) { LogPrintf("CDarksendBroadcastTx::Sign -- SignMessage() failed\n"); return false; } @@ -2387,12 +2420,11 @@ bool CDarksendBroadcastTx::Sign() return CheckSignature(activeZnode.pubKeyZnode); } -bool CDarksendBroadcastTx::CheckSignature(const CPubKey& pubKeyZnode) -{ +bool CDarksendBroadcastTx::CheckSignature(const CPubKey &pubKeyZnode) { std::string strMessage = tx.GetHash().ToString() + boost::lexical_cast(sigTime); std::string strError = ""; - if(!darkSendSigner.VerifyMessage(pubKeyZnode, vchSig, strMessage, strError)) { + if (!darkSendSigner.VerifyMessage(pubKeyZnode, vchSig, strMessage, strError)) { LogPrintf("CDarksendBroadcastTx::CheckSignature -- Got bad dstx signature, error: %s\n", strError); return false; } @@ -2400,50 +2432,44 @@ bool CDarksendBroadcastTx::CheckSignature(const CPubKey& pubKeyZnode) return true; } -void CDarksendPool::RelayFinalTransaction(const CTransaction& txFinal) -{ +void CDarksendPool::RelayFinalTransaction(const CTransaction &txFinal) { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - if(pnode->nVersion >= MIN_PRIVATESEND_PEER_PROTO_VERSION) + BOOST_FOREACH(CNode * pnode, vNodes) + if (pnode->nVersion >= MIN_PRIVATESEND_PEER_PROTO_VERSION) pnode->PushMessage(NetMsgType::DSFINALTX, nSessionID, txFinal); } -void CDarksendPool::RelayIn(const CDarkSendEntry& entry) -{ - if(!pSubmittedToZnode) return; +void CDarksendPool::RelayIn(const CDarkSendEntry &entry) { + if (!pSubmittedToZnode) return; - CNode* pnode = FindNode(pSubmittedToZnode->addr); - if(pnode != NULL) { + CNode *pnode = FindNode(pSubmittedToZnode->addr); + if (pnode != NULL) { LogPrintf("CDarksendPool::RelayIn -- found master, relaying message to %s\n", pnode->addr.ToString()); pnode->PushMessage(NetMsgType::DSVIN, entry); } } -void CDarksendPool::PushStatus(CNode* pnode, PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID) -{ - if(!pnode) return; - pnode->PushMessage(NetMsgType::DSSTATUSUPDATE, nSessionID, (int)nState, (int)vecEntries.size(), (int)nStatusUpdate, (int)nMessageID); +void CDarksendPool::PushStatus(CNode *pnode, PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID) { + if (!pnode) return; + pnode->PushMessage(NetMsgType::DSSTATUSUPDATE, nSessionID, (int) nState, (int) vecEntries.size(), (int) nStatusUpdate, (int) nMessageID); } -void CDarksendPool::RelayStatus(PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID) -{ +void CDarksendPool::RelayStatus(PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID) { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - if(pnode->nVersion >= MIN_PRIVATESEND_PEER_PROTO_VERSION) + BOOST_FOREACH(CNode * pnode, vNodes) + if (pnode->nVersion >= MIN_PRIVATESEND_PEER_PROTO_VERSION) PushStatus(pnode, nStatusUpdate, nMessageID); } -void CDarksendPool::RelayCompletedTransaction(PoolMessage nMessageID) -{ +void CDarksendPool::RelayCompletedTransaction(PoolMessage nMessageID) { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - if(pnode->nVersion >= MIN_PRIVATESEND_PEER_PROTO_VERSION) - pnode->PushMessage(NetMsgType::DSCOMPLETE, nSessionID, (int)nMessageID); + BOOST_FOREACH(CNode * pnode, vNodes) + if (pnode->nVersion >= MIN_PRIVATESEND_PEER_PROTO_VERSION) + pnode->PushMessage(NetMsgType::DSCOMPLETE, nSessionID, (int) nMessageID); } -void CDarksendPool::SetState(PoolState nStateNew) -{ - if(fZNode && (nStateNew == POOL_STATE_ERROR || nStateNew == POOL_STATE_SUCCESS)) { +void CDarksendPool::SetState(PoolState nStateNew) { + if (fZNode && (nStateNew == POOL_STATE_ERROR || nStateNew == POOL_STATE_SUCCESS)) { LogPrint("privatesend", "CDarksendPool::SetState -- Can't set state to ERROR or SUCCESS as a Znode. \n"); return; } @@ -2452,23 +2478,21 @@ void CDarksendPool::SetState(PoolState nStateNew) nState = nStateNew; } -void CDarksendPool::UpdatedBlockTip(const CBlockIndex *pindex) -{ +void CDarksendPool::UpdatedBlockTip(const CBlockIndex *pindex) { pCurrentBlockIndex = pindex; LogPrint("privatesend", "CDarksendPool::UpdatedBlockTip -- pCurrentBlockIndex->nHeight: %d\n", pCurrentBlockIndex->nHeight); - if(!fLiteMode && znodeSync.IsZnodeListSynced()) { + if (!fLiteMode && znodeSync.IsZnodeListSynced()) { NewBlock(); } } //TODO: Rename/move to core -void ThreadCheckDarkSendPool() -{ - if(fLiteMode) return; // disable all Dash specific functionality +void ThreadCheckDarkSendPool() { + if (fLiteMode) return; // disable all Dash specific functionality static bool fOneThread; - if(fOneThread) return; + if (fOneThread) return; fOneThread = true; // Make this thread recognisable as the PrivateSend thread @@ -2477,14 +2501,13 @@ void ThreadCheckDarkSendPool() unsigned int nTick = 0; unsigned int nDoAutoNextRun = nTick + PRIVATESEND_AUTO_TIMEOUT_MIN; - while (true) - { + while (true) { MilliSleep(1000); // try to sync from all available nodes, one step at a time znodeSync.ProcessTick(); - if(znodeSync.IsBlockchainSynced() && !ShutdownRequested()) { + if (znodeSync.IsBlockchainSynced() && !ShutdownRequested()) { nTick++; @@ -2493,16 +2516,16 @@ void ThreadCheckDarkSendPool() // check if we should activate or ping every few minutes, // slightly postpone first run to give net thread a chance to connect to some peers - if(nTick % ZNODE_MIN_MNP_SECONDS == 15) + if (nTick % ZNODE_MIN_MNP_SECONDS == 15) activeZnode.ManageState(); - if(nTick % 60 == 0) { + if (nTick % 60 == 0) { mnodeman.ProcessZnodeConnections(); mnodeman.CheckAndRemove(); mnpayments.CheckAndRemove(); instantsend.CheckAndRemove(); } - if(fZNode && (nTick % (60 * 5) == 0)) { + if (fZNode && (nTick % (60 * 5) == 0)) { mnodeman.DoFullVerificationStep(); } @@ -2513,7 +2536,7 @@ void ThreadCheckDarkSendPool() darkSendPool.CheckTimeout(); darkSendPool.CheckForCompleteQueue(); - if(nDoAutoNextRun == nTick) { + if (nDoAutoNextRun == nTick) { darkSendPool.DoAutomaticDenominating(); nDoAutoNextRun = nTick + PRIVATESEND_AUTO_TIMEOUT_MIN + GetRandInt(PRIVATESEND_AUTO_TIMEOUT_MAX - PRIVATESEND_AUTO_TIMEOUT_MIN); } diff --git a/src/darksend.h b/src/darksend.h index 65cd52360b..6dc623cf80 100644 --- a/src/darksend.h +++ b/src/darksend.h @@ -8,7 +8,7 @@ #include "znode.h" #include "wallet/wallet.h" -//class CDarksendPool; +class CDarksendPool; class CDarkSendSigner; class CDarksendBroadcastTx; @@ -36,11 +36,16 @@ static const int PRIVATESEND_KEYS_THRESHOLD_WARNING = 100; // Stop mixing completely, it's too dangerous to continue when we have only this many keys left static const int PRIVATESEND_KEYS_THRESHOLD_STOP = 50; +// The main object for accessing mixing +extern CDarksendPool darkSendPool; +// A helper object for signing messages from Znodes +extern CDarkSendSigner darkSendSigner; + extern int nPrivateSendRounds; extern int nPrivateSendAmount; extern int nLiquidityProvider; extern bool fEnablePrivateSend; -//extern bool fPrivateSendMultiSession; +extern bool fPrivateSendMultiSession; extern std::map mapDarksendBroadcastTxes; extern std::vector vecPrivateSendDenominations; @@ -473,11 +478,6 @@ class CDarksendPool void UpdatedBlockTip(const CBlockIndex *pindex); }; -// The main object for accessing mixing -extern CDarksendPool darkSendPool; -// A helper object for signing messages from Znodes -extern CDarkSendSigner darkSendSigner; - void ThreadCheckDarkSendPool(); #endif \ No newline at end of file diff --git a/src/definition.h b/src/definition.h index 14db923f3e..73343a10c9 100644 --- a/src/definition.h +++ b/src/definition.h @@ -16,7 +16,7 @@ enum { BLOCK_VERSION_CHAIN_START = (1 << 16), BLOCK_VERSION_CHAIN_END = (1 << 30), }; -static int64_t nStartRewardTime = 1475020800; +static const int64_t nStartRewardTime = 1475020800; #endif //BTZC_DEFINITION_H diff --git a/src/flat-database.h b/src/flat-database.h new file mode 100644 index 0000000000..bba622a65c --- /dev/null +++ b/src/flat-database.h @@ -0,0 +1,229 @@ +// Copyright (c) 2014-2017 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef FLAT_DATABASE_H +#define FLAT_DATABASE_H + +#include "chainparams.h" +#include "clientversion.h" +#include "hash.h" +#include "streams.h" +#include "util.h" + +#include + +/** +* Generic Dumping and Loading +* --------------------------- +*/ + +template +class CFlatDB +{ +private: + + enum ReadResult { + Ok, + FileError, + HashReadError, + IncorrectHash, + IncorrectMagicMessage, + IncorrectMagicNumber, + IncorrectFormat + }; + + boost::filesystem::path pathDB; + std::string strFilename; + std::string strMagicMessage; + + bool Write(const T& objToSave) + { + // LOCK(objToSave.cs); + + int64_t nStart = GetTimeMillis(); + + // serialize, checksum data up to that point, then append checksum + CDataStream ssObj(SER_DISK, CLIENT_VERSION); + ssObj << strMagicMessage; // specific magic message for this type of object + ssObj << FLATDATA(Params().MessageStart()); // network specific magic number + ssObj << objToSave; + uint256 hash = Hash(ssObj.begin(), ssObj.end()); + ssObj << hash; + + // open output file, and associate with CAutoFile + FILE *file = fopen(pathDB.string().c_str(), "wb"); + CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); + if (fileout.IsNull()) + return error("%s: Failed to open file %s", __func__, pathDB.string()); + + // Write and commit header, data + try { + fileout << ssObj; + } + catch (std::exception &e) { + return error("%s: Serialize or I/O error - %s", __func__, e.what()); + } + fileout.fclose(); + + LogPrintf("Written info to %s %dms\n", strFilename, GetTimeMillis() - nStart); + LogPrintf(" %s\n", objToSave.ToString()); + + return true; + } + + ReadResult Read(T& objToLoad, bool fDryRun = false) + { + //LOCK(objToLoad.cs); + + int64_t nStart = GetTimeMillis(); + // open input file, and associate with CAutoFile + FILE *file = fopen(pathDB.string().c_str(), "rb"); + CAutoFile filein(file, SER_DISK, CLIENT_VERSION); + if (filein.IsNull()) + { + error("%s: Failed to open file %s", __func__, pathDB.string()); + return FileError; + } + + // use file size to size memory buffer + int fileSize = boost::filesystem::file_size(pathDB); + int dataSize = fileSize - sizeof(uint256); + // Don't try to resize to a negative number if file is small + if (dataSize < 0) + dataSize = 0; + std::vector vchData; + vchData.resize(dataSize); + uint256 hashIn; + + // read data and checksum from file + try { + filein.read((char *)&vchData[0], dataSize); + filein >> hashIn; + } + catch (std::exception &e) { + error("%s: Deserialize or I/O error - %s", __func__, e.what()); + return HashReadError; + } + filein.fclose(); + + CDataStream ssObj(vchData, SER_DISK, CLIENT_VERSION); + + // verify stored checksum matches input data + uint256 hashTmp = Hash(ssObj.begin(), ssObj.end()); + if (hashIn != hashTmp) + { + error("%s: Checksum mismatch, data corrupted", __func__); + return IncorrectHash; + } + + + unsigned char pchMsgTmp[4]; + std::string strMagicMessageTmp; + try { + // de-serialize file header (file specific magic message) and .. + ssObj >> strMagicMessageTmp; + + // ... verify the message matches predefined one + if (strMagicMessage != strMagicMessageTmp) + { + error("%s: Invalid magic message", __func__); + return IncorrectMagicMessage; + } + + + // de-serialize file header (network specific magic number) and .. + ssObj >> FLATDATA(pchMsgTmp); + + // ... verify the network matches ours + if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) + { + error("%s: Invalid network magic number", __func__); + return IncorrectMagicNumber; + } + + // de-serialize data into T object + ssObj >> objToLoad; + } + catch (std::exception &e) { + objToLoad.Clear(); + error("%s: Deserialize or I/O error - %s", __func__, e.what()); + return IncorrectFormat; + } + + LogPrintf("Loaded info from %s %dms\n", strFilename, GetTimeMillis() - nStart); + LogPrintf(" %s\n", objToLoad.ToString()); + if(!fDryRun) { + LogPrintf("%s: Cleaning....\n", __func__); + objToLoad.CheckAndRemove(); + LogPrintf(" %s\n", objToLoad.ToString()); + } + + return Ok; + } + + +public: + CFlatDB(std::string strFilenameIn, std::string strMagicMessageIn) + { + pathDB = GetDataDir() / strFilenameIn; + strFilename = strFilenameIn; + strMagicMessage = strMagicMessageIn; + } + + bool Load(T& objToLoad) + { + LogPrintf("Reading info from %s...\n", strFilename); + ReadResult readResult = Read(objToLoad); + if (readResult == FileError) + LogPrintf("Missing file %s, will try to recreate\n", strFilename); + else if (readResult != Ok) + { + LogPrintf("Error reading %s: ", strFilename); + if(readResult == IncorrectFormat) + { + LogPrintf("%s: Magic is ok but data has invalid format, will try to recreate\n", __func__); + } + else { + LogPrintf("%s: File format is unknown or invalid, please fix it manually\n", __func__); + // program should exit with an error + return false; + } + } + return true; + } + + bool Dump(T& objToSave) + { + int64_t nStart = GetTimeMillis(); + + LogPrintf("Verifying %s format...\n", strFilename); + T tmpObjToLoad; + ReadResult readResult = Read(tmpObjToLoad, true); + + // there was an error and it was not an error on file opening => do not proceed + if (readResult == FileError) + LogPrintf("Missing file %s, will try to recreate\n", strFilename); + else if (readResult != Ok) + { + LogPrintf("Error reading %s: ", strFilename); + if(readResult == IncorrectFormat) + LogPrintf("%s: Magic is ok but data has invalid format, will try to recreate\n", __func__); + else + { + LogPrintf("%s: File format is unknown or invalid, please fix it manually\n", __func__); + return false; + } + } + + LogPrintf("Writting info to %s...\n", strFilename); + Write(objToSave); + LogPrintf("%s dump finished %dms\n", strFilename, GetTimeMillis() - nStart); + + return true; + } + +}; + + +#endif \ No newline at end of file diff --git a/src/init.cpp b/src/init.cpp index f766667136..7583853f7d 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +70,8 @@ #include "znodeman.h" #include "znodeconfig.h" #include "netfulfilledman.h" +#include "flat-database.h" +#include "instantx.h" #include "spork.h" #if ENABLE_ZMQ @@ -1627,7 +1630,8 @@ bool AppInit2(boost::thread_group &threadGroup, CScheduler &scheduler) { std::vector vImportFiles; if (mapArgs.count("-loadblock")) { - BOOST_FOREACH(const std::string &strFile, mapMultiArgs["-loadblock"]) + BOOST_FOREACH( + const std::string &strFile, mapMultiArgs["-loadblock"]) vImportFiles.push_back(strFile); } @@ -1663,35 +1667,138 @@ bool AppInit2(boost::thread_group &threadGroup, CScheduler &scheduler) { // Generate coins in the background GenerateBitcoins(GetBoolArg("-gen", DEFAULT_GENERATE), GetArg("-genproclimit", DEFAULT_GENERATE_THREADS), chainparams); -// ********************************************************* Step 11a: setup PrivateSend - fZNode = GetBoolArg("-znode", false); - if((fZNode || znodeConfig.getCount() > -1) && fTxIndex == false) { - return InitError("Enabling Znode support requires turning on transaction indexing." + // ********************************************************* Step 11a: setup PrivateSend + fZNode = GetBoolArg("-masternode", false); + + LogPrintf("znodeConfig.getCount(): %s\n", znodeConfig.getCount()); + + if ((fZNode || znodeConfig.getCount() > 0) && !fTxIndex) { + return InitError("Enabling Masternode support requires turning on transaction indexing." "Please add txindex=1 to your configuration and start with -reindex"); } - if(fZNode) { + if (fZNode) { LogPrintf("ZNODE:\n"); - if(!GetArg("-znodeaddr", "").empty()) { - // Hot znode (either local or remote) should get its address in - // CActiveZnode::ManageState() automatically and no longer relies on znodeaddr. + if (!GetArg("-znodeaddr", "").empty()) { + // Hot masternode (either local or remote) should get its address in + // CActiveMasternode::ManageState() automatically and no longer relies on masternodeaddr. return InitError(_("znodeaddr option is deprecated. Please use znode.conf to manage your remote znodes.")); } - std::string strZNodePrivKey = GetArg("-znodeprivkey", ""); - if(!strZNodePrivKey.empty()) { - if(!darkSendSigner.GetKeysFromSecret(strZNodePrivKey, activeZnode.keyZnode, activeZnode.pubKeyZnode)) + std::string strMasterNodePrivKey = GetArg("-masternodeprivkey", ""); + if (!strMasterNodePrivKey.empty()) { + if (!darkSendSigner.GetKeysFromSecret(strMasterNodePrivKey, activeZnode.keyZnode, + activeZnode.pubKeyZnode)) return InitError(_("Invalid znodeprivkey. Please see documenation.")); LogPrintf(" pubKeyZnode: %s\n", CBitcoinAddress(activeZnode.pubKeyZnode.GetID()).ToString()); } else { - return InitError(_("You must specify a znodeprivkey in the configuration. Please see documentation for help.")); + return InitError( + _("You must specify a masternodeprivkey in the configuration. Please see documentation for help.")); + } + } + + LogPrintf("Using masternode config file %s\n", GetZnodeConfigFile().string()); + + if (GetBoolArg("-mnconflock", true) && pwalletMain && (znodeConfig.getCount() > 0)) { + LOCK(pwalletMain->cs_wallet); + LogPrintf("Locking Znodes:\n"); + uint256 mnTxHash; + int outputIndex; + BOOST_FOREACH(CZnodeConfig::CZnodeEntry mne, znodeConfig.getEntries()) { + mnTxHash.SetHex(mne.getTxHash()); + outputIndex = boost::lexical_cast(mne.getOutputIndex()); + COutPoint outpoint = COutPoint(mnTxHash, outputIndex); + // don't lock non-spendable outpoint (i.e. it's already spent or it's not from this wallet at all) + if (pwalletMain->IsMine(CTxIn(outpoint)) != ISMINE_SPENDABLE) { + LogPrintf(" %s %s - IS NOT SPENDABLE, was not locked\n", mne.getTxHash(), mne.getOutputIndex()); + continue; + } + pwalletMain->LockCoin(outpoint); + LogPrintf(" %s %s - locked successfully\n", mne.getTxHash(), mne.getOutputIndex()); + } + } + + + nLiquidityProvider = GetArg("-liquidityprovider", nLiquidityProvider); + nLiquidityProvider = std::min(std::max(nLiquidityProvider, 0), 100); + darkSendPool.SetMinBlockSpacing(nLiquidityProvider * 15); + + fEnablePrivateSend = GetBoolArg("-enableprivatesend", 0); + fPrivateSendMultiSession = GetBoolArg("-privatesendmultisession", DEFAULT_PRIVATESEND_MULTISESSION); + nPrivateSendRounds = GetArg("-privatesendrounds", DEFAULT_PRIVATESEND_ROUNDS); + nPrivateSendRounds = std::min(std::max(nPrivateSendRounds, 2), nLiquidityProvider ? 99999 : 16); + nPrivateSendAmount = GetArg("-privatesendamount", DEFAULT_PRIVATESEND_AMOUNT); + nPrivateSendAmount = std::min(std::max(nPrivateSendAmount, 2), 999999); + + fEnableInstantSend = GetBoolArg("-enableinstantsend", 1); + nInstantSendDepth = GetArg("-instantsenddepth", DEFAULT_INSTANTSEND_DEPTH); + nInstantSendDepth = std::min(std::max(nInstantSendDepth, 0), 60); + + //lite mode disables all Masternode and Darksend related functionality + fLiteMode = GetBoolArg("-litemode", false); + if (fZNode && fLiteMode) { + return InitError("You can not start a masternode in litemode"); + } + + LogPrintf("fLiteMode %d\n", fLiteMode); + LogPrintf("nInstantSendDepth %d\n", nInstantSendDepth); + LogPrintf("PrivateSend rounds %d\n", nPrivateSendRounds); + LogPrintf("PrivateSend amount %d\n", nPrivateSendAmount); + + darkSendPool.InitDenominations(); + + // ********************************************************* Step 11b: Load cache data + + // LOAD SERIALIZED DAT FILES INTO DATA CACHES FOR INTERNAL USE + + uiInterface.InitMessage(_("Loading znode cache...")); + CFlatDB flatdb1("mncache.dat", "magicMasternodeCache"); + if (!flatdb1.Load(mnodeman)) { + return InitError("Failed to load znode cache from mncache.dat"); + } + + if (mnodeman.size()) { + uiInterface.InitMessage(_("Loading masternode payment cache...")); + CFlatDB flatdb2("mnpayments.dat", "magicMasternodePaymentsCache"); + if (!flatdb2.Load(mnpayments)) { + return InitError("Failed to load znode payments cache from mnpayments.dat"); } + +// uiInterface.InitMessage(_("Loading governance cache...")); +// CFlatDB flatdb3("governance.dat", "magicGovernanceCache"); +// if(!flatdb3.Load(governance)) { +// return InitError("Failed to load governance cache from governance.dat"); +// } +// governance.InitOnLoad(); + } else { + uiInterface.InitMessage(_("Masternode cache is empty, skipping payments and governance cache...")); } - LogPrintf("Using znode config file %s\n", GetZnodeConfigFile().string()); -// fPrivateSendMultiSession = GetBoolArg("-privatesendmultisession", DEFAULT_PRIVATESEND_MULTISESSION); + + uiInterface.InitMessage(_("Loading fulfilled requests cache...")); + CFlatDB flatdb4("netfulfilled.dat", "magicFulfilledCache"); + if (!flatdb4.Load(netfulfilledman)) { + return InitError("Failed to load fulfilled requests cache from netfulfilled.dat"); + } + + // ********************************************************* Step 11c: update block tip in Dash modules + + // force UpdatedBlockTip to initialize pCurrentBlockIndex for DS, MN payments and budgets + // but don't call it directly to prevent triggering of other listeners like zmq etc. + // GetMainSignals().UpdatedBlockTip(chainActive.Tip()); + mnodeman.UpdatedBlockTip(chainActive.Tip()); + darkSendPool.UpdatedBlockTip(chainActive.Tip()); + mnpayments.UpdatedBlockTip(chainActive.Tip()); + znodeSync.UpdatedBlockTip(chainActive.Tip()); +// governance.UpdatedBlockTip(chainActive.Tip()); + + // ********************************************************* Step 11d: start dash-privatesend thread + + threadGroup.create_thread(boost::bind(&ThreadCheckDarkSendPool)); + + // ********************************************************* Step 12: finished diff --git a/src/key.cpp b/src/key.cpp index 79023566c3..2607a5b61d 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -12,6 +12,7 @@ #include #include +#include static secp256k1_context* secp256k1_context_sign = NULL; diff --git a/src/libzerocoin/Coin.cpp b/src/libzerocoin/Coin.cpp index 07bbcbffab..3aff696e31 100755 --- a/src/libzerocoin/Coin.cpp +++ b/src/libzerocoin/Coin.cpp @@ -11,9 +11,23 @@ **/ #include +#include #include "Zerocoin.h" namespace libzerocoin { +secp256k1_context* init_ctx() { + secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + unsigned char seed[32]; + if (RAND_bytes(seed, sizeof(seed)) != 1) { + throw ZerocoinException("Unable to generate randomness for context"); + } + if (secp256k1_context_randomize(ctx, seed) != 1) { + throw ZerocoinException("Unable to randomize context"); + }; + return ctx; +} +// global context +secp256k1_context* ctx = init_ctx(); //PublicCoin class PublicCoin::PublicCoin(const Params* p): @@ -81,14 +95,52 @@ const Bignum& PrivateCoin::getRandomness() const { return this->randomness; } +const unsigned char* PrivateCoin::getEcdsaSeckey() const { + return this->ecdsaSeckey; +} + +unsigned int PrivateCoin::getVersion() const { + return this->version; +} + void PrivateCoin::mintCoin(const CoinDenomination denomination) { + + Bignum s; + // Repeat this process up to MAX_COINMINT_ATTEMPTS times until // we obtain a prime number - for(uint32_t attempt = 0; attempt < MAX_COINMINT_ATTEMPTS; attempt++) { + for (uint32_t attempt = 0; attempt < MAX_COINMINT_ATTEMPTS; attempt++) { + if (this->version == 2) { - // Generate a random serial number in the range 0...{q-1} where - // "q" is the order of the commitment group. - Bignum s = Bignum::randBignum(this->params->coinCommitmentGroup.groupOrder); + // Create a key pair + secp256k1_pubkey pubkey; + do { + if (RAND_bytes(this->ecdsaSeckey, sizeof(this->ecdsaSeckey)) + != 1) { + throw ZerocoinException("Unable to generate randomness"); + } + } while (!secp256k1_ec_pubkey_create(ctx, &pubkey, + this->ecdsaSeckey)); + + std::vector pubkey_hash(32, 0); + + static const unsigned char one[32] = { 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; + + // We use secp256k1_ecdh instead of secp256k1_serialize_pubkey to avoid a timing channel. + int ignored_ret = secp256k1_ecdh(ctx, &pubkey_hash[0], &pubkey, + &one[0]); + + // Hash the public key in the group to obtain a serial number + s = serialNumberFromSerializedPublicKey(pubkey_hash); + } else { + // Generate a random serial number in the range 0...{q-1} where + // "q" is the order of the commitment group. + s = Bignum::randBignum( + this->params->coinCommitmentGroup.groupOrder); + } // Generate a Pedersen commitment to the serial number "s" Commitment coin(¶ms->coinCommitmentGroup, s); @@ -96,13 +148,16 @@ void PrivateCoin::mintCoin(const CoinDenomination denomination) { // Now verify that the commitment is a prime number // in the appropriate range. If not, we'll throw this coin // away and generate a new one. - if (coin.getCommitmentValue().isPrime(ZEROCOIN_MINT_PRIME_PARAM) && - coin.getCommitmentValue() >= params->accumulatorParams.minCoinValue && - coin.getCommitmentValue() <= params->accumulatorParams.maxCoinValue) { + if (coin.getCommitmentValue().isPrime(ZEROCOIN_MINT_PRIME_PARAM) + && coin.getCommitmentValue() + >= params->accumulatorParams.minCoinValue + && coin.getCommitmentValue() + <= params->accumulatorParams.maxCoinValue) { // Found a valid coin. Store it. this->serialNumber = s; this->randomness = coin.getRandomness(); - this->publicCoin = PublicCoin(params,coin.getCommitmentValue(), denomination); + this->publicCoin = PublicCoin(params, coin.getCommitmentValue(), + denomination); // Success! We're done. return; @@ -111,14 +166,43 @@ void PrivateCoin::mintCoin(const CoinDenomination denomination) { // We only get here if we did not find a coin within // MAX_COINMINT_ATTEMPTS. Throw an exception. - throw ZerocoinException("Unable to mint a new Zerocoin (too many attempts)"); + throw ZerocoinException( + "Unable to mint a new Zerocoin (too many attempts)"); } void PrivateCoin::mintCoinFast(const CoinDenomination denomination) { - // Generate a random serial number in the range 0...{q-1} where - // "q" is the order of the commitment group. - Bignum s = Bignum::randBignum(this->params->coinCommitmentGroup.groupOrder); + Bignum s; + + if(this->version == 2) { + + // Create a key pair + secp256k1_pubkey pubkey; + do { + if (RAND_bytes(this->ecdsaSeckey, sizeof(this->ecdsaSeckey)) != 1) { + throw ZerocoinException("Unable to generate randomness"); + } + }while (!secp256k1_ec_pubkey_create(ctx, &pubkey, this->ecdsaSeckey)); + + std::vector pubkey_hash(32, 0); + + static const unsigned char one[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + }; + + // We use secp256k1_ecdh instead of secp256k1_serialize_pubkey to avoid a timing channel. + int ignored_ret = secp256k1_ecdh(ctx, &pubkey_hash[0], &pubkey, &one[0]); + + // Hash the public key in the group to obtain a serial number + s = serialNumberFromSerializedPublicKey(pubkey_hash); + } else { + // Generate a random serial number in the range 0...{q-1} where + // "q" is the order of the commitment group. + s = Bignum::randBignum(this->params->coinCommitmentGroup.groupOrder); + } // Generate a random number "r" in the range 0...{q-1} Bignum r = Bignum::randBignum(this->params->coinCommitmentGroup.groupOrder); @@ -164,4 +248,28 @@ const PublicCoin& PrivateCoin::getPublicCoin() const { return this->publicCoin; } + +const Bignum PrivateCoin::serialNumberFromSerializedPublicKey(const std::vector &pub) { + if (pub.size() != 33) { + throw ZerocoinException( + "Wrong public key size. You must check the size before calling this function."); + } + + std::string zpts(ZEROCOIN_PUBLICKEY_TO_SERIALNUMBER); + std::vector pre(zpts.begin(), zpts.end()); + std::copy(pub.begin(), pub.end(), std::back_inserter(pre)); + uint160 hash; + CRIPEMD160().Write(&pre[0], pre.size()).Finalize(hash.begin()); + // We want the 160 least-significant bits of the pubkey to be the hash of the serial number. + // The remaining bits (incl. the sign bit) should be 0. + // Bignum reverses the bits when parsing a char vector (Bitcoin's hash byte order), + // so we put the hash at position 0 of the char vector. + // We need 1 additional byte to make sure that the sign bit is always 0. + std::vector hash_vch(160 / 8 + 1, 0); + hash_vch.insert(hash_vch.end(), hash.begin(), hash.end()); + //RIPEMD160(&pre[0], pre.size(), &hash[0]); + Bignum s(hash_vch); + return s; +} + } /* namespace libzerocoin */ diff --git a/src/libzerocoin/Coin.h b/src/libzerocoin/Coin.h index c172a36843..4d24a44b33 100755 --- a/src/libzerocoin/Coin.h +++ b/src/libzerocoin/Coin.h @@ -12,6 +12,8 @@ #ifndef COIN_H_ #define COIN_H_ +#include +#include #include "bitcoin_bignum/bignum.h" #include "Params.h" namespace libzerocoin { @@ -98,6 +100,9 @@ class PrivateCoin { const PublicCoin& getPublicCoin() const; const Bignum& getSerialNumber() const; const Bignum& getRandomness() const; + const unsigned char* getEcdsaSeckey() const; + unsigned int getVersion() const; + static const Bignum serialNumberFromSerializedPublicKey(const std::vector &pub); void setPublicCoin(PublicCoin p){ publicCoin = p; @@ -110,13 +115,20 @@ class PrivateCoin { void setSerialNumber(Bignum n){ serialNumber = n; } - ADD_SERIALIZE_METHODS; + + void setVersion(unsigned int nVersion){ + version = nVersion; + }; template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(publicCoin); READWRITE(randomness); READWRITE(serialNumber); + if(version == 2){ + READWRITE(version); + READWRITE(ecdsaSeckey); + } } private: @@ -124,6 +136,8 @@ class PrivateCoin { PublicCoin publicCoin; Bignum randomness; Bignum serialNumber; + unsigned int version = 0; + unsigned char ecdsaSeckey[32]; /** * @brief Mint a new coin. diff --git a/src/libzerocoin/CoinSpend.cpp b/src/libzerocoin/CoinSpend.cpp index 68c2387f95..e18dfa9393 100755 --- a/src/libzerocoin/CoinSpend.cpp +++ b/src/libzerocoin/CoinSpend.cpp @@ -21,7 +21,9 @@ CoinSpend::CoinSpend(const Params* p, const PrivateCoin& coin, coinSerialNumber((coin.getSerialNumber())), accumulatorPoK(&p->accumulatorParams), serialNumberSoK(p), - commitmentPoK(&p->serialNumberSoKCommitmentGroup, &p->accumulatorParams.accumulatorPoKCommitmentGroup) { + commitmentPoK(&p->serialNumberSoKCommitmentGroup, &p->accumulatorParams.accumulatorPoKCommitmentGroup), + ecdsaPubkey(33, 0), + ecdsaSignature(64, 0) { // Sanity check: let's verify that the Witness is valid with respect to // the coin and Accumulator provided. @@ -51,7 +53,25 @@ CoinSpend::CoinSpend(const Params* p, const PrivateCoin& coin, // 4. Proves that the coin is correct w.r.t. serial number and hidden coin secret // (This proof is bound to the coin 'metadata', i.e., transaction hash) - this->serialNumberSoK = SerialNumberSignatureOfKnowledge(p, coin, fullCommitmentToCoinUnderSerialParams, signatureHash(m)); + uint256 metahash = signatureHash(m); + this->serialNumberSoK = SerialNumberSignatureOfKnowledge(p, coin, fullCommitmentToCoinUnderSerialParams, metahash); + + if(coin.getVersion() == 2){ + // 5. Sign the transaction under the public key associate with the serial number. + secp256k1_pubkey pubkey; + size_t len = 33; + secp256k1_ecdsa_signature sig; + + // TODO timing channel, since secp256k1_ec_pubkey_serialize does not expect its output to be secret. + // See main_impl.h of ecdh module on secp256k1 + if (!secp256k1_ec_pubkey_create(ctx, &pubkey, coin.getEcdsaSeckey())) { + throw ZerocoinException("Invalid secret key"); + } + secp256k1_ec_pubkey_serialize(ctx, &this->ecdsaPubkey[0], &len, &pubkey, SECP256K1_EC_COMPRESSED); + + secp256k1_ecdsa_sign(ctx, &sig, metahash.begin(), coin.getEcdsaSeckey(), NULL, NULL); + secp256k1_ecdsa_signature_serialize_compact(ctx, &this->ecdsaSignature[0], &sig); + } } const Bignum&CoinSpend::getCoinSerialNumber() { @@ -63,19 +83,57 @@ CoinDenomination CoinSpend::getDenomination() const { } bool CoinSpend::Verify(const Accumulator& a, const SpendMetaData &m) const { + uint256 metahash = signatureHash(m); // Verify both of the sub-proofs using the given meta-data -// printf("CoinSpend.Verify"); - return (a.getDenomination() == this->denomination) - && commitmentPoK.Verify(serialCommitmentToCoinValue, accCommitmentToCoinValue) - && accumulatorPoK.Verify(a, accCommitmentToCoinValue) - && serialNumberSoK.Verify(coinSerialNumber, serialCommitmentToCoinValue, signatureHash(m)); + int ret = (a.getDenomination() == this->denomination) + && commitmentPoK.Verify(serialCommitmentToCoinValue, accCommitmentToCoinValue) + && accumulatorPoK.Verify(a, accCommitmentToCoinValue) + && serialNumberSoK.Verify(coinSerialNumber, serialCommitmentToCoinValue, metahash); + if (!ret) { + return false; + } + + + if(this->version != 2){ + return ret; + }else{ + int hashBits = 160; + Bignum hashMax(1); + hashMax <<= 160; + // Check if this is a coin that requires a signatures + if (coinSerialNumber < hashMax) { + // Check sizes + if (this->ecdsaPubkey.size() != 33 || this->ecdsaSignature.size() != 64) { + return false; + } + + // Recompute and compare hash of public key + if (coinSerialNumber != PrivateCoin::serialNumberFromSerializedPublicKey(ecdsaPubkey)) { + return false; + } + + // Verify signature + secp256k1_pubkey pubkey; + secp256k1_ecdsa_signature signature; + + if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, &this->ecdsaPubkey[0], 33)) { + return false; + } + secp256k1_ecdsa_signature_parse_compact(ctx, &signature, &this->ecdsaSignature[0]); + if (!secp256k1_ecdsa_verify(ctx, &signature, metahash.begin(), &pubkey)) { + return false; + } + } + + return true; + } + } -const arith_uint256 CoinSpend::signatureHash(const SpendMetaData &m) const { +const uint256 CoinSpend::signatureHash(const SpendMetaData &m) const { CHashWriter h(0,0); h << m << serialCommitmentToCoinValue << accCommitmentToCoinValue << commitmentPoK << accumulatorPoK; - arith_uint256 hx= UintToArith256(h.GetHash()); - return hx; + return h.GetHash(); } } /* namespace libzerocoin */ diff --git a/src/libzerocoin/CoinSpend.h b/src/libzerocoin/CoinSpend.h index 9a1f355375..1c9a8d801c 100755 --- a/src/libzerocoin/CoinSpend.h +++ b/src/libzerocoin/CoinSpend.h @@ -76,6 +76,10 @@ class CoinSpend { */ CoinDenomination getDenomination() const; + void setVersion(unsigned int nVersion){ + version = nVersion; + } + bool Verify(const Accumulator& a, const SpendMetaData &metaData) const; ADD_SERIALIZE_METHODS; @@ -88,17 +92,25 @@ class CoinSpend { READWRITE(accumulatorPoK); READWRITE(serialNumberSoK); READWRITE(commitmentPoK); + if(version == 2){ + READWRITE(version); + READWRITE(ecdsaPubkey); + READWRITE(ecdsaSignature); + } } private: const Params *params; - const arith_uint256 signatureHash(const SpendMetaData &m) const; + const uint256 signatureHash(const SpendMetaData &m) const; // Denomination is stored as an INT because storing // and enum raises amigiuities in the serialize code //FIXME if possible int denomination; + unsigned int version = 0; Bignum accCommitmentToCoinValue; Bignum serialCommitmentToCoinValue; Bignum coinSerialNumber; + std::vector ecdsaSignature; + std::vector ecdsaPubkey; AccumulatorProofOfKnowledge accumulatorPoK; SerialNumberSignatureOfKnowledge serialNumberSoK; CommitmentProofOfKnowledge commitmentPoK; diff --git a/src/libzerocoin/ParamGeneration.cpp b/src/libzerocoin/ParamGeneration.cpp index f71c61a738..eac0551018 100755 --- a/src/libzerocoin/ParamGeneration.cpp +++ b/src/libzerocoin/ParamGeneration.cpp @@ -249,8 +249,8 @@ namespace libzerocoin { // Calculate "p" and "q" and "domain_parameter_seed" from the // "seed" buffer above, using the procedure described in NIST // FIPS 186-3, Appendix A.1.2. - calculateGroupModulusAndOrder(seed, pLen, qLen, &(result.modulus), - &(result.groupOrder), &pSeed, &qSeed); + calculateGroupModulusAndOrder(seed, pLen, qLen, result.modulus, + result.groupOrder, &pSeed, &qSeed); // Calculate the generators "g", "h" using the process described in // NIST FIPS 186-3, Appendix A.2.3. This algorithm takes ("p", "q", @@ -348,7 +348,7 @@ namespace libzerocoin { /// primes "p" and "q". void calculateGroupModulusAndOrder(arith_uint256 seed, uint32_t pLen, uint32_t qLen, - Bignum *resultModulus, Bignum *resultGroupOrder, + Bignum &resultModulus, Bignum &resultGroupOrder, arith_uint256 *resultPseed, arith_uint256 *resultQseed) { // Verify that the seed length is >= qLen if (qLen > (sizeof(seed)) * 8) { @@ -365,7 +365,7 @@ namespace libzerocoin { // Result is the value "resultGroupOrder", "qseed" and "qgen_counter". arith_uint256 qseed; uint32_t qgen_counter; - *resultGroupOrder = generateRandomPrime(qLen, seed, &qseed, &qgen_counter); + resultGroupOrder = generateRandomPrime(qLen, seed, &qseed, &qgen_counter); // Using ⎡pLen / 2 + 1⎤ as the length and qseed as the input_seed, use the random prime // routine to obtain p0 , pseed, and pgen_counter. We pass exceptions upward. @@ -388,7 +388,7 @@ namespace libzerocoin { // t = ⎡x / (2 * resultGroupOrder * p0)⎤. // TODO: we don't have a ceiling function - Bignum t = x / (Bignum(2) * (*resultGroupOrder) * p0); + Bignum t = x / (Bignum(2) * resultGroupOrder * p0); // Now loop until we find a valid prime "p" or we fail due to // pgen_counter exceeding ((4*pLen) + old_counter). @@ -396,29 +396,29 @@ namespace libzerocoin { // If (2 * t * resultGroupOrder * p0 + 1) > 2^{pLen}, then // t = ⎡2^{pLen−1} / (2 * resultGroupOrder * p0)⎤. powerOfTwo = Bignum(2).pow(pLen); - Bignum prod = (Bignum(2) * t * (*resultGroupOrder) * p0) + Bignum(1); + Bignum prod = (Bignum(2) * t * resultGroupOrder * p0) + Bignum(1); if (prod > powerOfTwo) { // TODO: implement a ceil function - t = Bignum(2).pow(pLen - 1) / (Bignum(2) * (*resultGroupOrder) * p0); + t = Bignum(2).pow(pLen - 1) / (Bignum(2) * resultGroupOrder * p0); } // Compute a candidate prime resultModulus = 2tqp0 + 1. - *resultModulus = (Bignum(2) * t * (*resultGroupOrder) * p0) + Bignum(1); + resultModulus = (Bignum(2) * t * resultGroupOrder * p0) + Bignum(1); // Verify that resultModulus is prime. First generate a pseudorandom integer "a". Bignum a = generateIntegerFromSeed(pLen, pseed, &iterations); pseed += iterations + 1; // Set a = 2 + (a mod (resultModulus–3)). - a = Bignum(2) + (a % ((*resultModulus) - Bignum(3))); + a = Bignum(2) + (a % (resultModulus - Bignum(3))); // Set z = a^{2 * t * resultGroupOrder} mod resultModulus - Bignum z = a.pow_mod(Bignum(2) * t * (*resultGroupOrder), (*resultModulus)); + Bignum z = a.pow_mod(Bignum(2) * t * resultGroupOrder, resultModulus); // If GCD(z–1, resultModulus) == 1 AND (z^{p0} mod resultModulus == 1) // then we have found our result. Return. - if ((resultModulus->gcd(z - Bignum(1))).isOne() && - (z.pow_mod(p0, (*resultModulus))).isOne()) { + if ((resultModulus.gcd(z - Bignum(1))).isOne() && + (z.pow_mod(p0, resultModulus)).isOne()) { // Success! Return the seeds and primes. *resultPseed = pseed; *resultQseed = qseed; diff --git a/src/libzerocoin/ParamGeneration.h b/src/libzerocoin/ParamGeneration.h index b64bac0c13..d72b5a2a9b 100755 --- a/src/libzerocoin/ParamGeneration.h +++ b/src/libzerocoin/ParamGeneration.h @@ -37,7 +37,7 @@ arith_uint256 calculateGeneratorSeed(arith_uint256 seed, arith_uint256 pSeed, ar arith_uint256 calculateHash(arith_uint256 input); IntegerGroupParams deriveIntegerGroupParams(arith_uint256 seed, uint32_t pLen, uint32_t qLen); IntegerGroupParams deriveIntegerGroupFromOrder(Bignum &groupOrder); -void calculateGroupModulusAndOrder(arith_uint256 seed, uint32_t pLen, uint32_t qLen, Bignum *resultModulus, Bignum *resultGroupOrder, arith_uint256 *resultPseed, arith_uint256 *resultQseed); +void calculateGroupModulusAndOrder(arith_uint256 seed, uint32_t pLen, uint32_t qLen, Bignum &resultModulus, Bignum &resultGroupOrder, arith_uint256 *resultPseed, arith_uint256 *resultQseed); Bignum calculateGroupGenerator(arith_uint256 seed, arith_uint256 pSeed, arith_uint256 qSeed, Bignum modulus, Bignum groupOrder, uint32_t index); Bignum generateRandomPrime(uint32_t primeBitLen, arith_uint256 in_seed, arith_uint256 *out_seed, uint32_t *prime_gen_counter); Bignum generateIntegerFromSeed(uint32_t numBits, arith_uint256 seed, uint32_t *numIterations); diff --git a/src/libzerocoin/SerialNumberSignatureOfKnowledge.cpp b/src/libzerocoin/SerialNumberSignatureOfKnowledge.cpp index aa9c80de9b..3d80c9ce97 100755 --- a/src/libzerocoin/SerialNumberSignatureOfKnowledge.cpp +++ b/src/libzerocoin/SerialNumberSignatureOfKnowledge.cpp @@ -16,7 +16,7 @@ namespace libzerocoin { SerialNumberSignatureOfKnowledge::SerialNumberSignatureOfKnowledge(const Params* p): params(p) { } -SerialNumberSignatureOfKnowledge::SerialNumberSignatureOfKnowledge(const Params* p, const PrivateCoin& coin, const Commitment& commitmentToCoin, arith_uint256 msghash) +SerialNumberSignatureOfKnowledge::SerialNumberSignatureOfKnowledge(const Params* p, const PrivateCoin& coin, const Commitment& commitmentToCoin, uint256 msghash) :params(p), s_notprime(p->zkp_iterations), sprime(p->zkp_iterations) { // Sanity check: verify that the order of the "accumulatedValueCommitmentGroup" is @@ -101,11 +101,18 @@ inline Bignum SerialNumberSignatureOfKnowledge::challengeCalculation(const Bignu } bool SerialNumberSignatureOfKnowledge::Verify(const Bignum& coinSerialNumber, const Bignum& valueOfCommitmentToCoin, - const arith_uint256 msghash) const { + const uint256 msghash) const { Bignum a = params->coinCommitmentGroup.g; Bignum b = params->coinCommitmentGroup.h; Bignum g = params->serialNumberSoKCommitmentGroup.g; Bignum h = params->serialNumberSoKCommitmentGroup.h; + + // Make sure that the serial number has a unique representation + if (coinSerialNumber < 0 || coinSerialNumber >= params->coinCommitmentGroup.groupOrder){ + return false; + } + + CHashWriter hasher(0,0); hasher << *params << valueOfCommitmentToCoin < diff --git a/src/libzerocoin/SpendMetaData.cpp b/src/libzerocoin/SpendMetaData.cpp index 08ef1b2f79..966ea054c4 100755 --- a/src/libzerocoin/SpendMetaData.cpp +++ b/src/libzerocoin/SpendMetaData.cpp @@ -14,6 +14,6 @@ namespace libzerocoin { -SpendMetaData::SpendMetaData(arith_uint256 accumulatorId, arith_uint256 txHash): accumulatorId(accumulatorId), txHash(txHash) {} +SpendMetaData::SpendMetaData(arith_uint256 accumulatorId, uint256 txHash): accumulatorId(accumulatorId), txHash(txHash) {} } /* namespace libzerocoin */ diff --git a/src/libzerocoin/SpendMetaData.h b/src/libzerocoin/SpendMetaData.h index 002bb24c36..3ba2a69ff5 100755 --- a/src/libzerocoin/SpendMetaData.h +++ b/src/libzerocoin/SpendMetaData.h @@ -29,7 +29,7 @@ class SpendMetaData { * @param accumulatorId hash of block containing accumulator * @param txHash hash of transaction */ - SpendMetaData(arith_uint256 accumulatorId, arith_uint256 txHash); + SpendMetaData(arith_uint256 accumulatorId, uint256 txHash); /** * The hash of the block containing the accumulator CoinSpend @@ -39,7 +39,7 @@ class SpendMetaData { /**Contains the hash of the rest of transaction * spending a zerocoin (excluding the coinspend proof) */ - arith_uint256 txHash; // The Hash of the rest of the transaction the spend proof is n. + uint256 txHash; // The Hash of the rest of the transaction the spend proof is n. // Allows us to sign the transaction. ADD_SERIALIZE_METHODS; template diff --git a/src/libzerocoin/Tutorial.cpp b/src/libzerocoin/Tutorial.cpp index 21437e864d..96e1131a10 100755 --- a/src/libzerocoin/Tutorial.cpp +++ b/src/libzerocoin/Tutorial.cpp @@ -23,10 +23,8 @@ using namespace std; #define DUMMY_TRANSACTION_HASH 0 // in real life these would be uint256 hashes #define DUMMY_ACCUMULATOR_ID 0 // in real life these would be uint256 hashes -// -// We generated this for testing only. Don't use it in production! -// -#define TUTORIAL_TEST_MODULUS "a8852ebf7c49f01cd196e35394f3b74dd86283a07f57e0a262928e7493d4a3961d93d93c90ea3369719641d626d28b9cddc6d9307b9aabdbffc40b6d6da2e329d079b4187ff784b2893d9f53e9ab913a04ff02668114695b07d8ce877c4c8cac1b12b9beff3c51294ebe349eca41c24cd32a6d09dd1579d3947e5c4dcc30b2090b0454edb98c6336e7571db09e0fdafbd68d8f0470223836e90666a5b143b73b9cd71547c917bf24c0efc86af2eba046ed781d9acb05c80f007ef5a0a5dfca23236f37e698e8728def12554bc80f294f71c040a88eff144d130b24211016a97ce0f5fe520f477e555c9997683d762aff8bd1402ae6938dd5c994780b1bf6aa7239e9d8101630ecfeaa730d2bbc97d39beb057f016db2e28bf12fab4989c0170c2593383fd04660b5229adcd8486ba78f6cc1b558bcd92f344100dff239a8c00dbc4c2825277f24bdd04475bcc9a8c39fd895eff97c1967e434effcb9bd394e0577f4cf98c30d9e6b54cd47d6e447dcf34d67e48e4421691dbe4a7d9bd503abb9" +//This modulus is the actual modulus used in Zcoin; it is RSA-2048 from the RSA Factoring Challenge. +#define ZCOIN_MODULUS "c7970ceedcc3b0754490201a7aa613cd73911081c790f5f1a8726f463550bb5b7ff0db8e1ea1189ec72f93d1650011bd721aeeacc2acde32a04107f0648c2813a31f5b0b7765ff8b44b4b6ffc93384b646eb09c7cf5e8592d40ea33c80039f35b4f14a04b51f7bfd781be4d1673164ba8eb991c2c4d730bbbe35f592bdef524af7e8daefd26c66fc02c479af89d64d373f442709439de66ceb955f3ea37d5159f6135809f85334b5cb1813addc80cd05609f10ac6a95ad65872c909525bdad32bc729592642920f24c61dc5b3c3b7923e56b16a4d9d373d8721f24a3fc0f1b3131f55615172866bccc30f95054c824e733a5eb6817f7bc16399d48c6361cc7e5" // // The following routine exercises most of the core functions of @@ -60,12 +58,11 @@ ZerocoinTutorial() // the included 'paramgen' utility. /********************************************************************/ - // Load a test modulus from our hardcoded string (above) - Bignum testModulus; - testModulus.SetHex(std::string(TUTORIAL_TEST_MODULUS)); + Bignum modulus; + modulus.SetHex(std::string(ZCOIN_MODULUS)); // Set up the Zerocoin Params object - libzerocoin::Params* params = new libzerocoin::Params(testModulus); + libzerocoin::Params* params = new libzerocoin::Params(modulus); cout << "Successfully loaded parameters." << endl; diff --git a/src/libzerocoin/Zerocoin.h b/src/libzerocoin/Zerocoin.h index 508baef2d2..9fc551d9a7 100755 --- a/src/libzerocoin/Zerocoin.h +++ b/src/libzerocoin/Zerocoin.h @@ -14,6 +14,9 @@ #define ZEROCOIN_H_ #include +#include +#include +#include #define ZEROCOIN_DEFAULT_SECURITYLEVEL 80 #define ZEROCOIN_MIN_SECURITY_LEVEL 80 @@ -29,6 +32,7 @@ #define ZEROCOIN_COMMITMENT_EQUALITY_PROOF "COMMITMENT_EQUALITY_PROOF" #define ZEROCOIN_ACCUMULATOR_PROOF "ACCUMULATOR_PROOF" #define ZEROCOIN_SERIALNUMBER_PROOF "SERIALNUMBER_PROOF" +#define ZEROCOIN_PUBLICKEY_TO_SERIALNUMBER "PUBLICKEY_TO_SERIALNUMBER" // Activate multithreaded mode for proof verification #define ZEROCOIN_THREADING 1 @@ -45,6 +49,11 @@ class ZerocoinException : public std::runtime_error explicit ZerocoinException(const std::string& str) : std::runtime_error(str) {} }; +namespace libzerocoin { + // Defined in coin.cpp + extern secp256k1_context* ctx; +} + #include "../serialize.h" #include "bitcoin_bignum/bignum.h" #include "../hash.h" diff --git a/src/libzerocoin/bitcoin_bignum/bignum.h b/src/libzerocoin/bitcoin_bignum/bignum.h index 2154681cf6..817649545b 100755 --- a/src/libzerocoin/bitcoin_bignum/bignum.h +++ b/src/libzerocoin/bitcoin_bignum/bignum.h @@ -20,7 +20,6 @@ class bignum_error : public std::runtime_error explicit bignum_error(const std::string& str) : std::runtime_error(str) {} }; - /** RAII encapsulated BN_CTX (OpenSSL bignum context) */ class CAutoBN_CTX { @@ -49,54 +48,66 @@ class CAutoBN_CTX }; -/** C++ wrapper for BIGNUM (OpenSSL bignum) */ -class CBigNum : public BIGNUM +/** C++ wrapper for BIGNUM (OpenSSL bignum) */class CBigNum { +protected: + BIGNUM *bn; + + void init() + { + bn = BN_new(); + } + public: CBigNum() { - BN_init(this); + init(); } CBigNum(const CBigNum& b) { - BN_init(this); - if (!BN_copy(this, &b)) + init(); + if (!BN_copy(bn, &b)) { - BN_clear_free(this); + BN_clear_free(bn); throw bignum_error("CBigNum::CBigNum(const CBigNum&) : BN_copy failed"); } } CBigNum& operator=(const CBigNum& b) { - if (!BN_copy(this, &b)) + if (!BN_copy(bn, &b)) throw bignum_error("CBigNum::operator= : BN_copy failed"); return (*this); } ~CBigNum() { - BN_clear_free(this); + BN_clear_free(bn); + } + + BIGNUM *operator &() const + { + return bn; } //CBigNum(char n) is not portable. Use 'signed char' or 'unsigned char'. - CBigNum(signed char n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } - CBigNum(short n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } - CBigNum(int n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } -// CBigNum(long n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); } - CBigNum(int64_t n) { BN_init(this); setint64(n); } - CBigNum(unsigned char n) { BN_init(this); setulong(n); } - CBigNum(unsigned short n) { BN_init(this); setulong(n); } - CBigNum(unsigned int n) { BN_init(this); setulong(n); } -// CBigNum(unsigned long n) { BN_init(this); setulong(n); } - CBigNum(uint64_t n) { BN_init(this); setuint64(n); } - explicit CBigNum(arith_uint256 n) { BN_init(this); setuint256(n); } - explicit CBigNum(uint256 n) { arith_uint256 m = UintToArith256(n); BN_init(this); setuint256(m); } + CBigNum(signed char n) { init(); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(short n) { init(); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(int n) { init(); if (n >= 0) setulong(n); else setint64(n); } +// CBigNum(long n) { init(); if (n >= 0) setulong(n); else setint64(n); } + CBigNum(int64_t n) { init(); setint64(n); } + CBigNum(unsigned char n) { init(); setulong(n); } + CBigNum(unsigned short n) { init(); setulong(n); } + CBigNum(unsigned int n) { init(); setulong(n); } +// CBigNum(unsigned long n) { init(); setulong(n); } + CBigNum(uint64_t n) { init(); setuint64(n); } + explicit CBigNum(arith_uint256 n) { init(); setuint256(n); } + explicit CBigNum(uint256 n) { arith_uint256 m = UintToArith256(n); init(); setuint256(m); } explicit CBigNum(const std::vector& vch) { - BN_init(this); + init(); setvch(vch); } @@ -130,29 +141,29 @@ class CBigNum : public BIGNUM * @return the size */ int bitSize() const{ - return BN_num_bits(this); + return BN_num_bits(bn); } void setulong(unsigned long n) { - if (!BN_set_word(this, n)) + if (!BN_set_word(bn, n)) throw bignum_error("CBigNum conversion from unsigned long : BN_set_word failed"); } unsigned long getulong() const { - return BN_get_word(this); + return BN_get_word(bn); } unsigned int getuint() const { - return BN_get_word(this); + return BN_get_word(bn); } int getint() const { - unsigned long n = BN_get_word(this); - if (!BN_is_negative(this)) + unsigned long n = BN_get_word(bn); + if (!BN_is_negative(bn)) return (n > (unsigned long)std::numeric_limits::max() ? std::numeric_limits::max() : n); else return (n > (unsigned long)std::numeric_limits::max() ? std::numeric_limits::min() : -(int)n); @@ -200,7 +211,7 @@ class CBigNum : public BIGNUM pch[1] = (nSize >> 16) & 0xff; pch[2] = (nSize >> 8) & 0xff; pch[3] = (nSize) & 0xff; - BN_mpi2bn(pch, p - pch, this); + BN_mpi2bn(pch, p - pch, bn); } void setuint64(uint64_t n) @@ -227,7 +238,7 @@ class CBigNum : public BIGNUM pch[1] = (nSize >> 16) & 0xff; pch[2] = (nSize >> 8) & 0xff; pch[3] = (nSize) & 0xff; - BN_mpi2bn(pch, p - pch, this); + BN_mpi2bn(pch, p - pch, bn); } void setuint256(arith_uint256 n) @@ -255,18 +266,18 @@ class CBigNum : public BIGNUM pch[1] = (nSize >> 16) & 0xff; pch[2] = (nSize >> 8) & 0xff; pch[3] = (nSize >> 0) & 0xff; - BN_mpi2bn(pch, p - pch, this); + BN_mpi2bn(pch, p - pch, bn); } // uint256 getuint256() const // { // uint64_t x = 0; // uint256 n = x; -// unsigned int nSize = BN_bn2mpi(this, NULL); +// unsigned int nSize = BN_bn2mpi(bn, NULL); // if (nSize < 4) // return n; // std::vector vch(nSize); -// BN_bn2mpi(this, &vch[0]); +// BN_bn2mpi(bn, &vch[0]); // if (vch.size() > 4) // vch[4] &= 0x7f; // for (unsigned int i = 0, j = vch.size()-1; i < sizeof(n) && j >= 4; i++, j--) @@ -286,16 +297,16 @@ class CBigNum : public BIGNUM vch2[3] = (nSize >> 0) & 0xff; // swap data to big endian reverse_copy(vch.begin(), vch.end(), vch2.begin() + 4); - BN_mpi2bn(&vch2[0], vch2.size(), this); + BN_mpi2bn(&vch2[0], vch2.size(), bn); } std::vector getvch() const { - unsigned int nSize = BN_bn2mpi(this, NULL); + unsigned int nSize = BN_bn2mpi(bn, NULL); if (nSize <= 4) return std::vector(); std::vector vch(nSize); - BN_bn2mpi(this, &vch[0]); + BN_bn2mpi(bn, &vch[0]); vch.erase(vch.begin(), vch.begin() + 4); reverse(vch.begin(), vch.end()); return vch; @@ -331,28 +342,28 @@ class CBigNum : public BIGNUM if (nSize <= 3) { nWord >>= 8*(3-nSize); - BN_set_word(this, nWord); + BN_set_word(bn, nWord); } else { - BN_set_word(this, nWord); - BN_lshift(this, this, 8*(nSize-3)); + BN_set_word(bn, nWord); + BN_lshift(bn, bn, 8*(nSize-3)); } - BN_set_negative(this, fNegative); + BN_set_negative(bn, fNegative); return *this; } unsigned int GetCompact() const { - unsigned int nSize = BN_num_bytes(this); + unsigned int nSize = BN_num_bytes(bn); unsigned int nCompact = 0; if (nSize <= 3) - nCompact = BN_get_word(this) << 8*(3-nSize); + nCompact = BN_get_word(bn) << 8*(3-nSize); else { - CBigNum bn; - BN_rshift(&bn, this, 8*(nSize-3)); - nCompact = BN_get_word(&bn); + CBigNum bn1; + BN_rshift(&bn1, bn, 8*(nSize-3)); + nCompact = BN_get_word(&bn1); } // The 0x00800000 bit denotes the sign. // Thus, if it is already set, divide the mantissa by 256 and increase the exponent. @@ -362,7 +373,7 @@ class CBigNum : public BIGNUM nSize++; } nCompact |= nSize << 24; - nCompact |= (BN_is_negative(this) ? 0x00800000 : 0); + nCompact |= (BN_is_negative(bn) ? 0x00800000 : 0); return nCompact; } @@ -435,21 +446,21 @@ class CBigNum : public BIGNUM CBigNum bnBase = nBase; CBigNum bn0 = 0; std::string str; - CBigNum bn = *this; - BN_set_negative(&bn, false); + CBigNum bn1 = *this; + BN_set_negative(&bn1, false); CBigNum dv; CBigNum rem; - if (BN_cmp(&bn, &bn0) == 0) + if (BN_cmp(&bn1, &bn0) == 0) return "0"; - while (BN_cmp(&bn, &bn0) > 0) + while (BN_cmp(&bn1, &bn0) > 0) { - if (!BN_div(&dv, &rem, &bn, &bnBase, pctx)) + if (!BN_div(&dv, &rem, &bn1, &bnBase, pctx)) throw bignum_error("CBigNum::ToString() : BN_div failed"); - bn = dv; + bn1 = dv; unsigned int c = rem.getulong(); str += "0123456789abcdef"[c]; } - if (BN_is_negative(this)) + if (BN_is_negative(bn)) str += "-"; reverse(str.begin(), str.end()); return str; @@ -496,7 +507,7 @@ class CBigNum : public BIGNUM CBigNum pow(const CBigNum& e) const { CAutoBN_CTX pctx; CBigNum ret; - if (!BN_exp(&ret, this, &e, pctx)) + if (!BN_exp(&ret, bn, &e, pctx)) throw bignum_error("CBigNum::pow : BN_exp failed"); return ret; } @@ -509,7 +520,7 @@ class CBigNum : public BIGNUM CBigNum mul_mod(const CBigNum& b, const CBigNum& m) const { CAutoBN_CTX pctx; CBigNum ret; - if (!BN_mod_mul(&ret, this, &b, &m, pctx)) + if (!BN_mod_mul(&ret, bn, &b, &m, pctx)) throw bignum_error("CBigNum::mul_mod : BN_mod_mul failed"); return ret; @@ -530,7 +541,7 @@ class CBigNum : public BIGNUM if (!BN_mod_exp(&ret, &inv, &posE, &m, pctx)) throw bignum_error("CBigNum::pow_mod: BN_mod_exp failed on negative exponent"); }else - if (!BN_mod_exp(&ret, this, &e, &m, pctx)) + if (!BN_mod_exp(&ret, bn, &e, &m, pctx)) throw bignum_error("CBigNum::pow_mod : BN_mod_exp failed"); return ret; @@ -545,7 +556,7 @@ class CBigNum : public BIGNUM CBigNum inverse(const CBigNum& m) const { CAutoBN_CTX pctx; CBigNum ret; - if (!BN_mod_inverse(&ret, this, &m, pctx)) + if (!BN_mod_inverse(&ret, bn, &m, pctx)) throw bignum_error("CBigNum::inverse*= :BN_mod_inverse"); return ret; } @@ -571,7 +582,7 @@ class CBigNum : public BIGNUM CBigNum gcd( const CBigNum& b) const{ CAutoBN_CTX pctx; CBigNum ret; - if (!BN_gcd(&ret, this, &b, pctx)) + if (!BN_gcd(&ret, bn, &b, pctx)) throw bignum_error("CBigNum::gcd*= :BN_gcd"); return ret; } @@ -584,7 +595,7 @@ class CBigNum : public BIGNUM */ bool isPrime(const int checks=BN_prime_checks) const { CAutoBN_CTX pctx; - int ret = BN_is_prime(this, checks, NULL, pctx, NULL); + int ret = BN_is_prime_ex(bn, checks, pctx, NULL); if(ret < 0){ throw bignum_error("CBigNum::isPrime :BN_is_prime"); } @@ -592,19 +603,19 @@ class CBigNum : public BIGNUM } bool isOne() const { - return BN_is_one(this); + return BN_is_one(bn); } bool operator!() const { - return BN_is_zero(this); + return BN_is_zero(bn); } CBigNum& operator+=(const CBigNum& b) { - if (!BN_add(this, this, &b)) + if (!BN_add(bn, bn, &b)) throw bignum_error("CBigNum::operator+= : BN_add failed"); return *this; } @@ -618,7 +629,7 @@ class CBigNum : public BIGNUM CBigNum& operator*=(const CBigNum& b) { CAutoBN_CTX pctx; - if (!BN_mul(this, this, &b, pctx)) + if (!BN_mul(bn, bn, &b, pctx)) throw bignum_error("CBigNum::operator*= : BN_mul failed"); return *this; } @@ -637,7 +648,7 @@ class CBigNum : public BIGNUM CBigNum& operator<<=(unsigned int shift) { - if (!BN_lshift(this, this, shift)) + if (!BN_lshift(bn, bn, shift)) throw bignum_error("CBigNum:operator<<= : BN_lshift failed"); return *this; } @@ -648,13 +659,13 @@ class CBigNum : public BIGNUM // if built on ubuntu 9.04 or 9.10, probably depends on version of OpenSSL CBigNum a = 1; a <<= shift; - if (BN_cmp(&a, this) > 0) + if (BN_cmp(&a, bn) > 0) { *this = 0; return *this; } - if (!BN_rshift(this, this, shift)) + if (!BN_rshift(bn, bn, shift)) throw bignum_error("CBigNum:operator>>= : BN_rshift failed"); return *this; } @@ -663,7 +674,7 @@ class CBigNum : public BIGNUM CBigNum& operator++() { // prefix operator - if (!BN_add(this, this, BN_value_one())) + if (!BN_add(bn, bn, BN_value_one())) throw bignum_error("CBigNum::operator++ : BN_add failed"); return *this; } @@ -680,7 +691,7 @@ class CBigNum : public BIGNUM { // prefix operator CBigNum r; - if (!BN_sub(&r, this, BN_value_one())) + if (!BN_sub(&r, bn, BN_value_one())) throw bignum_error("CBigNum::operator-- : BN_sub failed"); *this = r; return *this; diff --git a/src/main.cpp b/src/main.cpp index 01a5c29366..47aed1057d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -42,6 +42,12 @@ #include "versionbits.h" #include "definition.h" +#include "darksend.h" +#include "instantx.h" +#include "znode-payments.h" +#include "znode-sync.h" +#include "znodeman.h" + #include #include #include @@ -61,7 +67,6 @@ using namespace std; #define ZEROCOIN_MODULUS "25195908475657893494027183240048398571429282126204032027777137836043662020707595556264018525880784406918290641249515082189298559149176184502808489120072844992687392807287776735971418347270261896375014971824691165077613379859095700097330459748808428401797429100642458691817195118746121515172654632282216869987549182422433637259085141865462043576798423387184774447920739934236584823824281198163815010674810451660377306056201619676256133844143603833904414952634432190114657544454178424020924616515723350778707749817125772467962926386356373289912154831438167899885040445364023527381951378636564391212010397122822120720357" - /** * Global state */ @@ -95,6 +100,9 @@ CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; CTxMemPool mempool(::minRelayTxFee); FeeFilterRounder filterRounder(::minRelayTxFee); +// Dash masternode +map mapRejectedBlocks GUARDED_BY(cs_main); + // Settings int64_t nTransactionFee = 0; int64_t nMinimumInputValue = DUST_HARD_LIMIT; @@ -658,8 +666,7 @@ namespace { if (vBlocks.size() == count) { return; } - } - else if (waitingfor == -1) { + } else if (waitingfor == -1) { // This is the first already-in-flight block. waitingfor = mapBlocksInFlight[pindex->GetBlockHash()].first; } @@ -677,19 +684,19 @@ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { stats.nMisbehavior = state->nMisbehavior; stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1; stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1; - BOOST_FOREACH(const QueuedBlock &queue, state->vBlocksInFlight) { + BOOST_FOREACH( + const QueuedBlock &queue, state->vBlocksInFlight) { if (queue.pindex) stats.vHeightInFlight.push_back(queue.pindex->nHeight); } return true; } -bool GetBlockHash(uint256& hashRet, int nBlockHeight) -{ +bool GetBlockHash(uint256 &hashRet, int nBlockHeight) { LOCK(cs_main); - if(chainActive.Tip() == NULL) return false; - if(nBlockHeight < -1 || nBlockHeight > chainActive.Height()) return false; - if(nBlockHeight == -1) nBlockHeight = chainActive.Height(); + if (chainActive.Tip() == NULL) return false; + if (nBlockHeight < -1 || nBlockHeight > chainActive.Height()) return false; + if (nBlockHeight == -1) nBlockHeight = chainActive.Height(); hashRet = chainActive[nBlockHeight]->GetBlockHash(); return true; } @@ -835,7 +842,8 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) { if ((int64_t) tx.nLockTime < ((int64_t) tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t) nBlockHeight : nBlockTime)) { return true; } - BOOST_FOREACH(const CTxIn &txin, tx.vin) { + BOOST_FOREACH( + const CTxIn &txin, tx.vin) { if (txin.nSequence != CTxIn::SEQUENCE_FINAL) { LogPrintf("txin=%s\n", txin.ToString()); LogPrintf("IsFinalTx tx=%s --> FAILED\n", tx.GetHash().ToString()); @@ -1100,34 +1108,62 @@ bool setParams = bnTrustedModulus.SetHexBool(ZEROCOIN_MODULUS); uint32_t securityLevel = 80; static libzerocoin::Params *ZCParams = new libzerocoin::Params(bnTrustedModulus); -bool CheckSpendZcoinTransaction(const CTransaction &tx, CZerocoinEntry pubCoinTx, list listPubCoin, libzerocoin::CoinDenomination targetDenomination, CValidationState &state, uint256 hashTx, bool isVerifyDB, int nHeight, bool isCheckWallet) { +bool CheckSpendZcoinTransaction(const CTransaction &tx, CZerocoinEntry pubCoinTx, list listPubCoin, + libzerocoin::CoinDenomination targetDenomination, CValidationState &state, + uint256 hashTx, bool isVerifyDB, int nHeight, bool isCheckWallet) { // Check vOut // Only one loop, we checked on the format before enter this case // Check vIn CWalletDB walletdb(pwalletMain->strWalletFile); LogPrintf("CheckSpendZcoinTransaction denomination=%d nHeight=%d\n", targetDenomination, nHeight); - BOOST_FOREACH(const CTxIn &txin, tx.vin) + BOOST_FOREACH( + const CTxIn &txin, tx.vin) { if (txin.scriptSig.IsZerocoinSpend()) { + + uint32_t pubcoinId = txin.nSequence; + if (pubcoinId < 1 || pubcoinId >= INT_MAX) { // IT BEGINS WITH 1 + return state.DoS(100, false, NSEQUENCE_INCORRECT, + "CTransaction::CheckTransaction() : Error: nSequence is not correct format"); + } + // Deserialize the CoinSpend intro a fresh object std::vector > dataTxIn; dataTxIn.insert(dataTxIn.end(), txin.scriptSig.begin() + 4, txin.scriptSig.end()); CDataStream serializedCoinSpend(SER_NETWORK, PROTOCOL_VERSION); serializedCoinSpend.vch = dataTxIn; libzerocoin::CoinSpend newSpend(ZCParams, serializedCoinSpend); + if ((pubcoinId > 0) && + (((targetDenomination == libzerocoin::ZQ_LOVELACE) && (pubcoinId >= ZC_V2_SWITCH_ID_1)) + || ((targetDenomination == libzerocoin::ZQ_GOLDWASSER) && (pubcoinId >= ZC_V2_SWITCH_ID_10)) + || ((targetDenomination == libzerocoin::ZQ_RACKOFF) && (pubcoinId >= ZC_V2_SWITCH_ID_25)) + || ((targetDenomination == libzerocoin::ZQ_PEDERSEN) && (pubcoinId >= ZC_V2_SWITCH_ID_50)) + || ((targetDenomination == libzerocoin::ZQ_WILLIAMSON) && (pubcoinId >= ZC_V2_SWITCH_ID_100)))) { + newSpend.setVersion(2); + } // Create a new metadata object to contain the hash of the received // ZEROCOIN_SPEND transaction. If we were a real client we'd actually // compute the hash of the received transaction here. - libzerocoin::SpendMetaData newMetadata(0, 0); + uint256 txHash = ArithToUint256(arith_uint256(0)); + libzerocoin::SpendMetaData newMetadata(0, txHash); + if ((pubcoinId > 0) && + (((targetDenomination == libzerocoin::ZQ_LOVELACE) && (pubcoinId >= ZC_V2_SWITCH_ID_1)) + || ((targetDenomination == libzerocoin::ZQ_GOLDWASSER) && (pubcoinId >= ZC_V2_SWITCH_ID_10)) + || ((targetDenomination == libzerocoin::ZQ_RACKOFF) && (pubcoinId >= ZC_V2_SWITCH_ID_25)) + || ((targetDenomination == libzerocoin::ZQ_PEDERSEN) && (pubcoinId >= ZC_V2_SWITCH_ID_50)) + || ((targetDenomination == libzerocoin::ZQ_WILLIAMSON) && (pubcoinId >= ZC_V2_SWITCH_ID_100)))) { + newMetadata.accumulatorId = txin.nSequence; + newMetadata.txHash = tx.GetNormalizedHash(); + } libzerocoin::Accumulator accumulator(ZCParams, targetDenomination); libzerocoin::Accumulator accumulatorRev(ZCParams, targetDenomination); libzerocoin::Accumulator accumulatorPrecomputed(ZCParams, targetDenomination); bool passVerify = false; - uint32_t pubcoinId = txin.nSequence; - if (pubcoinId < 1 || pubcoinId >= INT_MAX) { // IT BEGINS WITH 1 - return state.DoS(100, false, NSEQUENCE_INCORRECT, - "CTransaction::CheckTransaction() : Error: nSequence is not correct format"); - } +// uint32_t pubcoinId = txin.nSequence; +// if (pubcoinId < 1 || pubcoinId >= INT_MAX) { // IT BEGINS WITH 1 +// return state.DoS(100, false, NSEQUENCE_INCORRECT, +// "CTransaction::CheckTransaction() : Error: nSequence is not correct format"); +// } // VERIFY COINSPEND TX // used pre-computed accumulator @@ -1137,7 +1173,8 @@ bool CheckSpendZcoinTransaction(const CTransaction &tx, CZerocoinEntry pubCoinTx } int countPubcoin = 0; if (!passVerify) { - BOOST_FOREACH(const CZerocoinEntry &pubCoinItem, listPubCoin) { + BOOST_FOREACH( + const CZerocoinEntry &pubCoinItem, listPubCoin) { if (pubCoinItem.denomination == targetDenomination && (pubCoinItem.id >= 0 && (uint32_t) pubCoinItem.id == pubcoinId) && pubCoinItem.nHeight != -1) { @@ -1148,7 +1185,8 @@ bool CheckSpendZcoinTransaction(const CTransaction &tx, CZerocoinEntry pubCoinTx } countPubcoin++; accumulator += pubCoinTemp; - LogPrintf("count=%s, accumulator=%s\n", countPubcoin, accumulator.getValue().ToString().substr(0, 15)); + LogPrintf("count=%s, accumulator=%s\n", countPubcoin, + accumulator.getValue().ToString().substr(0, 15)); if (countPubcoin >= 2) { // MINIMUM REQUIREMENT IS 2 PUBCOINS if (newSpend.Verify(accumulator, newMetadata)) { LogPrintf("COIN SPEND TX DID VERIFY - accumulator!\n"); @@ -1165,22 +1203,25 @@ bool CheckSpendZcoinTransaction(const CTransaction &tx, CZerocoinEntry pubCoinTx // It does not have this mint coins id, still sync if (countPubcoin == 0) { - return state.DoS(0, false, NO_MINT_ZEROCOIN, "CTransaction::CheckTransaction() : Error: Node does not have mint zerocoin to verify, please wait until "); + return state.DoS(0, false, NO_MINT_ZEROCOIN, + "CTransaction::CheckTransaction() : Error: Node does not have mint zerocoin to verify, please wait until "); } } if (!passVerify) { int countPubcoin = 0; // LogPrint("CheckSpendZcoinTransaction", "Check reverse\n"); - BOOST_REVERSE_FOREACH(const CZerocoinEntry &pubCoinItem, listPubCoin) { + BOOST_REVERSE_FOREACH( + const CZerocoinEntry &pubCoinItem, listPubCoin) { // LogPrintf("--denomination = %d, id = %d, pubcoinId = %d height = %d\n", // pubCoinItem.denomination, pubCoinItem.id, pubcoinId, pubCoinItem.nHeight); if (pubCoinItem.denomination == targetDenomination && (pubCoinItem.id >= 0 && (uint32_t) pubCoinItem.id == pubcoinId) && pubCoinItem.nHeight != -1) { - LogPrint("CheckSpendZcoinTransaction", "--## denomination = %d, id = %d, pubcoinId = %d height = %d\n", - pubCoinItem.denomination, pubCoinItem.id, pubcoinId, - pubCoinItem.nHeight); + LogPrint("CheckSpendZcoinTransaction", + "--## denomination = %d, id = %d, pubcoinId = %d height = %d\n", + pubCoinItem.denomination, pubCoinItem.id, pubcoinId, + pubCoinItem.nHeight); libzerocoin::PublicCoin pubCoinTemp(ZCParams, pubCoinItem.value, targetDenomination); if (!pubCoinTemp.validate()) { return state.DoS(100, @@ -1188,7 +1229,8 @@ bool CheckSpendZcoinTransaction(const CTransaction &tx, CZerocoinEntry pubCoinTx } countPubcoin++; accumulatorRev += pubCoinTemp; - LogPrintf("count=%s, accumulatorRev=%s\n", countPubcoin, accumulatorRev.getValue().ToString().substr(0, 15)); + LogPrintf("count=%s, accumulatorRev=%s\n", countPubcoin, + accumulatorRev.getValue().ToString().substr(0, 15)); if (countPubcoin >= 2) { // MINIMUM REQUIREMENT IS 2 PUBCOINS if (newSpend.Verify(accumulatorRev, newMetadata)) { LogPrintf("COIN SPEND TX DID VERIFY - accumulatorRev!\n"); @@ -1201,7 +1243,8 @@ bool CheckSpendZcoinTransaction(const CTransaction &tx, CZerocoinEntry pubCoinTx // It does not have this mint coins id, still sync if (countPubcoin == 0) { - return state.DoS(0, false, NO_MINT_ZEROCOIN, "CTransaction::CheckTransaction() : Error: Node does not have mint zerocoin to verify, please wait until "); + return state.DoS(0, false, NO_MINT_ZEROCOIN, + "CTransaction::CheckTransaction() : Error: Node does not have mint zerocoin to verify, please wait until "); } } @@ -1219,19 +1262,22 @@ bool CheckSpendZcoinTransaction(const CTransaction &tx, CZerocoinEntry pubCoinTx std::list listCoinSpendSerial; walletdb.ListCoinSpendSerial(listCoinSpendSerial); - BOOST_FOREACH(const CZerocoinSpendEntry &item, listCoinSpendSerial) { + BOOST_FOREACH( + const CZerocoinSpendEntry &item, listCoinSpendSerial) { if (item.coinSerial == serialNumber && item.denomination == targetDenomination && (item.id >= 0 && (uint32_t) item.id == pubcoinId) && item.hashTx != hashTx) { - return state.DoS(100, error("CTransaction::CheckTransaction() : The CoinSpend serial has been used")); + return state.DoS(0, + error("CTransaction::CheckTransaction() : The CoinSpend serial has been used")); } else if (item.coinSerial == serialNumber && item.hashTx == hashTx && item.denomination == targetDenomination && (item.id >= 0 && (uint32_t) item.id == pubcoinId) && item.pubCoin != 0) { // UPDATING PROCESS - BOOST_FOREACH(const CZerocoinEntry &pubCoinItem, listPubCoin) { + BOOST_FOREACH( + const CZerocoinEntry &pubCoinItem, listPubCoin) { if (pubCoinItem.value == item.pubCoin) { pubCoinTx.nHeight = pubCoinItem.nHeight; pubCoinTx.denomination = pubCoinItem.denomination; @@ -1245,7 +1291,8 @@ bool CheckSpendZcoinTransaction(const CTransaction &tx, CZerocoinEntry pubCoinTx walletdb.WriteZerocoinEntry(pubCoinTx); // Update UI wallet // LogPrint("CheckSpendZcoinTransaction", "pubcoin=%s, isUsed=Used\n", pubCoinItem.value.GetHex()); - pwalletMain->NotifyZerocoinChanged(pwalletMain, pubCoinItem.value.GetHex(), "Used", CT_UPDATED); + pwalletMain->NotifyZerocoinChanged(pwalletMain, pubCoinItem.value.GetHex(), "Used", + CT_UPDATED); break; } } @@ -1268,7 +1315,8 @@ bool CheckSpendZcoinTransaction(const CTransaction &tx, CZerocoinEntry pubCoinTx zccoinSpend.hashTx = hashTx; zccoinSpend.pubCoin = 0; zccoinSpend.id = pubcoinId; - if (nHeight > HF_ZEROSPEND_FIX && nHeight < INT_MAX) { + bool fTestNet = (Params().NetworkIDString() == CBaseChainParams::TESTNET); + if ((fTestNet || nHeight > ZC_CHECK_BUG_FIXED_AT_BLOCK) && nHeight < INT_MAX) { zccoinSpend.denomination = targetDenomination; } // LogPrintf("WriteCoinSpendSerialEntry, serialNumber=%s", serialNumber.ToString()); @@ -1284,8 +1332,10 @@ bool CheckSpendZcoinTransaction(const CTransaction &tx, CZerocoinEntry pubCoinTx } //static libzerocoin::Params *ZCParams; -bool CheckTransaction(const CTransaction &tx, CValidationState &state, uint256 hashTx, bool isVerifyDB, int nHeight, bool isCheckWallet) { - LogPrintf("CheckTransaction nHeight=%s, isVerifyDB=%s, isCheckWallet=%s, txHash=%s\n", nHeight, isVerifyDB, isCheckWallet, tx.GetHash().ToString()); +bool CheckTransaction(const CTransaction &tx, CValidationState &state, uint256 hashTx, bool isVerifyDB, int nHeight, + bool isCheckWallet) { + LogPrintf("CheckTransaction nHeight=%s, isVerifyDB=%s, isCheckWallet=%s, txHash=%s\n", nHeight, isVerifyDB, + isCheckWallet, tx.GetHash().ToString()); // LogPrintf("transaction = %s\n", tx.ToString()); bool fTestNet = (Params().NetworkIDString() == CBaseChainParams::TESTNET); // Basic checks that don't depend on any context @@ -1299,7 +1349,8 @@ bool CheckTransaction(const CTransaction &tx, CValidationState &state, uint256 h // Check for negative or overflow output values CAmount nValueOut = 0; - BOOST_FOREACH(const CTxOut &txout, tx.vout) + BOOST_FOREACH( + const CTxOut &txout, tx.vout) { if (txout.nValue < 0) return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative"); @@ -1312,7 +1363,8 @@ bool CheckTransaction(const CTransaction &tx, CValidationState &state, uint256 h // Check for duplicate inputs set vInOutPoints; - BOOST_FOREACH(const CTxIn &txin, tx.vin) + BOOST_FOREACH( + const CTxIn &txin, tx.vin) { if (vInOutPoints.count(txin.prevout)) return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate"); @@ -1322,9 +1374,8 @@ bool CheckTransaction(const CTransaction &tx, CValidationState &state, uint256 h if (tx.IsCoinBase()) { if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) return state.DoS(100, false, REJECT_INVALID, "bad-cb-length"); - //BTZC: add ZCOIN code // Check for founders inputs - if ((nHeight > 0) && (nHeight < 210000) && (nHeight != 57157)) { + if ((nHeight > ZC_CHECK_BUG_FIXED_AT_BLOCK) && (nHeight < 210000)) { bool found_1 = false; bool found_2 = false; bool found_3 = false; @@ -1340,9 +1391,11 @@ bool CheckTransaction(const CTransaction &tx, CValidationState &state, uint256 h if (!fTestNet && GetAdjustedTime() > nStartRewardTime) { FOUNDER_1_SCRIPT = GetScriptForDestination(CBitcoinAddress("aCAgTPgtYcA4EysU4UKC86EQd5cTtHtCcr").Get()); if (nHeight < 14000) { - FOUNDER_2_SCRIPT = GetScriptForDestination(CBitcoinAddress("aLrg41sXbXZc5MyEj7dts8upZKSAtJmRDR").Get()); + FOUNDER_2_SCRIPT = GetScriptForDestination( + CBitcoinAddress("aLrg41sXbXZc5MyEj7dts8upZKSAtJmRDR").Get()); } else { - FOUNDER_2_SCRIPT = GetScriptForDestination(CBitcoinAddress("aHu897ivzmeFuLNB6956X6gyGeVNHUBRgD").Get()); + FOUNDER_2_SCRIPT = GetScriptForDestination( + CBitcoinAddress("aHu897ivzmeFuLNB6956X6gyGeVNHUBRgD").Get()); } FOUNDER_3_SCRIPT = GetScriptForDestination(CBitcoinAddress("aQ18FBVFtnueucZKeVg4srhmzbpAeb1KoN").Get()); FOUNDER_4_SCRIPT = GetScriptForDestination(CBitcoinAddress("a1HwTdCmQV3NspP2QqCGpehoFpi8NY4Zg3").Get()); @@ -1358,7 +1411,8 @@ bool CheckTransaction(const CTransaction &tx, CValidationState &state, uint256 h FOUNDER_5_SCRIPT = GetScriptForDestination(CBitcoinAddress("TTtLk1iapn8QebamQcb8GEh1MNq8agYcVk").Get()); } - BOOST_FOREACH(const CTxOut &output, tx.vout) { + BOOST_FOREACH( + const CTxOut &output, tx.vout) { if (output.scriptPubKey == FOUNDER_1_SCRIPT && output.nValue == (int64_t)(2 * COIN)) { found_1 = true; } @@ -1382,15 +1436,18 @@ bool CheckTransaction(const CTransaction &tx, CValidationState &state, uint256 h } } } else { - BOOST_FOREACH(const CTxIn &txin, tx.vin) + BOOST_FOREACH( + const CTxIn &txin, tx.vin) if (txin.prevout.IsNull() && !txin.scriptSig.IsZerocoinSpend()) { return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null"); } // Check Mint Zerocoin Transaction - BOOST_FOREACH(const CTxOut &txout, tx.vout) { + BOOST_FOREACH( + const CTxOut &txout, tx.vout) { if (!txout.scriptPubKey.empty() && txout.scriptPubKey.IsZerocoinMint()) { - LogPrintf("CheckMintZcoinTransaction txHash = %s, isVerifyDB = %d\n", txout.GetHash().ToString(), isVerifyDB); + LogPrintf("CheckMintZcoinTransaction txHash = %s, isVerifyDB = %d\n", txout.GetHash().ToString(), + isVerifyDB); LogPrintf("nValue = %d\n", txout.nValue); vector vchZeroMint; vchZeroMint.insert(vchZeroMint.end(), txout.scriptPubKey.begin() + 6, @@ -1446,7 +1503,8 @@ bool CheckTransaction(const CTransaction &tx, CValidationState &state, uint256 h bool isAlreadyStored = false; // CHECKING PROCESS - BOOST_FOREACH(const CZerocoinEntry &pubCoinItem, listPubCoin) { + BOOST_FOREACH( + const CZerocoinEntry &pubCoinItem, listPubCoin) { if (pubCoinItem.value == pubCoin && pubCoinItem.denomination == denomination) { isAlreadyStored = true; break; @@ -1472,7 +1530,8 @@ bool CheckTransaction(const CTransaction &tx, CValidationState &state, uint256 h if (tx.IsZerocoinSpend()) { // Check vOut // Only one loop, we checked on the format before enter this case - BOOST_FOREACH(const CTxOut &txout, tx.vout) + BOOST_FOREACH( + const CTxOut &txout, tx.vout) { CZerocoinEntry pubCoinTx; list listPubCoin; @@ -1483,27 +1542,38 @@ bool CheckTransaction(const CTransaction &tx, CValidationState &state, uint256 h listPubCoin.sort(CompHeight); if (txout.nValue == libzerocoin::ZQ_LOVELACE * COIN) { // Check vIn - if (!CheckSpendZcoinTransaction(tx, pubCoinTx, listPubCoin, libzerocoin::ZQ_LOVELACE, state, hashTx,isVerifyDB, nHeight, isCheckWallet)) { - return state.DoS(100, error("CTransaction::CheckTransaction() : COIN SPEND TX IN ZQ_LOVELACE DID NOT VERIFY!")); + if (!CheckSpendZcoinTransaction(tx, pubCoinTx, listPubCoin, libzerocoin::ZQ_LOVELACE, state, + hashTx, isVerifyDB, nHeight, isCheckWallet)) { + return state.DoS(100, + error("CTransaction::CheckTransaction() : COIN SPEND TX IN ZQ_LOVELACE DID NOT VERIFY!")); }; } else if (txout.nValue == libzerocoin::ZQ_GOLDWASSER * COIN) { - if (!CheckSpendZcoinTransaction(tx, pubCoinTx, listPubCoin, libzerocoin::ZQ_GOLDWASSER, state, hashTx, isVerifyDB, nHeight, isCheckWallet)) { - return state.DoS(100, error("CTr[ansaction::CheckTransaction() : COIN SPEND TX IN ZQ_GOLDWASSER DID NOT VERIFY!")); + if (!CheckSpendZcoinTransaction(tx, pubCoinTx, listPubCoin, libzerocoin::ZQ_GOLDWASSER, state, + hashTx, isVerifyDB, nHeight, isCheckWallet)) { + return state.DoS(100, + error("CTr[ansaction::CheckTransaction() : COIN SPEND TX IN ZQ_GOLDWASSER DID NOT VERIFY!")); }; } else if (txout.nValue == libzerocoin::ZQ_RACKOFF * COIN) { - if (!CheckSpendZcoinTransaction(tx, pubCoinTx, listPubCoin, libzerocoin::ZQ_RACKOFF, state, hashTx, isVerifyDB, nHeight, isCheckWallet)) { - return state.DoS(100, error("CTransaction::CheckTransaction() : COIN SPEND TX IN ZQ_RACKOFF DID NOT VERIFY!")); + if (!CheckSpendZcoinTransaction(tx, pubCoinTx, listPubCoin, libzerocoin::ZQ_RACKOFF, state, + hashTx, isVerifyDB, nHeight, isCheckWallet)) { + return state.DoS(100, + error("CTransaction::CheckTransaction() : COIN SPEND TX IN ZQ_RACKOFF DID NOT VERIFY!")); }; } else if (txout.nValue == libzerocoin::ZQ_PEDERSEN * COIN) { - if (!CheckSpendZcoinTransaction(tx, pubCoinTx, listPubCoin, libzerocoin::ZQ_PEDERSEN, state, hashTx, isVerifyDB, nHeight, isCheckWallet)) { - return state.DoS(100, error("CTransaction::CheckTransaction() : COIN SPEND TX IN ZQ_PEDERSEN DID NOT VERIFY!")); + if (!CheckSpendZcoinTransaction(tx, pubCoinTx, listPubCoin, libzerocoin::ZQ_PEDERSEN, state, + hashTx, isVerifyDB, nHeight, isCheckWallet)) { + return state.DoS(100, + error("CTransaction::CheckTransaction() : COIN SPEND TX IN ZQ_PEDERSEN DID NOT VERIFY!")); }; } else if (txout.nValue == libzerocoin::ZQ_WILLIAMSON * COIN) { - if (!CheckSpendZcoinTransaction(tx, pubCoinTx, listPubCoin, libzerocoin::ZQ_WILLIAMSON, state, hashTx, isVerifyDB, nHeight, isCheckWallet)) { - return state.DoS(100, error("CTransaction::CheckTransaction() : COIN SPEND TX IN ZQ_WILLIAMSON DID NOT VERIFY!")); + if (!CheckSpendZcoinTransaction(tx, pubCoinTx, listPubCoin, libzerocoin::ZQ_WILLIAMSON, state, + hashTx, isVerifyDB, nHeight, isCheckWallet)) { + return state.DoS(100, + error("CTransaction::CheckTransaction() : COIN SPEND TX IN ZQ_WILLIAMSON DID NOT VERIFY!")); }; } else { - return state.DoS(100, error("CTransaction::CheckTransaction() : Your spending txout value does not match")); + return state.DoS(100, + error("CTransaction::CheckTransaction() : Your spending txout value does not match")); } walletdb.Flush(); walletdb.Close(); @@ -1534,8 +1604,9 @@ std::string FormatStateMessage(const CValidationState &state) { state.GetRejectCode()); } -bool AcceptToMemoryPoolWorker(CTxMemPool &pool, CValidationState &state, const CTransaction &tx, bool fCheckInputs, bool fLimitFree, - bool *pfMissingInputs, bool fOverrideMempoolLimit, const CAmount &nAbsurdFee, +bool AcceptToMemoryPoolWorker(CTxMemPool &pool, CValidationState &state, const CTransaction &tx, bool fCheckInputs, + bool fLimitFree, + bool *pfMissingInputs, bool fOverrideMempoolLimit, const CAmount &nAbsurdFee, std::vector &vHashTxnToUncache, bool isCheckWalletTransaction) { bool fTestNet = (Params().NetworkIDString() == CBaseChainParams::TESTNET); LogPrintf("AcceptToMemoryPoolWorker(),fCheckInputs=%s, tx.IsZerocoinSpend()=%s, fTestNet=%s\n", fCheckInputs, @@ -1589,11 +1660,12 @@ bool AcceptToMemoryPoolWorker(CTxMemPool &pool, CValidationState &state, const C return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-in-mempool"); // Check for conflicts with in-memory transactions set setConflicts; - //btzc + //btzc { LOCK(pool.cs); // protect pool.mapNextTx if (!tx.IsZerocoinSpend()) { - BOOST_FOREACH(const CTxIn &txin, tx.vin) + BOOST_FOREACH( + const CTxIn &txin, tx.vin) { auto itConflicting = pool.mapNextTx.find(txin.prevout); if (itConflicting != pool.mapNextTx.end()) { @@ -1601,7 +1673,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool &pool, CValidationState &state, const C if (!setConflicts.count(ptxConflicting->GetHash())) { bool fReplacementOptOut = true; if (fEnableReplacement) { - BOOST_FOREACH(const CTxIn &txin, ptxConflicting->vin) + BOOST_FOREACH( + const CTxIn &txin, ptxConflicting->vin) { if (txin.nSequence < std::numeric_limits < unsigned int > ::max() - 1) { @@ -1642,7 +1715,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool &pool, CValidationState &state, const C // Note that this does not check for the presence of actual outputs (see the next check for that), // and only helps with filling in pfMissingInputs (to determine missing vs spent). - BOOST_FOREACH(const CTxIn txin, tx.vin) { + BOOST_FOREACH( + const CTxIn txin, tx.vin) { if (!pcoinsTip->HaveCoinsInCache(txin.prevout.hash)) vHashTxnToUncache.push_back(txin.prevout.hash); if (!view.HaveCoins(txin.prevout.hash)) { @@ -1701,7 +1775,9 @@ bool AcceptToMemoryPoolWorker(CTxMemPool &pool, CValidationState &state, const C // Keep track of transactions that spend a coinbase, which we re-scan // during reorgs to ensure COINBASE_MATURITY is still met. bool fSpendsCoinbase = false; - BOOST_FOREACH(const CTxIn &txin, tx.vin) { const CCoins *coins = view.AccessCoins(txin.prevout.hash); + BOOST_FOREACH( + const CTxIn &txin, tx.vin) { + const CCoins *coins = view.AccessCoins(txin.prevout.hash); if (coins->IsCoinBase()) { fSpendsCoinbase = true; break; @@ -1716,7 +1792,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool &pool, CValidationState &state, const C int64_t txMinFee = 0; if (fLimitFree && nFees < txMinFee) { LogPrintf("not enough fee, nFees=%d, txMinFee=%d\n", nFees, txMinFee); - return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "not enough fee", false, strprintf("nFees=%d, txMinFee=%d", nFees, txMinFee)); + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "not enough fee", false, + strprintf("nFees=%d, txMinFee=%d", nFees, txMinFee)); } unsigned int nSize = entry.GetTxSize(); @@ -1741,7 +1818,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool &pool, CValidationState &state, const C // nModifiedFees < ::minRelayTxFee.GetFee(nSize) && // !AllowFree(entry.GetPriority(chainActive.Height() + 1))) { // LogPrintf("cause by -> insufficient priority\n"); - // Require that free transactions have sufficient priority to be mined in the next block. + // Require that free transactions have sufficient priority to be mined in the next block. // return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority"); // } @@ -1773,7 +1850,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool &pool, CValidationState &state, const C // return state.Invalid(false, REJECT_HIGHFEE, "absurdly-high-fee", strprintf("%d > %d", nFees, nAbsurdFee)); if (nAbsurdFee && nFees > CTransaction::nMinRelayTxFee * 1000) - return state.Invalid(false, REJECT_HIGHFEE, "insane fee", strprintf("%d > %d", nFees, CTransaction::nMinRelayTxFee * 1000)); + return state.Invalid(false, REJECT_HIGHFEE, "insane fee", + strprintf("%d > %d", nFees, CTransaction::nMinRelayTxFee * 1000)); // Calculate in-mempool ancestors, up to a limit. CTxMemPool::setEntries setAncestors; @@ -1792,7 +1870,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool &pool, CValidationState &state, const C // that we have the set of all ancestors we can detect this // pathological case by making sure setConflicts and setAncestors don't // intersect. - BOOST_FOREACH(CTxMemPool::txiter ancestorIt, setAncestors) + BOOST_FOREACH(CTxMemPool::txiter + ancestorIt, setAncestors) { const uint256 &hashAncestor = ancestorIt->GetTx().GetHash(); if (setConflicts.count(hashAncestor)) { @@ -1821,7 +1900,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool &pool, CValidationState &state, const C set setConflictsParents; const int maxDescendantsToVisit = 100; CTxMemPool::setEntries setIterConflicting; - BOOST_FOREACH(const uint256 &hashConflicting, setConflicts) + BOOST_FOREACH( + const uint256 &hashConflicting, setConflicts) { CTxMemPool::txiter mi = pool.mapTx.find(hashConflicting); if (mi == pool.mapTx.end()) @@ -1856,7 +1936,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool &pool, CValidationState &state, const C oldFeeRate.ToString())); } - BOOST_FOREACH(const CTxIn &txin, mi->GetTx().vin) + BOOST_FOREACH( + const CTxIn &txin, mi->GetTx().vin) { setConflictsParents.insert(txin.prevout.hash); } @@ -1965,7 +2046,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool &pool, CValidationState &state, const C // } // Remove conflicting transactions from the mempool - BOOST_FOREACH(const CTxMemPool::txiter it, allConflicting) + BOOST_FOREACH( + const CTxMemPool::txiter it, allConflicting) { LogPrint("mempool", "replacing tx %s with %s for %s BTC additional fees, %d delta bytes\n", it->GetTx().GetHash().ToString(), @@ -2010,22 +2092,27 @@ bool AcceptToMemoryPoolWorker(CTxMemPool &pool, CValidationState &state, const C return true; } -bool AcceptToMemoryPool(CTxMemPool &pool, CValidationState &state, const CTransaction &tx, bool fCheckInputs, bool fLimitFree, - bool *pfMissingInputs, bool fOverrideMempoolLimit, const CAmount nAbsurdFee, bool isCheckWalletTransaction) { +bool AcceptToMemoryPool(CTxMemPool &pool, CValidationState &state, const CTransaction &tx, bool fCheckInputs, + bool fLimitFree, + bool *pfMissingInputs, bool fOverrideMempoolLimit, const CAmount nAbsurdFee, + bool isCheckWalletTransaction) { LogPrintf("AcceptToMemoryPool(), fCheckInputs=%s\n", fCheckInputs); std::vector vHashTxToUncache; - bool res = AcceptToMemoryPoolWorker(pool, state, tx, fCheckInputs, fLimitFree, pfMissingInputs, fOverrideMempoolLimit, nAbsurdFee, + bool res = AcceptToMemoryPoolWorker(pool, state, tx, fCheckInputs, fLimitFree, pfMissingInputs, + fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache, isCheckWalletTransaction); if (!res) { LogPrintf("AcceptToMemoryPoolWorker --> FAILED\n"); - BOOST_FOREACH(const uint256 &hashTx, vHashTxToUncache) - pcoinsTip->Uncache(hashTx); + BOOST_FOREACH( + const uint256 &hashTx, vHashTxToUncache) + pcoinsTip->Uncache(hashTx); } return res; } /** Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock */ -bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::Params &consensusParams, uint256 &hashBlock, +bool +GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::Params &consensusParams, uint256 &hashBlock, bool fAllowSlow) { CBlockIndex *pindexSlow = NULL; @@ -2386,7 +2473,8 @@ int GetSpendHeight(const CCoinsViewCache &inputs) { } namespace Consensus { - bool CheckTxInputs(const CTransaction &tx, CValidationState &state, const CCoinsViewCache &inputs, int nSpendHeight) { + bool + CheckTxInputs(const CTransaction &tx, CValidationState &state, const CCoinsViewCache &inputs, int nSpendHeight) { // This doesn't trigger the DoS code on purpose; if it did, it would make it easier // for an attacker to attempt to split the network. if (!inputs.HaveInputs(tx)) @@ -2831,10 +2919,12 @@ bool ConnectBlock(const CBlock &block, CValidationState &state, CBlockIndex *pin bool fEnforceBIP30 = true; if (fEnforceBIP30) { - BOOST_FOREACH(const CTransaction &tx, block.vtx) { + BOOST_FOREACH( + const CTransaction &tx, block.vtx) { const CCoins *coins = view.AccessCoins(tx.GetHash()); if (coins && !coins->IsPruned()) - return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"), REJECT_INVALID, "bad-txns-BIP30"); + return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"), REJECT_INVALID, + "bad-txns-BIP30"); } } @@ -2971,6 +3061,25 @@ bool ConnectBlock(const CBlock &block, CValidationState &state, CBlockIndex *pin return state.DoS(100, error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)", block.vtx[0].GetValueOut(), blockReward), REJECT_INVALID, "bad-cb-amount"); + // DASH : MODIFIED TO CHECK MASTERNODE PAYMENTS AND SUPERBLOCKS + + // It's possible that we simply don't have enough data and this could fail + // (i.e. block itself could be a correct one and we need to store it), + // that's why this is in ConnectBlock. Could be the other way around however - + // the peer who sent us this block is missing some data and wasn't able + // to recognize that block is actually invalid. + // TODO: resync data (both ways?) and try to reprocess this block later. + std::string strError = ""; + if (!IsBlockValueValid(block, pindex->nHeight, blockReward, strError)) { + return state.DoS(0, error("ConnectBlock(DASH): %s", strError), REJECT_INVALID, "bad-cb-amount"); + } + + if (!IsBlockPayeeValid(block.vtx[0], pindex->nHeight, blockReward)) { + mapRejectedBlocks.insert(make_pair(block.GetHash(), GetTime())); + return state.DoS(0, error("ConnectBlock(DASH): couldn't find masternode or superblock payments"), + REJECT_INVALID, "bad-cb-payee"); + } + // END DASH if (!control.Wait()) return state.DoS(100, false); @@ -3199,14 +3308,16 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams &chainParams) { warningMessages.push_back(strprintf("%d of last 100 blocks have unexpected version", nUpgraded)); if (nUpgraded > 100 / 2) { // strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user: - strMiscWarning = _("Warning: Unknown block versions being mined! It's possible unknown rules are in effect"); + strMiscWarning = _( + "Warning: Unknown block versions being mined! It's possible unknown rules are in effect"); if (!fWarned) { AlertNotify(strMiscWarning); fWarned = true; } } } - LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utx)", + LogPrintf( + "%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utx)", __func__, chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), chainActive.Tip()->nVersion, log(chainActive.Tip()->nChainWork.getdouble()) / log(2.0), (unsigned long) chainActive.Tip()->nChainTx, @@ -3244,12 +3355,15 @@ bool static DisconnectTip(CValidationState &state, const CChainParams &chainpara list listCoinSpendSerial; walletdb.ListCoinSpendSerial(listCoinSpendSerial); - BOOST_FOREACH(const CTransaction &tx, block.vtx){ + BOOST_FOREACH( + const CTransaction &tx, block.vtx){ // Check Spend Zerocoin Transaction if (tx.IsZerocoinSpend()) { - BOOST_FOREACH(const CZerocoinSpendEntry &item, listCoinSpendSerial) { + BOOST_FOREACH( + const CZerocoinSpendEntry &item, listCoinSpendSerial) { if (item.hashTx == tx.GetHash()) { - BOOST_FOREACH(const CZerocoinEntry &pubCoinItem, listPubCoin) { + BOOST_FOREACH( + const CZerocoinEntry &pubCoinItem, listPubCoin) { if (pubCoinItem.value == item.pubCoin) { CZerocoinEntry pubCoinTx; pubCoinTx.nHeight = pubCoinItem.nHeight; @@ -3263,7 +3377,8 @@ bool static DisconnectTip(CValidationState &state, const CChainParams &chainpara walletdb.WriteZerocoinEntry(pubCoinTx); LogPrintf("DisconnectTip() -> NotifyZerocoinChanged\n"); LogPrintf("pubcoin=%s, isUsed=New\n", pubCoinItem.value.GetHex()); - pwalletMain->NotifyZerocoinChanged(pwalletMain, pubCoinItem.value.GetHex(), "New", CT_UPDATED); + pwalletMain->NotifyZerocoinChanged(pwalletMain, pubCoinItem.value.GetHex(), "New", + CT_UPDATED); walletdb.EraseCoinSpendSerialEntry(item); pwalletMain->EraseFromWallet(item.hashTx); } @@ -3273,14 +3388,17 @@ bool static DisconnectTip(CValidationState &state, const CChainParams &chainpara } // Check Mint Zerocoin Transaction - BOOST_FOREACH(const CTxOut txout, tx.vout) { + BOOST_FOREACH( + const CTxOut txout, tx.vout) { if (!txout.scriptPubKey.empty() && txout.scriptPubKey.IsZerocoinMint()) { vector vchZeroMint; - vchZeroMint.insert(vchZeroMint.end(), txout.scriptPubKey.begin() + 6, txout.scriptPubKey.begin() + txout.scriptPubKey.size()); + vchZeroMint.insert(vchZeroMint.end(), txout.scriptPubKey.begin() + 6, + txout.scriptPubKey.begin() + txout.scriptPubKey.size()); CBigNum pubCoin; pubCoin.setvch(vchZeroMint); int zerocoinMintHeight = -1; - BOOST_FOREACH(const CZerocoinEntry &pubCoinItem, listPubCoin) { + BOOST_FOREACH( + const CZerocoinEntry &pubCoinItem, listPubCoin) { if (pubCoinItem.value == pubCoin) { zerocoinMintHeight = pubCoinItem.nHeight; CZerocoinEntry pubCoinTx; @@ -3291,13 +3409,15 @@ bool static DisconnectTip(CValidationState &state, const CChainParams &chainpara pubCoinTx.serialNumber = pubCoinItem.serialNumber; pubCoinTx.value = pubCoin; pubCoinTx.nHeight = -1; - LogPrintf("- Pubcoin Disconnect Reset Pubcoin Id: %d Height: %d\n", pubCoinTx.id, pindexDelete->nHeight); + LogPrintf("- Pubcoin Disconnect Reset Pubcoin Id: %d Height: %d\n", pubCoinTx.id, + pindexDelete->nHeight); walletdb.WriteZerocoinEntry(pubCoinTx); } } - BOOST_FOREACH(const CZerocoinEntry &pubCoinItem, listPubCoin) { + BOOST_FOREACH( + const CZerocoinEntry &pubCoinItem, listPubCoin) { if (pubCoinItem.nHeight > zerocoinMintHeight) { CZerocoinEntry pubCoinTx; pubCoinTx.id = -1; @@ -3307,7 +3427,8 @@ bool static DisconnectTip(CValidationState &state, const CChainParams &chainpara pubCoinTx.serialNumber = pubCoinItem.serialNumber; pubCoinTx.value = pubCoin; pubCoinTx.nHeight = -1; - LogPrintf("- Disconnect Reset Pubcoin Id: %d Height: %d\n", pubCoinTx.id, pindexDelete->nHeight); + LogPrintf("- Disconnect Reset Pubcoin Id: %d Height: %d\n", pubCoinTx.id, + pindexDelete->nHeight); walletdb.WriteZerocoinEntry(pubCoinTx); } @@ -3323,7 +3444,8 @@ bool static DisconnectTip(CValidationState &state, const CChainParams &chainpara if (!fBare) { // Resurrect mempool transactions from the disconnected block. std::vector vHashUpdate; - BOOST_FOREACH(const CTransaction &tx, block.vtx) { + BOOST_FOREACH( + const CTransaction &tx, block.vtx) { // ignore validation errors in resurrected transactions list removed; CValidationState stateDummy; @@ -3350,6 +3472,7 @@ bool static DisconnectTip(CValidationState &state, const CChainParams &chainpara } return true; } + static int64_t nTimeReadFromDisk = 0; static int64_t nTimeConnectTotal = 0; static int64_t nTimeFlush = 0; @@ -3360,7 +3483,8 @@ static int64_t nTimePostConnect = 0; * Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock * corresponding to pindexNew, to bypass loading it again from disk. */ -bool static ConnectTip(CValidationState &state, const CChainParams &chainparams, CBlockIndex *pindexNew, const CBlock *pblock) { +bool static +ConnectTip(CValidationState &state, const CChainParams &chainparams, CBlockIndex *pindexNew, const CBlock *pblock) { LogPrintf("ConnectTip() nHeight=%s\n", pindexNew->nHeight); assert(pindexNew->pprev == chainActive.Tip()); // Read block from disk. @@ -3388,7 +3512,8 @@ bool static ConnectTip(CValidationState &state, const CChainParams &chainparams, mapBlockSource.erase(pindexNew->GetBlockHash()); nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2; - LogPrint("bench", " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001); + LogPrint("bench", " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, + nTimeConnectTotal * 0.000001); assert(view.Flush()); } int64_t nTime4 = GetTimeMicros(); @@ -3409,11 +3534,13 @@ bool static ConnectTip(CValidationState &state, const CChainParams &chainparams, UpdateTip(pindexNew, chainparams); // Tell wallet about transactions that went from mempool // to conflicted: - BOOST_FOREACH(const CTransaction &tx, txConflicted) { + BOOST_FOREACH( + const CTransaction &tx, txConflicted) { SyncWithWallets(tx, pindexNew, NULL); } // ... and about transactions that got confirmed: - BOOST_FOREACH(const CTransaction &tx, pblock->vtx) { + BOOST_FOREACH( + const CTransaction &tx, pblock->vtx) { SyncWithWallets(tx, pindexNew, pblock); } @@ -3427,20 +3554,18 @@ bool static ConnectTip(CValidationState &state, const CChainParams &chainparams, } -int GetUTXOHeight(const COutPoint& outpoint) -{ +int GetUTXOHeight(const COutPoint &outpoint) { LOCK(cs_main); CCoins coins; - if(!pcoinsTip->GetCoins(outpoint.hash, coins) || - (unsigned int)outpoint.n>=coins.vout.size() || - coins.vout[outpoint.n].IsNull()) { + if (!pcoinsTip->GetCoins(outpoint.hash, coins) || + (unsigned int) outpoint.n >= coins.vout.size() || + coins.vout[outpoint.n].IsNull()) { return -1; } return coins.nHeight; } -int GetInputAge(const CTxIn &txin) -{ +int GetInputAge(const CTxIn &txin) { CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); { @@ -3448,10 +3573,10 @@ int GetInputAge(const CTxIn &txin) CCoinsViewMemPool viewMempool(pcoinsTip, mempool); view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view - const CCoins* coins = view.AccessCoins(txin.prevout.hash); + const CCoins *coins = view.AccessCoins(txin.prevout.hash); if (coins) { - if(coins->nHeight < 0) return 0; + if (coins->nHeight < 0) return 0; return chainActive.Height() - coins->nHeight + 1; } else { return -1; @@ -3459,23 +3584,22 @@ int GetInputAge(const CTxIn &txin) } } -CAmount GetZnodePayment(int nHeight, CAmount blockValue) -{ - CAmount ret = blockValue/5; // start at 20% +CAmount GetZnodePayment(int nHeight, CAmount blockValue) { + CAmount ret = blockValue / 5; // start at 20% int nMNPIBlock = Params().GetConsensus().nZnodePaymentsIncreaseBlock; int nMNPIPeriod = Params().GetConsensus().nZnodePaymentsIncreasePeriod; // mainnet: - if(nHeight > nMNPIBlock) ret += blockValue / 20; // 158000 - 25.0% - 2014-10-24 - if(nHeight > nMNPIBlock+(nMNPIPeriod* 1)) ret += blockValue / 20; // 175280 - 30.0% - 2014-11-25 - if(nHeight > nMNPIBlock+(nMNPIPeriod* 2)) ret += blockValue / 20; // 192560 - 35.0% - 2014-12-26 - if(nHeight > nMNPIBlock+(nMNPIPeriod* 3)) ret += blockValue / 40; // 209840 - 37.5% - 2015-01-26 - if(nHeight > nMNPIBlock+(nMNPIPeriod* 4)) ret += blockValue / 40; // 227120 - 40.0% - 2015-02-27 - if(nHeight > nMNPIBlock+(nMNPIPeriod* 5)) ret += blockValue / 40; // 244400 - 42.5% - 2015-03-30 - if(nHeight > nMNPIBlock+(nMNPIPeriod* 6)) ret += blockValue / 40; // 261680 - 45.0% - 2015-05-01 - if(nHeight > nMNPIBlock+(nMNPIPeriod* 7)) ret += blockValue / 40; // 278960 - 47.5% - 2015-06-01 - if(nHeight > nMNPIBlock+(nMNPIPeriod* 9)) ret += blockValue / 40; // 313520 - 50.0% - 2015-08-03 + if (nHeight > nMNPIBlock) ret += blockValue / 20; // 158000 - 25.0% - 2014-10-24 + if (nHeight > nMNPIBlock + (nMNPIPeriod * 1)) ret += blockValue / 20; // 175280 - 30.0% - 2014-11-25 + if (nHeight > nMNPIBlock + (nMNPIPeriod * 2)) ret += blockValue / 20; // 192560 - 35.0% - 2014-12-26 + if (nHeight > nMNPIBlock + (nMNPIPeriod * 3)) ret += blockValue / 40; // 209840 - 37.5% - 2015-01-26 + if (nHeight > nMNPIBlock + (nMNPIPeriod * 4)) ret += blockValue / 40; // 227120 - 40.0% - 2015-02-27 + if (nHeight > nMNPIBlock + (nMNPIPeriod * 5)) ret += blockValue / 40; // 244400 - 42.5% - 2015-03-30 + if (nHeight > nMNPIBlock + (nMNPIPeriod * 6)) ret += blockValue / 40; // 261680 - 45.0% - 2015-05-01 + if (nHeight > nMNPIBlock + (nMNPIPeriod * 7)) ret += blockValue / 40; // 278960 - 47.5% - 2015-06-01 + if (nHeight > nMNPIBlock + (nMNPIPeriod * 9)) ret += blockValue / 40; // 313520 - 50.0% - 2015-08-03 return ret; } @@ -3484,7 +3608,8 @@ CAmount GetZnodePayment(int nHeight, CAmount blockValue) * Connect a new ZCblock to chainActive. pblock is either NULL or a pointer to a CBlock * corresponding to pindexNew, to bypass loading it again from disk. */ -bool static ConnectTipZC(CValidationState &state, const CChainParams &chainparams, CBlockIndex *pindexNew, const CBlock *pblock) { +bool static +ConnectTipZC(CValidationState &state, const CChainParams &chainparams, CBlockIndex *pindexNew, const CBlock *pblock) { CBlock block; if (!pblock) { if (!ReadBlockFromDisk(block, pindexNew, chainparams.GetConsensus())) @@ -3497,17 +3622,21 @@ bool static ConnectTipZC(CValidationState &state, const CChainParams &chainparam walletdb.ListPubCoin(listPubCoin); // listPubCoin.sort(CompHeight); - BOOST_FOREACH(const CTransaction &tx, pblock->vtx){ + BOOST_FOREACH( + const CTransaction &tx, pblock->vtx){ // Check Mint Zerocoin Transaction - BOOST_FOREACH(const CTxOut txout, tx.vout) { + BOOST_FOREACH( + const CTxOut txout, tx.vout) { if (!txout.scriptPubKey.empty() && txout.scriptPubKey.IsZerocoinMint()) { vector vchZeroMint; - vchZeroMint.insert(vchZeroMint.end(), txout.scriptPubKey.begin() + 6, txout.scriptPubKey.begin() + txout.scriptPubKey.size()); + vchZeroMint.insert(vchZeroMint.end(), txout.scriptPubKey.begin() + 6, + txout.scriptPubKey.begin() + txout.scriptPubKey.size()); CBigNum pubCoin; pubCoin.setvch(vchZeroMint); int zerocoinMintHeight = -1; - BOOST_FOREACH(const CZerocoinEntry &pubCoinItem, listPubCoin) { + BOOST_FOREACH( + const CZerocoinEntry &pubCoinItem, listPubCoin) { if (pubCoinItem.value == pubCoin) { // zerocoinMintHeight = pubCoinItem.nHeight; CZerocoinEntry pubCoinTx; @@ -3519,11 +3648,13 @@ bool static ConnectTipZC(CValidationState &state, const CChainParams &chainparam pubCoinTx.value = pubCoinItem.value; pubCoinTx.nHeight = -1; walletdb.WriteZerocoinEntry(pubCoinTx); - LogPrintf("Pubcoin Connect Reset Pubcoin Denomination: %d Pubcoin Id: %d Height: %d\n", pubCoinTx.denomination, pubCoinTx.id, pubCoinItem.nHeight); + LogPrintf("Pubcoin Connect Reset Pubcoin Denomination: %d Pubcoin Id: %d Height: %d\n", + pubCoinTx.denomination, pubCoinTx.id, pubCoinItem.nHeight); zerocoinMintHeight = pindexNew->nHeight; } } - BOOST_FOREACH(const CZerocoinEntry &pubCoinItem, listPubCoin) { + BOOST_FOREACH( + const CZerocoinEntry &pubCoinItem, listPubCoin) { if (pubCoinItem.nHeight > zerocoinMintHeight) { CZerocoinEntry pubCoinTx; pubCoinTx.id = -1; @@ -3533,7 +3664,8 @@ bool static ConnectTipZC(CValidationState &state, const CChainParams &chainparam pubCoinTx.serialNumber = pubCoinItem.serialNumber; pubCoinTx.value = pubCoin; pubCoinTx.nHeight = -1; - LogPrintf("Connect Reset Pubcoin Denomination: %d Pubcoin Id: %d Height: %d\n", pubCoinTx.denomination, pubCoinTx.id, pubCoinItem.nHeight); + LogPrintf("Connect Reset Pubcoin Denomination: %d Pubcoin Id: %d Height: %d\n", + pubCoinTx.denomination, pubCoinTx.id, pubCoinItem.nHeight); walletdb.WriteZerocoinEntry(pubCoinTx); } } @@ -3543,48 +3675,46 @@ bool static ConnectTipZC(CValidationState &state, const CChainParams &chainparam return true; } -bool DisconnectBlocks(int blocks) -{ -// LOCK(cs_main); -// -// CValidationState state; -// const CChainParams& chainparams = Params(); -// -// LogPrintf("DisconnectBlocks -- Got command to replay %d blocks\n", blocks); -// for(int i = 0; i < blocks; i++) { -// if(!DisconnectTip(state, chainparams.GetConsensus()) || !state.IsValid()) { -// return false; -// } -// } -// -// return true; +bool DisconnectBlocks(int blocks) { + LOCK(cs_main); + + CValidationState state; + const CChainParams &chainparams = Params(); + + LogPrintf("DisconnectBlocks -- Got command to replay %d blocks\n", blocks); + for (int i = 0; i < blocks; i++) { + if (!DisconnectTip(state, chainparams) || !state.IsValid()) { + return false; + } + } + + return true; } -void ReprocessBlocks(int nBlocks) -{ -// LOCK(cs_main); -// -// std::map::iterator it = mapRejectedBlocks.begin(); -// while(it != mapRejectedBlocks.end()){ -// //use a window twice as large as is usual for the nBlocks we want to reset -// if((*it).second > GetTime() - (nBlocks*60*5)) { -// BlockMap::iterator mi = mapBlockIndex.find((*it).first); -// if (mi != mapBlockIndex.end() && (*mi).second) { -// -// CBlockIndex* pindex = (*mi).second; -// LogPrintf("ReprocessBlocks -- %s\n", (*it).first.ToString()); -// -// CValidationState state; -// ReconsiderBlock(state, pindex); -// } -// } -// ++it; -// } -// -// DisconnectBlocks(nBlocks); -// -// CValidationState state; -// ActivateBestChain(state, Params()); +void ReprocessBlocks(int nBlocks) { + LOCK(cs_main); + + std::map::iterator it = mapRejectedBlocks.begin(); + while (it != mapRejectedBlocks.end()) { + //use a window twice as large as is usual for the nBlocks we want to reset + if ((*it).second > GetTime() - (nBlocks * 60 * 5)) { + BlockMap::iterator mi = mapBlockIndex.find((*it).first); + if (mi != mapBlockIndex.end() && (*mi).second) { + + CBlockIndex *pindex = (*mi).second; + LogPrintf("ReprocessBlocks -- %s\n", (*it).first.ToString()); + + CValidationState state; + ReconsiderBlock(state, pindex); + } + } + ++it; + } + + DisconnectBlocks(nBlocks); + + CValidationState state; + ActivateBestChain(state, Params()); } @@ -3592,7 +3722,8 @@ void ReprocessBlocks(int nBlocks) * Connect a new ZCblock to chainActive. pblock is either NULL or a pointer to a CBlock * corresponding to pindexNew, to bypass loading it again from disk. */ -bool static ReArrangeZcoinMint(CValidationState &state, const CChainParams &chainparams, CBlockIndex *pindexNew, const CBlock *pblock) { +bool static ReArrangeZcoinMint(CValidationState &state, const CChainParams &chainparams, CBlockIndex *pindexNew, + const CBlock *pblock) { // LogPrintf("[ReArrangeZcoinMint]\n"); CBlock block; if (!pblock) { @@ -3605,9 +3736,11 @@ bool static ReArrangeZcoinMint(CValidationState &state, const CChainParams &chai CWalletDB walletdb(pwalletMain->strWalletFile); walletdb.ListPubCoin(listPubCoin); - BOOST_FOREACH(const CTransaction &tx, pblock->vtx){ + BOOST_FOREACH( + const CTransaction &tx, pblock->vtx){ // Check Mint Zerocoin Transaction - BOOST_FOREACH(const CTxOut txout, tx.vout) { + BOOST_FOREACH( + const CTxOut txout, tx.vout) { if (!txout.scriptPubKey.empty() && txout.scriptPubKey.IsZerocoinMint()) { vector vchZeroMint; vchZeroMint.insert(vchZeroMint.end(), txout.scriptPubKey.begin() + 6, @@ -3623,7 +3756,8 @@ bool static ReArrangeZcoinMint(CValidationState &state, const CChainParams &chai int currentId = 1; unsigned int countExistingItems = 0; listPubCoin.sort(CompHeight); - BOOST_FOREACH(const CZerocoinEntry &pubCoinIdItem, listPubCoin) { + BOOST_FOREACH( + const CZerocoinEntry &pubCoinIdItem, listPubCoin) { // LogPrintf("denomination = %d, id = %d, height = %d\n", pubCoinIdItem.denomination, pubCoinIdItem.id, pubCoinIdItem.nHeight); if (pubCoinIdItem.id > 0) { if (pubCoinIdItem.nHeight <= pindexNew->nHeight) { @@ -3694,7 +3828,8 @@ static CBlockIndex *FindMostWorkChain() { bool fMissingData = !(pindexTest->nStatus & BLOCK_HAVE_DATA); if (fFailedChain || fMissingData) { // Candidate chain is not usable (either invalid or missing data) - if (fFailedChain && (pindexBestInvalid == NULL || pindexNew->nChainWork > pindexBestInvalid->nChainWork)) + if (fFailedChain && + (pindexBestInvalid == NULL || pindexNew->nChainWork > pindexBestInvalid->nChainWork)) pindexBestInvalid = pindexNew; CBlockIndex *pindexFailed = pindexNew; // Remove the entire chain from the set. @@ -3754,9 +3889,9 @@ static bool ActivateBestChainStep(CValidationState &state, const CChainParams &c } int nHeight = pindexFork ? pindexFork->nHeight : -1; // Build list of new blocks to connect. - std::vector vpindexToConnect; + std::vector < CBlockIndex * > vpindexToConnect; //btzc: add zcoin code - std::vector vpindexToConnectZC; + std::vector < CBlockIndex * > vpindexToConnectZC; bool fContinue = true; // LogPrintf("[ActivateBestChainStep] fContinue=%d, nHeight=%d, pindexMostWork->nHeight=%d\n", fContinue, nHeight, pindexMostWork->nHeight); while (fContinue && nHeight != pindexMostWork->nHeight) { @@ -3799,10 +3934,12 @@ static bool ActivateBestChainStep(CValidationState &state, const CChainParams &c } } } - BOOST_FOREACH(CBlockIndex * pindexConnect, vpindexToConnect){ + BOOST_FOREACH(CBlockIndex * pindexConnect, vpindexToConnect) + { ConnectTipZC(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL); } - BOOST_FOREACH(CBlockIndex * pindexConnect, vpindexToConnectZC){ + BOOST_FOREACH(CBlockIndex * pindexConnect, vpindexToConnectZC) + { ReArrangeZcoinMint(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL); } } @@ -3885,11 +4022,13 @@ bool ActivateBestChain(CValidationState &state, const CChainParams &chainparams, // Whether we have anything to do at all. if (pindexMostWork == NULL || pindexMostWork == chainActive.Tip()) { - return true; + return true; } bool fInvalidFound = false; - if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL, fInvalidFound)) { + if (!ActivateBestChainStep(state, chainparams, pindexMostWork, + pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL, + fInvalidFound)) { LogPrintf("ActivateBestChainStep --> Failed!\n"); return false; } @@ -3994,6 +4133,41 @@ bool InvalidateBlock(CValidationState &state, const CChainParams &chainparams, C return true; } +bool ReconsiderBlock(CValidationState &state, CBlockIndex *pindex) { + AssertLockHeld(cs_main); + + int nHeight = pindex->nHeight; + + // Remove the invalidity flag from this block and all its descendants. + BlockMap::iterator it = mapBlockIndex.begin(); + while (it != mapBlockIndex.end()) { + if (!it->second->IsValid() && it->second->GetAncestor(nHeight) == pindex) { + it->second->nStatus &= ~BLOCK_FAILED_MASK; + setDirtyBlockIndex.insert(it->second); + if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && + setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) { + setBlockIndexCandidates.insert(it->second); + } + if (it->second == pindexBestInvalid) { + // Reset invalid block marker if it was pointing to one of those. + pindexBestInvalid = NULL; + } + } + it++; + } + + // Remove the invalidity flag from all ancestors too. + while (pindex != NULL) { + if (pindex->nStatus & BLOCK_FAILED_MASK) { + pindex->nStatus &= ~BLOCK_FAILED_MASK; + setDirtyBlockIndex.insert(pindex); + } + pindex = pindex->pprev; + } + return true; +} + + bool ResetBlockFailureFlags(CBlockIndex *pindex) { AssertLockHeld(cs_main); @@ -4199,7 +4373,8 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne } //btzc: code from vertcoin, add -bool CheckBlockHeader(const CBlockHeader &block, CValidationState &state, const Consensus::Params &consensusParams, bool fCheckPOW) { +bool CheckBlockHeader(const CBlockHeader &block, CValidationState &state, const Consensus::Params &consensusParams, + bool fCheckPOW) { int nHeight = getNHeight(block); if (fCheckPOW && !CheckProofOfWork(block.GetPoWHash(nHeight), block.nBits, consensusParams)) { return state.DoS(50, false, REJECT_INVALID, "high-hash", false, "proof of work failed"); @@ -4209,7 +4384,8 @@ bool CheckBlockHeader(const CBlockHeader &block, CValidationState &state, const bool CheckBlock(const CBlock &block, CValidationState &state, const Consensus::Params &consensusParams, bool fCheckPOW, bool fCheckMerkleRoot, int nHeight, bool isVerifyDB) { - LogPrintf("CheckBlock() nHeight=%s, blockHash= %s, isVerifyDB = %s\n", nHeight, block.GetHash().ToString(), isVerifyDB); + LogPrintf("CheckBlock() nHeight=%s, blockHash= %s, isVerifyDB = %s\n", nHeight, block.GetHash().ToString(), + isVerifyDB); try { // These are checks that are independent of context. if (block.fChecked) @@ -4265,14 +4441,16 @@ bool CheckBlock(const CBlock &block, CValidationState &state, const Consensus::P } // Check transactions - BOOST_FOREACH(const CTransaction &tx, block.vtx) + BOOST_FOREACH( + const CTransaction &tx, block.vtx) if (!CheckTransaction(tx, state, tx.GetHash(), isVerifyDB, nHeight)) { return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(), strprintf("Transaction check failed (tx hash %s) %s", tx.GetHash().ToString(), state.GetDebugMessage())); } unsigned int nSigOps = 0; - BOOST_FOREACH(const CTransaction &tx, block.vtx) + BOOST_FOREACH( + const CTransaction &tx, block.vtx) { nSigOps += GetLegacySigOpCount(tx); } @@ -4290,7 +4468,8 @@ bool CheckBlock(const CBlock &block, CValidationState &state, const Consensus::P return true; } -static bool CheckIndexAgainstCheckpoint(const CBlockIndex *pindexPrev, CValidationState &state, const CChainParams &chainparams, +static bool +CheckIndexAgainstCheckpoint(const CBlockIndex *pindexPrev, CValidationState &state, const CChainParams &chainparams, const uint256 &hash) { if (*pindexPrev->phashBlock == chainparams.GetConsensus().hashGenesisBlock) return true; @@ -4335,7 +4514,8 @@ void UpdateUncommittedBlockStructures(CBlock &block, const CBlockIndex *pindexPr } } -std::vectorGenerateCoinbaseCommitment(CBlock &block, const CBlockIndex *pindexPrev, const Consensus::Params &consensusParams) { +std::vector +GenerateCoinbaseCommitment(CBlock &block, const CBlockIndex *pindexPrev, const Consensus::Params &consensusParams) { std::vector commitment; int commitpos = GetWitnessCommitmentIndex(block); std::vector ret(32, 0x00); @@ -4363,7 +4543,8 @@ std::vectorGenerateCoinbaseCommitment(CBlock &block, const CBlock return commitment; } -bool ContextualCheckBlockHeader(const CBlockHeader &block, CValidationState &state, const Consensus::Params &consensusParams, +bool +ContextualCheckBlockHeader(const CBlockHeader &block, CValidationState &state, const Consensus::Params &consensusParams, CBlockIndex *const pindexPrev, int64_t nAdjustedTime) { // Check proof of work if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) @@ -4404,7 +4585,8 @@ bool ContextualCheckBlock(const CBlock &block, CValidationState &state, CBlockIn : block.GetBlockTime(); // Check that all transactions are finalized - BOOST_FOREACH(const CTransaction &tx, block.vtx) { + BOOST_FOREACH( + const CTransaction &tx, block.vtx) { if (!IsFinalTx(tx, nHeight, nLockTimeCutoff)) { return state.DoS(10, false, REJECT_INVALID, "bad-txns-nonfinal", false, "non-final transaction"); } @@ -4412,10 +4594,12 @@ bool ContextualCheckBlock(const CBlock &block, CValidationState &state, CBlockIn // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): - if ((block.nVersion&0xff) >= 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityEnforceBlockUpgrade, consensusParams)) { + if ((block.nVersion & 0xff) >= 2 && + IsSuperMajority(2, pindexPrev, consensusParams.nMajorityEnforceBlockUpgrade, consensusParams)) { CScript expect = CScript() << nHeight; // LogPrintf("block.vtx[0].vin[0].scriptSig.begin()=%s\n", block.vtx[0].vin[0].scriptSig.begin()); - if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || !std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) { + if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || + !std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) { return state.DoS(100, false, REJECT_INVALID, "bad-cb-height", false, "block height mismatch in coinbase"); } } @@ -4540,7 +4724,8 @@ static bool AcceptBlockHeader(const CBlockHeader &block, CValidationState &state } /** Store block on disk. If dbp is non-NULL, the file is known to already reside on disk */ -static bool AcceptBlock(const CBlock &block, CValidationState &state, const CChainParams &chainparams, CBlockIndex **ppindex, +static bool +AcceptBlock(const CBlock &block, CValidationState &state, const CChainParams &chainparams, CBlockIndex **ppindex, bool fRequested, const CDiskBlockPos *dbp, bool *fNewBlock) { if (fNewBlock) *fNewBlock = false; AssertLockHeld(cs_main); @@ -4655,7 +4840,8 @@ bool ProcessNewBlock(CValidationState &state, const CChainParams &chainparams, C return true; } -bool TestBlockValidity(CValidationState &state, const CChainParams &chainparams, const CBlock &block, CBlockIndex *pindexPrev, bool fCheckPOW, bool fCheckMerkleRoot) { +bool TestBlockValidity(CValidationState &state, const CChainParams &chainparams, const CBlock &block, + CBlockIndex *pindexPrev, bool fCheckPOW, bool fCheckMerkleRoot) { AssertLockHeld(cs_main); assert(pindexPrev && pindexPrev == chainActive.Tip()); if (fCheckpointsEnabled && !CheckIndexAgainstCheckpoint(pindexPrev, state, chainparams, block.GetHash())) @@ -4858,13 +5044,15 @@ bool static LoadBlockIndexDB() { // Calculate nChainWork vector > vSortedByHeight; vSortedByHeight.reserve(mapBlockIndex.size()); - BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*) &item, mapBlockIndex) + BOOST_FOREACH( + const PAIRTYPE(uint256, CBlockIndex*) &item, mapBlockIndex) { CBlockIndex *pindex = item.second; vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex)); } sort(vSortedByHeight.begin(), vSortedByHeight.end()); - BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*) &item, vSortedByHeight) + BOOST_FOREACH( + const PAIRTYPE(int, CBlockIndex*) &item, vSortedByHeight) { CBlockIndex *pindex = item.second; pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex); @@ -5253,7 +5441,8 @@ bool LoadExternalBlockFile(const CChainParams &chainparams, FILE *fileIn, CDiskB int nLoaded = 0; try { // This takes over fileIn and calls fclose() on it in the CBufferedFile destructor - CBufferedFile blkdat(fileIn, 2 * MAX_BLOCK_SERIALIZED_SIZE, MAX_BLOCK_SERIALIZED_SIZE + 8, SER_DISK, CLIENT_VERSION); + CBufferedFile blkdat(fileIn, 2 * MAX_BLOCK_SERIALIZED_SIZE, MAX_BLOCK_SERIALIZED_SIZE + 8, SER_DISK, + CLIENT_VERSION); uint64_t nRewind = blkdat.GetPos(); while (!blkdat.eof()) { boost::this_thread::interruption_point(); @@ -5323,7 +5512,7 @@ bool LoadExternalBlockFile(const CChainParams &chainparams, FILE *fileIn, CDiskB } else if (hash != chainparams.GetConsensus().hashGenesisBlock && mapBlockIndex[hash]->nHeight % 1000 == 0) { LogPrintf("Block Import: already had block %s at height %d\n", hash.ToString(), - mapBlockIndex[hash]->nHeight); + mapBlockIndex[hash]->nHeight); } // Activate the genesis block so normal node progress can continue @@ -6179,8 +6368,7 @@ bool static ProcessMessage(CNode *pfrom, string strCommand, CDataStream &vRecv, if (fBlocksOnly) { LogPrint("net", "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->id); - } - else if (!fAlreadyHave && !fImporting && !fReindex && !IsInitialBlockDownload()) + } else if (!fAlreadyHave && !fImporting && !fReindex && !IsInitialBlockDownload()) pfrom->AskFor(inv); } @@ -6243,7 +6431,7 @@ bool static ProcessMessage(CNode *pfrom, string strCommand, CDataStream &vRecv, if (fPruneMode && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= chainActive.Tip()->nHeight - nPrunedBlocksLikelyToHave)) { LogPrintf("getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, - pindex->GetBlockHash().ToString()); + pindex->GetBlockHash().ToString()); break; } pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); @@ -6341,7 +6529,8 @@ bool static ProcessMessage(CNode *pfrom, string strCommand, CDataStream &vRecv, // pindexBestHeaderSent to be our tip. nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip(); pfrom->PushMessage(NetMsgType::HEADERS, vHeaders); - } else if (strCommand == NetMsgType::TX) { + } else if (strCommand == NetMsgType::TX || strCommand == NetMsgType::DSTX || + strCommand == NetMsgType::TXLOCKREQUEST) { // Stop processing the transaction early if // We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off if (!fRelayTxes && (!pfrom->fWhitelisted || !GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))) { @@ -6351,13 +6540,68 @@ bool static ProcessMessage(CNode *pfrom, string strCommand, CDataStream &vRecv, deque vWorkQueue; vector vEraseQueue; + CTxLockRequest txLockRequest; + CDarksendBroadcastTx dstx; + int nInvType = MSG_TX; CTransaction tx; - vRecv >> tx; +// vRecv >> tx; LogPrintf("ProcessMessage() txHash=%s\n", tx.GetHash().ToString()); + // Read data and assign inv type + if (strCommand == NetMsgType::TX) { + vRecv >> tx; + } else if (strCommand == NetMsgType::TXLOCKREQUEST) { + vRecv >> txLockRequest; + tx = txLockRequest; + nInvType = MSG_TXLOCK_REQUEST; + } else if (strCommand == NetMsgType::DSTX) { + vRecv >> dstx; + tx = dstx.tx; + nInvType = MSG_DSTX; + } + CInv inv(MSG_TX, tx.GetHash()); pfrom->AddInventoryKnown(inv); + // Process custom logic, no matter if tx will be accepted to mempool later or not + if (strCommand == NetMsgType::TXLOCKREQUEST) { + if (!instantsend.ProcessTxLockRequest(txLockRequest)) { + LogPrint("instantsend", "TXLOCKREQUEST -- failed %s\n", txLockRequest.GetHash().ToString()); + return false; + } + } else if (strCommand == NetMsgType::DSTX) { + uint256 hashTx = tx.GetHash(); + + if (mapDarksendBroadcastTxes.count(hashTx)) { + LogPrint("privatesend", "DSTX -- Already have %s, skipping...\n", hashTx.ToString()); + return true; // not an error + } + + CZnode *pmn = mnodeman.Find(dstx.vin); + if (pmn == NULL) { + LogPrint("privatesend", "DSTX -- Can't find masternode %s to verify %s\n", + dstx.vin.prevout.ToStringShort(), hashTx.ToString()); + return false; + } + + if (!pmn->fAllowMixingTx) { + LogPrint("privatesend", "DSTX -- Masternode %s is sending too many transactions %s\n", + dstx.vin.prevout.ToStringShort(), hashTx.ToString()); + return true; + // TODO: Not an error? Could it be that someone is relaying old DSTXes + // we have no idea about (e.g we were offline)? How to handle them? + } + + if (!dstx.CheckSignature(pmn->pubKeyZnode)) { + LogPrint("privatesend", "DSTX -- CheckSignature() failed for %s\n", hashTx.ToString()); + return false; + } + + LogPrintf("DSTX -- Got Masternode transaction %s\n", hashTx.ToString()); + mempool.PrioritiseTransaction(hashTx, hashTx.ToString(), 1000, 0.1 * COIN); + pmn->fAllowMixingTx = false; + } + LOCK(cs_main); bool fMissingInputs = false; @@ -6366,8 +6610,9 @@ bool static ProcessMessage(CNode *pfrom, string strCommand, CDataStream &vRecv, pfrom->setAskFor.erase(inv.hash); mapAlreadyAskedFor.erase(inv.hash); - //&& !AlreadyHave(inv) - if (!tx.IsZerocoinSpend() && AcceptToMemoryPool(mempool, state, tx, true, true, &fMissingInputs, false, 0, true)) { + //&& + if (!AlreadyHave(inv) && !tx.IsZerocoinSpend() && + AcceptToMemoryPool(mempool, state, tx, true, true, &fMissingInputs, false, 0, true)) { // mempool.check(pcoinsTip); RelayTransaction(tx); for (unsigned int i = 0; i < tx.vout.size(); i++) { @@ -6403,7 +6648,8 @@ bool static ProcessMessage(CNode *pfrom, string strCommand, CDataStream &vRecv, if (setMisbehaving.count(fromPeer)) continue; - if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, true, &fMissingInputs2, false, 0, true)) { + if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, true, &fMissingInputs2, false, 0, + true)) { // LogPrintf("Accepted orphan tx %s\n", orphanHash.ToString()); RelayTransaction(orphanTx); for (unsigned int i = 0; i < orphanTx.vout.size(); i++) { @@ -6434,11 +6680,13 @@ bool static ProcessMessage(CNode *pfrom, string strCommand, CDataStream &vRecv, } } - BOOST_FOREACH(uint256 hash, vEraseQueue) - EraseOrphanTx(hash); + BOOST_FOREACH(uint256 + hash, vEraseQueue) + EraseOrphanTx(hash); //btzc: zcoin condition //&& !AlreadyHave(inv) - } else if (tx.IsZerocoinSpend() && AcceptToMemoryPool(mempool, state, tx, false, true, &fMissingInputsZerocoin, false, 0, true)) { + } else if (!AlreadyHave(inv) && tx.IsZerocoinSpend() && + AcceptToMemoryPool(mempool, state, tx, false, true, &fMissingInputsZerocoin, false, 0, true)) { RelayTransaction(tx); // LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n", // pfrom->id, @@ -6447,7 +6695,8 @@ bool static ProcessMessage(CNode *pfrom, string strCommand, CDataStream &vRecv, } else if (fMissingInputs) { bool fRejectedParents = false; // It may be the case that the orphans parents have all been rejected - BOOST_FOREACH(const CTxIn &txin, tx.vin) { + BOOST_FOREACH( + const CTxIn &txin, tx.vin) { if (recentRejects->contains(txin.prevout.hash)) { fRejectedParents = true; break; @@ -6791,7 +7040,8 @@ bool static ProcessMessage(CNode *pfrom, string strCommand, CDataStream &vRecv, LogPrintf("ProcessMessage.AcceptBlockHeader() total %s blocks\n", headers.size()); CBlockIndex *pindexLast = NULL; - BOOST_FOREACH(const CBlockHeader &header, headers) { + BOOST_FOREACH( + const CBlockHeader &header, headers) { CValidationState state; // int64_t start = std::chrono::duration_cast( // std::chrono::system_clock::now().time_since_epoch()).count(); @@ -7107,10 +7357,29 @@ bool static ProcessMessage(CNode *pfrom, string strCommand, CDataStream &vRecv, // message would be undesirable as we transmit it ourselves. } else { // Ignore unknown commands for extensibility -// LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id); - } - + bool found = false; + const std::vector &allMessages = getAllNetMessageTypes(); + BOOST_FOREACH( + const std::string msg, allMessages) { + if (msg == strCommand) { + found = true; + break; + } + } + if (found) { + //probably one the extensions + darkSendPool.ProcessMessage(pfrom, strCommand, vRecv); + mnodeman.ProcessMessage(pfrom, strCommand, vRecv); + mnpayments.ProcessMessage(pfrom, strCommand, vRecv); + instantsend.ProcessMessage(pfrom, strCommand, vRecv); + sporkManager.ProcessSpork(pfrom, strCommand, vRecv); + znodeSync.ProcessMessage(pfrom, strCommand, vRecv); + } else { + // Ignore unknown commands for extensibility + LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id); + } + } return true; } @@ -7188,13 +7457,17 @@ bool ProcessMessages(CNode *pfrom) { pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, string("error parsing message")); if (strstr(e.what(), "end of data")) { // Allow exceptions from under-length message on vRecv - LogPrintf("%s(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", __func__, SanitizeString(strCommand), nMessageSize, e.what()); + LogPrintf( + "%s(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", + __func__, SanitizeString(strCommand), nMessageSize, e.what()); } else if (strstr(e.what(), "size too large")) { // Allow exceptions from over-long size - LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what()); + LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), + nMessageSize, e.what()); } else if (strstr(e.what(), "non-canonical ReadCompactSize()")) { // Allow exceptions from non-canonical encoding - LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what()); + LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), + nMessageSize, e.what()); } else { PrintExceptionContinue(&e, "ProcessMessages() 1"); } @@ -7209,7 +7482,8 @@ bool ProcessMessages(CNode *pfrom) { } if (!fRet) - LogPrintf("%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->id); + LogPrintf("%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, + pfrom->id); break; } @@ -7388,7 +7662,8 @@ bool SendMessages(CNode *pto) { // Try to find first header that our peer doesn't have, and // then send all headers past that one. If we come across any // headers that aren't on chainActive, give up. - BOOST_FOREACH(const uint256 &hash, pto->vBlockHashesToAnnounce) { + BOOST_FOREACH( + const uint256 &hash, pto->vBlockHashesToAnnounce) { BlockMap::iterator mi = mapBlockIndex.find(hash); assert(mi != mapBlockIndex.end()); CBlockIndex *pindex = mi->second; diff --git a/src/main.h b/src/main.h index 16b4923c30..b634a3a2d5 100644 --- a/src/main.h +++ b/src/main.h @@ -163,6 +163,28 @@ static std::map mapBlockData; static const bool DEFAULT_PEERBLOOMFILTERS = true; +// There were bugs before this block, don't do some checks on early blocks +#define ZC_CHECK_BUG_FIXED_AT_BLOCK 60000 + +// The mint id number to change to zerocoin v2 +#define ZC_V2_SWITCH_ID_1 120 +#define ZC_V2_SWITCH_ID_10 30 +#define ZC_V2_SWITCH_ID_25 15 +#define ZC_V2_SWITCH_ID_50 15 +#define ZC_V2_SWITCH_ID_100 30 + +// Block Height Lyra2Z +#define LYRA2Z_HEIGHT 20500 + +// Block Height Limit Spend One TX Per Block +#define OLD_LIMIT_SPEND_TXS 22000 + +// Add more spend txs per block at block height +#define SWITCH_TO_MORE_SPEND_TXS 60000 + +// Enabled lowest diff for test local env +#define ENABLED_LOWEST_DIFF false + struct BlockHasher { size_t operator()(const uint256& hash) const { return hash.GetCheapHash(); } @@ -536,6 +558,9 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc /** Mark a block as invalid. */ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex); +/** Remove invalidity status from a block and its descendants. */ +bool ReconsiderBlock(CValidationState& state, CBlockIndex *pindex); + /** Remove invalidity status from a block and its descendants. */ bool ResetBlockFailureFlags(CBlockIndex *pindex); diff --git a/src/miner.cpp b/src/miner.cpp index e27905b4b3..bf8caf31ba 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -30,7 +30,8 @@ #include "crypto/scrypt.h" #include "crypto/Lyra2Z/Lyra2Z.h" #include "crypto/Lyra2Z/Lyra2.h" - +#include "znode-payments.h" +#include "znode-sync.h" #include #include #include @@ -139,7 +140,7 @@ CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn) return NULL; CBlock *pblock = &pblocktemplate->block; // pointer for convenience // Create coinbase tx - CTransaction txNew; + CMutableTransaction txNew; txNew.vin.resize(1); txNew.vin[0].prevout.SetNull(); txNew.vout.resize(1); @@ -212,6 +213,9 @@ CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn) if(fTestNet || nHeight > HF_ZEROSPEND_FIX){ MAX_SPEND_ZC_TX_PER_BLOCK = 1; } + if(fTestNet || nHeight > SWITCH_TO_MORE_SPEND_TXS){ + MAX_SPEND_ZC_TX_PER_BLOCK = 1; + } // Collect memory pool transactions into the block CTxMemPool::setEntries inBlock; @@ -446,13 +450,17 @@ CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn) } } } + CAmount blockReward = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus()); + // Update coinbase transaction with additional info about masternode and governance payments, + // get some info back to pass to getblocktemplate + FillBlockPayments(txNew, nHeight, blockReward, pblock->txoutZnode, pblock->voutSuperblock); nLastBlockTx = nBlockTx; nLastBlockSize = nBlockSize; LogPrintf("CreateNewBlock(): total size %u txs: %u fees: %ld sigops %d\n", nBlockSize, nBlockTx, nFees, nBlockSigOps); // Compute final coinbase transaction. - txNew.vout[0].nValue += nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus()); + txNew.vout[0].nValue += blockReward; txNew.vin[0].scriptSig = CScript() << nHeight << OP_0; pblock->vtx[0] = txNew; pblocktemplate->vTxFees[0] = -nFees; @@ -940,7 +948,10 @@ void BlockAssembler::addPriorityTxs() unsigned int COUNT_SPEND_ZC_TX = 0; unsigned int MAX_SPEND_ZC_TX_PER_BLOCK = 0; - if (nHeight + 1 > HF_ZEROSPEND_FIX) { + if (chainActive.Height() + 1 > HF_ZEROSPEND_FIX) { + MAX_SPEND_ZC_TX_PER_BLOCK = 1; + } + if (nHeight + 1 > SWITCH_TO_MORE_SPEND_TXS) { MAX_SPEND_ZC_TX_PER_BLOCK = 1; } @@ -1182,14 +1193,14 @@ void static ZcoinMiner(const CChainParams &chainparams) { } else if (!fTestNet && pindexPrev->nHeight + 1 >= HF_LYRA2_HEIGHT) { LYRA2(BEGIN(thash), 32, BEGIN(pblock->nVersion), 80, BEGIN(pblock->nVersion), 80, 2, 8192, 256); } else if (!fTestNet && pindexPrev->nHeight + 1 >= HF_LYRA2VAR_HEIGHT) { - LYRA2(BEGIN(thash), 32, BEGIN(pblock->nVersion), 80, BEGIN(pblock->nVersion), 80, 2, pindexPrev->nHeight + 1, 256); + LYRA2(BEGIN(thash), 32, BEGIN(pblock->nVersion), 80, BEGIN(pblock->nVersion), 80, 2, + pindexPrev->nHeight + 1, 256); } else if (fTestNet && pindexPrev->nHeight + 1 >= HF_LYRA2Z_HEIGHT_TESTNET) { // testnet lyra2z_hash(BEGIN(pblock->nVersion), BEGIN(thash)); } else if (fTestNet && pindexPrev->nHeight + 1 >= HF_LYRA2_HEIGHT_TESTNET) { // testnet LYRA2(BEGIN(thash), 32, BEGIN(pblock->nVersion), 80, BEGIN(pblock->nVersion), 80, 2, 8192, 256); } else if (fTestNet && pindexPrev->nHeight + 1 >= HF_LYRA2VAR_HEIGHT_TESTNET) { // testnet LYRA2(BEGIN(thash), 32, BEGIN(pblock->nVersion), 80, BEGIN(pblock->nVersion), 80, 2, pindexPrev->nHeight + 1, 256); - } else { unsigned long int scrypt_scratpad_size_current_block = ((1 << (GetNfactor(pblock->nTime) + 1)) * 128) + 63; diff --git a/src/net.cpp b/src/net.cpp index 504de361c9..09e13f425a 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -24,7 +24,9 @@ #ifdef WIN32 #include #else + #include + #endif #ifdef USE_UPNP @@ -86,24 +88,24 @@ bool fListen = true; ServiceFlags nLocalServices = NODE_NETWORK; bool fRelayTxes = true; CCriticalSection cs_mapLocalHost; -std::map mapLocalHost; +std::map mapLocalHost; static bool vfLimited[NET_MAX] = {}; -static CNode* pnodeLocalHost = NULL; +static CNode *pnodeLocalHost = NULL; uint64_t nLocalHostNonce = 0; -static std::vector vhListenSocket; +static std::vector vhListenSocket; CAddrMan addrman; int nMaxConnections = DEFAULT_MAX_PEER_CONNECTIONS; bool fAddressesInitialized = false; std::string strSubVersion; -std::vector vNodes; +std::vector vNodes; CCriticalSection cs_vNodes; limitedmap mapAlreadyAskedFor(MAX_INV_SZ); -static std::deque vOneShots; +static std::deque vOneShots; CCriticalSection cs_vOneShots; -std::vector vAddedNodes; +std::vector vAddedNodes; CCriticalSection cs_vAddedNodes; NodeId nLastNodeId = 0; @@ -114,22 +116,20 @@ boost::condition_variable messageHandlerCondition; // Signals for message handling static CNodeSignals g_signals; -CNodeSignals& GetNodeSignals() { return g_signals; } -void AddOneShot(const std::string& strDest) -{ +CNodeSignals &GetNodeSignals() { return g_signals; } + +void AddOneShot(const std::string &strDest) { LOCK(cs_vOneShots); vOneShots.push_back(strDest); } -unsigned short GetListenPort() -{ - return (unsigned short)(GetArg("-port", Params().GetDefaultPort())); +unsigned short GetListenPort() { + return (unsigned short) (GetArg("-port", Params().GetDefaultPort())); } // find 'best' local address for a particular peer -bool GetLocal(CService& addr, const CNetAddr *paddrPeer) -{ +bool GetLocal(CService &addr, const CNetAddr *paddrPeer) { if (!fListen) return false; @@ -137,12 +137,10 @@ bool GetLocal(CService& addr, const CNetAddr *paddrPeer) int nBestReachability = -1; { LOCK(cs_mapLocalHost); - for (std::map::iterator it = mapLocalHost.begin(); it != mapLocalHost.end(); it++) - { + for (std::map::iterator it = mapLocalHost.begin(); it != mapLocalHost.end(); it++) { int nScore = (*it).second.nScore; int nReachability = (*it).first.GetReachabilityFrom(paddrPeer); - if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore)) - { + if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore)) { addr = CService((*it).first, (*it).second.nPort); nBestReachability = nReachability; nBestScore = nScore; @@ -153,17 +151,15 @@ bool GetLocal(CService& addr, const CNetAddr *paddrPeer) } //! Convert the pnSeeds6 array into usable address objects. -static std::vector convertSeed6(const std::vector &vSeedsIn) -{ +static std::vector convertSeed6(const std::vector &vSeedsIn) { // It'll only connect to one or two seed nodes because once it connects, // it'll get a pile of addresses with newer timestamps. // Seed nodes are given a random 'last seen time' of between one and two // weeks ago. - const int64_t nOneWeek = 7*24*60*60; - std::vector vSeedsOut; + const int64_t nOneWeek = 7 * 24 * 60 * 60; + std::vector vSeedsOut; vSeedsOut.reserve(vSeedsIn.size()); - for (std::vector::const_iterator i(vSeedsIn.begin()); i != vSeedsIn.end(); ++i) - { + for (std::vector::const_iterator i(vSeedsIn.begin()); i != vSeedsIn.end(); ++i) { struct in6_addr ip; memcpy(&ip, i->addr, sizeof(ip)); CAddress addr(CService(ip, i->port), NODE_NETWORK); @@ -177,20 +173,17 @@ static std::vector convertSeed6(const std::vector &vSeedsIn // Otherwise, return the unroutable 0.0.0.0 but filled in with // the normal parameters, since the IP may be changed to a useful // one by discovery. -CAddress GetLocalAddress(const CNetAddr *paddrPeer) -{ - CAddress ret(CService("0.0.0.0",GetListenPort()), nLocalServices); +CAddress GetLocalAddress(const CNetAddr *paddrPeer) { + CAddress ret(CService("0.0.0.0", GetListenPort()), nLocalServices); CService addr; - if (GetLocal(addr, paddrPeer)) - { + if (GetLocal(addr, paddrPeer)) { ret = CAddress(addr, nLocalServices); } ret.nTime = GetAdjustedTime(); return ret; } -int GetnScore(const CService& addr) -{ +int GetnScore(const CService &addr) { LOCK(cs_mapLocalHost); if (mapLocalHost.count(addr) == LOCAL_NONE) return 0; @@ -198,28 +191,23 @@ int GetnScore(const CService& addr) } // Is our peer's addrLocal potentially useful as an external IP source? -bool IsPeerAddrLocalGood(CNode *pnode) -{ +bool IsPeerAddrLocalGood(CNode *pnode) { return fDiscover && pnode->addr.IsRoutable() && pnode->addrLocal.IsRoutable() && !IsLimited(pnode->addrLocal.GetNetwork()); } // pushes our own address to a peer -void AdvertiseLocal(CNode *pnode) -{ - if (fListen && pnode->fSuccessfullyConnected) - { +void AdvertiseLocal(CNode *pnode) { + if (fListen && pnode->fSuccessfullyConnected) { CAddress addrLocal = GetLocalAddress(&pnode->addr); // If discovery is enabled, sometimes give our peer the address it // tells us that it sees us as in case it has a better idea of our // address than we do. if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() || - GetRand((GetnScore(addrLocal) > LOCAL_MANUAL) ? 8:2) == 0)) - { + GetRand((GetnScore(addrLocal) > LOCAL_MANUAL) ? 8 : 2) == 0)) { addrLocal.SetIP(pnode->addrLocal); } - if (addrLocal.IsRoutable()) - { + if (addrLocal.IsRoutable()) { LogPrintf("AdvertiseLocal: advertising address %s\n", addrLocal.ToString()); pnode->PushAddress(addrLocal); } @@ -227,8 +215,7 @@ void AdvertiseLocal(CNode *pnode) } // learn a new local address -bool AddLocal(const CService& addr, int nScore) -{ +bool AddLocal(const CService &addr, int nScore) { if (!addr.IsRoutable()) return false; @@ -253,13 +240,11 @@ bool AddLocal(const CService& addr, int nScore) return true; } -bool AddLocal(const CNetAddr &addr, int nScore) -{ +bool AddLocal(const CNetAddr &addr, int nScore) { return AddLocal(CService(addr, GetListenPort()), nScore); } -bool RemoveLocal(const CService& addr) -{ +bool RemoveLocal(const CService &addr) { LOCK(cs_mapLocalHost); LogPrintf("RemoveLocal(%s)\n", addr.ToString()); mapLocalHost.erase(addr); @@ -267,28 +252,24 @@ bool RemoveLocal(const CService& addr) } /** Make a particular network entirely off-limits (no automatic connects to it) */ -void SetLimited(enum Network net, bool fLimited) -{ +void SetLimited(enum Network net, bool fLimited) { if (net == NET_UNROUTABLE) return; LOCK(cs_mapLocalHost); vfLimited[net] = fLimited; } -bool IsLimited(enum Network net) -{ +bool IsLimited(enum Network net) { LOCK(cs_mapLocalHost); return vfLimited[net]; } -bool IsLimited(const CNetAddr &addr) -{ +bool IsLimited(const CNetAddr &addr) { return IsLimited(addr.GetNetwork()); } /** vote for a local address */ -bool SeenLocal(const CService& addr) -{ +bool SeenLocal(const CService &addr) { { LOCK(cs_mapLocalHost); if (mapLocalHost.count(addr) == 0) @@ -300,28 +281,24 @@ bool SeenLocal(const CService& addr) /** check whether a given address is potentially local */ -bool IsLocal(const CService& addr) -{ +bool IsLocal(const CService &addr) { LOCK(cs_mapLocalHost); return mapLocalHost.count(addr) > 0; } /** check whether a given network is one we can probably connect to */ -bool IsReachable(enum Network net) -{ +bool IsReachable(enum Network net) { LOCK(cs_mapLocalHost); return !vfLimited[net]; } /** check whether a given address is in a network we can probably connect to */ -bool IsReachable(const CNetAddr& addr) -{ +bool IsReachable(const CNetAddr &addr) { enum Network net = addr.GetNetwork(); return IsReachable(net); } -void AddressCurrentlyConnected(const CService& addr) -{ +void AddressCurrentlyConnected(const CService &addr) { addrman.Connected(addr); } @@ -333,65 +310,65 @@ CCriticalSection CNode::cs_totalBytesSent; uint64_t CNode::nMaxOutboundLimit = 0; uint64_t CNode::nMaxOutboundTotalBytesSentInCycle = 0; -uint64_t CNode::nMaxOutboundTimeframe = 60*60*24; //1 day +uint64_t CNode::nMaxOutboundTimeframe = 60 * 60 * 24; //1 day uint64_t CNode::nMaxOutboundCycleStartTime = 0; -CNode* FindNode(const CNetAddr& ip) -{ +CNode *FindNode(const CNetAddr &ip) { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - if ((CNetAddr)pnode->addr == ip) - return (pnode); + BOOST_FOREACH(CNode * pnode, vNodes) + if ((CNetAddr) pnode->addr == ip) + return (pnode); return NULL; } -CNode* FindNode(const CSubNet& subNet) -{ +CNode *FindNode(const CSubNet &subNet) { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - if (subNet.Match((CNetAddr)pnode->addr)) + BOOST_FOREACH(CNode * pnode, vNodes) + if (subNet.Match((CNetAddr) pnode->addr)) return (pnode); return NULL; } -CNode* FindNode(const std::string& addrName) -{ +CNode *FindNode(const std::string &addrName) { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode->addrName == addrName) - return (pnode); + BOOST_FOREACH(CNode * pnode, vNodes) + if (pnode->addrName == addrName) + return (pnode); return NULL; } -CNode* FindNode(const CService& addr) -{ +CNode *FindNode(const CService &addr) { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - if ((CService)pnode->addr == addr) - return (pnode); + BOOST_FOREACH(CNode * pnode, vNodes) + if ((CService) pnode->addr == addr) + return (pnode); return NULL; } //TODO: This is used in only one place in main, and should be removed -CNode* FindNode(const NodeId nodeid) -{ +CNode *FindNode(const NodeId nodeid) { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode->GetId() == nodeid) - return (pnode); + BOOST_FOREACH(CNode * pnode, vNodes) + if (pnode->GetId() == nodeid) + return (pnode); return NULL; } -CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure) -{ +CNode *ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool fConnectToZnode) { if (pszDest == NULL) { - if (IsLocal(addrConnect)) + // we clean znode connections in CZnodeMan::ProcessZnodeConnections() + // so should be safe to skip this and connect to local Hot MN on CActiveZnode::ManageState() + if (IsLocal(addrConnect) && !fConnectToZnode) return NULL; - + LOCK(cs_vNodes); // Look for an existing connection - CNode* pnode = FindNode((CService)addrConnect); - if (pnode) - { + CNode *pnode = FindNode((CService) addrConnect); + if (pnode) { + // we have existing connection to this node but it was not a connection to znode, + // change flag and add reference so that we can correctly clear it later + if (fConnectToZnode && !pnode->fZnode) { + pnode->fZnode = true; + } pnode->AddRef(); return pnode; } @@ -399,15 +376,15 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure /// debug print LogPrint("net", "trying connection %s lastseen=%.1fhrs\n", - pszDest ? pszDest : addrConnect.ToString(), - pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0); + pszDest ? pszDest : addrConnect.ToString(), + pszDest ? 0.0 : (double) (GetAdjustedTime() - addrConnect.nTime) / 3600.0); // Connect SOCKET hSocket; bool proxyConnectionFailed = false; - if (pszDest ? ConnectSocketByName(addrConnect, hSocket, pszDest, Params().GetDefaultPort(), nConnectTimeout, &proxyConnectionFailed) : - ConnectSocket(addrConnect, hSocket, nConnectTimeout, &proxyConnectionFailed)) - { + if (pszDest ? ConnectSocketByName(addrConnect, hSocket, pszDest, Params().GetDefaultPort(), nConnectTimeout, + &proxyConnectionFailed) : + ConnectSocket(addrConnect, hSocket, nConnectTimeout, &proxyConnectionFailed)) { if (!IsSelectableSocket(hSocket)) { LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n"); CloseSocket(hSocket); @@ -419,9 +396,11 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure // In that case, drop the connection that was just created, and return the existing CNode instead. // Also store the name we used to connect in that CNode, so that future FindNode() calls to that // name catch this early. - CNode* pnode = FindNode((CService)addrConnect); - if (pnode) - { + CNode *pnode = FindNode((CService) addrConnect); + if (pnode) { + if (fConnectToZnode && !pnode->fZnode) { + pnode->fZnode = true; + } pnode->AddRef(); { LOCK(cs_vNodes); @@ -437,7 +416,10 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure addrman.Attempt(addrConnect, fCountFailure); // Add node - CNode* pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false); + CNode *pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false); + if (fConnectToZnode) { + pnode->fZnode = true; + } pnode->AddRef(); { @@ -458,74 +440,8 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure return NULL; } -CNode* ConnectNodeDash(CAddress addrConnect, const char *pszDest, bool fConnectToZnode) -{ - if (pszDest == NULL) { - // we clean znode connections in CZnodeMan::ProcessZnodeConnections() - // so should be safe to skip this and connect to local Hot MN on CActiveZnode::ManageState() - if (IsLocal(addrConnect) && !fConnectToZnode) - return NULL; - - LOCK(cs_vNodes); - // Look for an existing connection - CNode* pnode = FindNode((CService)addrConnect); - if (pnode) - { - // we have existing connection to this node but it was not a connection to znode, - // change flag and add reference so that we can correctly clear it later - if(fConnectToZnode && !pnode->fZnode) { - pnode->AddRef(); - pnode->fZnode = true; - } - return pnode; - } - } - /// debug print - LogPrint("net", "trying connection %s lastseen=%.1fhrs\n", - pszDest ? pszDest : addrConnect.ToString(), - pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0); - - // Connect - SOCKET hSocket; - bool proxyConnectionFailed = false; - if (pszDest ? ConnectSocketByName(addrConnect, hSocket, pszDest, Params().GetDefaultPort(), nConnectTimeout, &proxyConnectionFailed) : - ConnectSocket(addrConnect, hSocket, nConnectTimeout, &proxyConnectionFailed)) - { - if (!IsSelectableSocket(hSocket)) { - LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n"); - CloseSocket(hSocket); - return NULL; - } - - addrman.Attempt(addrConnect, fConnectToZnode); - - // Add node -// CNode* pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false, true); - CNode* pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false); - - pnode->nTimeConnected = GetTime(); - if(fConnectToZnode) { - pnode->AddRef(); - pnode->fZnode = true; - } - - LOCK(cs_vNodes); - vNodes.push_back(pnode); - - return pnode; - } else if (!proxyConnectionFailed) { - // If connecting to the node failed, and failure is not caused by a problem connecting to - // the proxy, mark this as an attempt. - addrman.Attempt(addrConnect, fConnectToZnode); - } - - return NULL; -} - - -static void DumpBanlist() -{ +static void DumpBanlist() { CNode::SweepBanned(); // clean unused entries (if bantime has expired) if (!CNode::BannedSetIsDirty()) @@ -541,14 +457,12 @@ static void DumpBanlist() CNode::SetBannedSetDirty(true); LogPrint("net", "Flushed %d banned node ips/subnets to banlist.dat %dms\n", - banmap.size(), GetTimeMillis() - nStart); + banmap.size(), GetTimeMillis() - nStart); } -void CNode::CloseSocketDisconnect() -{ +void CNode::CloseSocketDisconnect() { fDisconnect = true; - if (hSocket != INVALID_SOCKET) - { + if (hSocket != INVALID_SOCKET) { LogPrint("net", "disconnecting peer=%d\n", id); CloseSocket(hSocket); } @@ -559,19 +473,20 @@ void CNode::CloseSocketDisconnect() vRecvMsg.clear(); } -void CNode::PushVersion() -{ +void CNode::PushVersion() { int nBestHeight = GetNodeSignals().GetHeight().get_value_or(0); int64_t nTime = (fInbound ? GetAdjustedTime() : GetTime()); CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0", 0), addr.nServices)); CAddress addrMe = CAddress(CService(), nLocalServices); - GetRandBytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); + GetRandBytes((unsigned char *) &nLocalHostNonce, sizeof(nLocalHostNonce)); if (fLogIPs) - LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), id); + LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, + nBestHeight, addrMe.ToString(), addrYou.ToString(), id); else - LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), id); - PushMessage(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalServices, nTime, addrYou, addrMe, + LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, + addrMe.ToString(), id); + PushMessage(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t) nLocalServices, nTime, addrYou, addrMe, nLocalHostNonce, strSubVersion, nBestHeight, ::fRelayTxes); } @@ -580,8 +495,7 @@ banmap_t CNode::setBanned; CCriticalSection CNode::cs_setBanned; bool CNode::setBannedIsDirty; -void CNode::ClearBanned() -{ +void CNode::ClearBanned() { { LOCK(cs_setBanned); setBanned.clear(); @@ -591,31 +505,27 @@ void CNode::ClearBanned() uiInterface.BannedListChanged(); } -bool CNode::IsBanned(CNetAddr ip) -{ +bool CNode::IsBanned(CNetAddr ip) { bool fResult = false; { LOCK(cs_setBanned); - for (banmap_t::iterator it = setBanned.begin(); it != setBanned.end(); it++) - { + for (banmap_t::iterator it = setBanned.begin(); it != setBanned.end(); it++) { CSubNet subNet = (*it).first; CBanEntry banEntry = (*it).second; - if(subNet.Match(ip) && GetTime() < banEntry.nBanUntil) + if (subNet.Match(ip) && GetTime() < banEntry.nBanUntil) fResult = true; } } return fResult; } -bool CNode::IsBanned(CSubNet subnet) -{ +bool CNode::IsBanned(CSubNet subnet) { bool fResult = false; { LOCK(cs_setBanned); banmap_t::iterator i = setBanned.find(subnet); - if (i != setBanned.end()) - { + if (i != setBanned.end()) { CBanEntry banEntry = (*i).second; if (GetTime() < banEntry.nBanUntil) fResult = true; @@ -624,39 +534,38 @@ bool CNode::IsBanned(CSubNet subnet) return fResult; } -void CNode::Ban(const CNetAddr& addr, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) { +void CNode::Ban(const CNetAddr &addr, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) { CSubNet subNet(addr); Ban(subNet, banReason, bantimeoffset, sinceUnixEpoch); } -void CNode::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) { +void CNode::Ban(const CSubNet &subNet, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) { CBanEntry banEntry(GetTime()); banEntry.banReason = banReason; - if (bantimeoffset <= 0) - { + if (bantimeoffset <= 0) { bantimeoffset = GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME); sinceUnixEpoch = false; } - banEntry.nBanUntil = (sinceUnixEpoch ? 0 : GetTime() )+bantimeoffset; + banEntry.nBanUntil = (sinceUnixEpoch ? 0 : GetTime()) + bantimeoffset; { LOCK(cs_setBanned); if (setBanned[subNet].nBanUntil < banEntry.nBanUntil) { setBanned[subNet] = banEntry; setBannedIsDirty = true; - } - else + } else return; } uiInterface.BannedListChanged(); { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) { - if (subNet.Match((CNetAddr)pnode->addr)) + BOOST_FOREACH(CNode * pnode, vNodes) + { + if (subNet.Match((CNetAddr) pnode->addr)) pnode->fDisconnect = true; } } - if(banReason == BanReasonManuallyAdded) + if (banReason == BanReasonManuallyAdded) DumpBanlist(); //store banlist to disk immediately if user requested ban } @@ -677,59 +586,52 @@ bool CNode::Unban(const CSubNet &subNet) { return true; } -void CNode::GetBanned(banmap_t &banMap) -{ +void CNode::GetBanned(banmap_t &banMap) { LOCK(cs_setBanned); banMap = setBanned; //create a thread safe copy } -void CNode::SetBanned(const banmap_t &banMap) -{ +void CNode::SetBanned(const banmap_t &banMap) { LOCK(cs_setBanned); setBanned = banMap; setBannedIsDirty = true; } -void CNode::SweepBanned() -{ +void CNode::SweepBanned() { int64_t now = GetTime(); LOCK(cs_setBanned); banmap_t::iterator it = setBanned.begin(); - while(it != setBanned.end()) - { + while (it != setBanned.end()) { CSubNet subNet = (*it).first; CBanEntry banEntry = (*it).second; - if(now > banEntry.nBanUntil) - { + if (now > banEntry.nBanUntil) { setBanned.erase(it++); setBannedIsDirty = true; LogPrint("net", "%s: Removed banned node ip/subnet from banlist.dat: %s\n", __func__, subNet.ToString()); - } - else + } else ++it; } } -bool CNode::BannedSetIsDirty() -{ +bool CNode::BannedSetIsDirty() { LOCK(cs_setBanned); return setBannedIsDirty; } -void CNode::SetBannedSetDirty(bool dirty) -{ +void CNode::SetBannedSetDirty(bool dirty) { LOCK(cs_setBanned); //reuse setBanned lock for the isDirty flag setBannedIsDirty = dirty; } -std::vector CNode::vWhitelistedRange; +std::vector CNode::vWhitelistedRange; CCriticalSection CNode::cs_vWhitelistedRange; bool CNode::IsWhitelistedRange(const CNetAddr &addr) { LOCK(cs_vWhitelistedRange); - BOOST_FOREACH(const CSubNet& subnet, vWhitelistedRange) { + BOOST_FOREACH( + const CSubNet &subnet, vWhitelistedRange) { if (subnet.Match(addr)) return true; } @@ -743,8 +645,8 @@ void CNode::AddWhitelistedRange(const CSubNet &subnet) { #undef X #define X(name) stats.name = name -void CNode::copyStats(CNodeStats &stats) -{ + +void CNode::copyStats(CNodeStats &stats) { stats.nodeid = this->GetId(); X(nServices); X(fRelayTxes); @@ -775,18 +677,18 @@ void CNode::copyStats(CNodeStats &stats) } // Raw ping time is in microseconds, but show it to user as whole seconds (Bitcoin users should be well used to small numbers with many decimal places by now :) - stats.dPingTime = (((double)nPingUsecTime) / 1e6); - stats.dPingMin = (((double)nMinPingUsecTime) / 1e6); - stats.dPingWait = (((double)nPingUsecWait) / 1e6); + stats.dPingTime = (((double) nPingUsecTime) / 1e6); + stats.dPingMin = (((double) nMinPingUsecTime) / 1e6); + stats.dPingWait = (((double) nPingUsecWait) / 1e6); // Leave string empty if addrLocal invalid (not filled in yet) stats.addrLocal = addrLocal.IsValid() ? addrLocal.ToString() : ""; } + #undef X // requires LOCK(cs_vRecvMsg) -bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes) -{ +bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes) { while (nBytes > 0) { // get current incomplete message, or create a new one @@ -794,7 +696,7 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes) vRecvMsg.back().complete()) vRecvMsg.push_back(CNetMessage(Params().MessageStart(), SER_NETWORK, nRecvVersion)); - CNetMessage& msg = vRecvMsg.back(); + CNetMessage &msg = vRecvMsg.back(); // absorb network data int handled; @@ -804,7 +706,7 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes) handled = msg.readData(pch, nBytes); if (handled < 0) - return false; + return false; if (msg.in_data && msg.hdr.nMessageSize > MAX_PROTOCOL_MESSAGE_LENGTH) { LogPrint("net", "Oversized message from peer=%i, disconnecting\n", GetId()); @@ -832,8 +734,7 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes) return true; } -int CNetMessage::readHeader(const char *pch, unsigned int nBytes) -{ +int CNetMessage::readHeader(const char *pch, unsigned int nBytes) { // copy data to temporary parsing buffer unsigned int nRemaining = 24 - nHdrPos; unsigned int nCopy = std::min(nRemaining, nBytes); @@ -849,13 +750,13 @@ int CNetMessage::readHeader(const char *pch, unsigned int nBytes) try { hdrbuf >> hdr; } - catch (const std::exception&) { + catch (const std::exception &) { return -1; } // reject messages larger than MAX_SIZE if (hdr.nMessageSize > MAX_SIZE) - return -1; + return -1; // switch state to reading message data in_data = true; @@ -863,8 +764,7 @@ int CNetMessage::readHeader(const char *pch, unsigned int nBytes) return nCopy; } -int CNetMessage::readData(const char *pch, unsigned int nBytes) -{ +int CNetMessage::readData(const char *pch, unsigned int nBytes) { unsigned int nRemaining = hdr.nMessageSize - nDataPos; unsigned int nCopy = std::min(nRemaining, nBytes); @@ -880,22 +780,15 @@ int CNetMessage::readData(const char *pch, unsigned int nBytes) } - - - - - - - // requires LOCK(cs_vSend) -void SocketSendData(CNode *pnode) -{ +void SocketSendData(CNode *pnode) { std::deque::iterator it = pnode->vSendMsg.begin(); while (it != pnode->vSendMsg.end()) { const CSerializeData &data = *it; assert(data.size() > pnode->nSendOffset); - int nBytes = send(pnode->hSocket, &data[pnode->nSendOffset], data.size() - pnode->nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT); + int nBytes = send(pnode->hSocket, &data[pnode->nSendOffset], data.size() - pnode->nSendOffset, + MSG_NOSIGNAL | MSG_DONTWAIT); if (nBytes > 0) { pnode->nLastSend = GetTime(); pnode->nSendBytes += nBytes; @@ -913,8 +806,7 @@ void SocketSendData(CNode *pnode) if (nBytes < 0) { // error int nErr = WSAGetLastError(); - if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) - { + if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) { LogPrintf("socket send error %s\n", NetworkErrorString(nErr)); pnode->CloseSocketDisconnect(); } @@ -931,10 +823,9 @@ void SocketSendData(CNode *pnode) pnode->vSendMsg.erase(pnode->vSendMsg.begin(), it); } -static std::list vNodesDisconnected; +static std::list vNodesDisconnected; -struct NodeEvictionCandidate -{ +struct NodeEvictionCandidate { NodeId id; int64_t nTimeConnected; int64_t nMinPingUsecTime; @@ -947,13 +838,11 @@ struct NodeEvictionCandidate uint64_t nKeyedNetGroup; }; -static bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) -{ +static bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { return a.nMinPingUsecTime > b.nMinPingUsecTime; } -static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) -{ +static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { return a.nTimeConnected > b.nTimeConnected; } @@ -961,16 +850,14 @@ static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvict return a.nKeyedNetGroup < b.nKeyedNetGroup; } -static bool CompareNodeBlockTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) -{ +static bool CompareNodeBlockTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { // There is a fall-through here because it is common for a node to have many peers which have not yet relayed a block. if (a.nLastBlockTime != b.nLastBlockTime) return a.nLastBlockTime < b.nLastBlockTime; if (a.fRelevantServices != b.fRelevantServices) return b.fRelevantServices; return a.nTimeConnected > b.nTimeConnected; } -static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) -{ +static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { // There is a fall-through here because it is common for a node to have more than a few peers that have not yet relayed txn. if (a.nLastTXTime != b.nLastTXTime) return a.nLastTXTime < b.nLastTXTime; if (a.fRelayTxes != b.fRelayTxes) return b.fRelayTxes; @@ -987,11 +874,12 @@ static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEviction * simultaneously better at all of them than honest peers. */ static bool AttemptToEvictConnection() { - std::vector vEvictionCandidates; + std::vector vEvictionCandidates; { LOCK(cs_vNodes); - BOOST_FOREACH(CNode *node, vNodes) { + BOOST_FOREACH(CNode * node, vNodes) + { if (node->fWhitelisted) continue; if (!node->fInbound) @@ -1001,7 +889,8 @@ static bool AttemptToEvictConnection() { NodeEvictionCandidate candidate = {node->id, node->nTimeConnected, node->nMinPingUsecTime, node->nLastBlockTime, node->nLastTXTime, (node->nServices & nRelevantServices) == nRelevantServices, - node->fRelayTxes, node->pfilter != NULL, node->addr, node->nKeyedNetGroup}; + node->fRelayTxes, node->pfilter != NULL, node->addr, + node->nKeyedNetGroup}; vEvictionCandidates.push_back(candidate); } } @@ -1013,35 +902,40 @@ static bool AttemptToEvictConnection() { // Deterministically select 4 peers to protect by netgroup. // An attacker cannot predict which netgroups will be protected std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNetGroupKeyed); - vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast(vEvictionCandidates.size())), vEvictionCandidates.end()); + vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast(vEvictionCandidates.size())), + vEvictionCandidates.end()); if (vEvictionCandidates.empty()) return false; // Protect the 8 nodes with the lowest minimum ping time. // An attacker cannot manipulate this metric without physically moving nodes closer to the target. std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeMinPingTime); - vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(8, static_cast(vEvictionCandidates.size())), vEvictionCandidates.end()); + vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(8, static_cast(vEvictionCandidates.size())), + vEvictionCandidates.end()); if (vEvictionCandidates.empty()) return false; // Protect 4 nodes that most recently sent us transactions. // An attacker cannot manipulate this metric without performing useful work. std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeTXTime); - vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast(vEvictionCandidates.size())), vEvictionCandidates.end()); + vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast(vEvictionCandidates.size())), + vEvictionCandidates.end()); if (vEvictionCandidates.empty()) return false; // Protect 4 nodes that most recently sent us blocks. // An attacker cannot manipulate this metric without performing useful work. std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeBlockTime); - vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast(vEvictionCandidates.size())), vEvictionCandidates.end()); + vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast(vEvictionCandidates.size())), + vEvictionCandidates.end()); if (vEvictionCandidates.empty()) return false; // Protect the half of the remaining nodes which have been connected the longest. // This replicates the non-eviction implicit behavior, and precludes attacks that start later. std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeTimeConnected); - vEvictionCandidates.erase(vEvictionCandidates.end() - static_cast(vEvictionCandidates.size() / 2), vEvictionCandidates.end()); + vEvictionCandidates.erase(vEvictionCandidates.end() - static_cast(vEvictionCandidates.size() / 2), + vEvictionCandidates.end()); if (vEvictionCandidates.empty()) return false; @@ -1050,8 +944,9 @@ static bool AttemptToEvictConnection() { uint64_t naMostConnections; unsigned int nMostConnections = 0; int64_t nMostConnectionsTime = 0; - std::map > mapAddrCounts; - BOOST_FOREACH(const NodeEvictionCandidate &node, vEvictionCandidates) { + std::map > mapAddrCounts; + BOOST_FOREACH( + const NodeEvictionCandidate &node, vEvictionCandidates) { mapAddrCounts[node.nKeyedNetGroup].push_back(node); int64_t grouptime = mapAddrCounts[node.nKeyedNetGroup][0].nTimeConnected; size_t groupsize = mapAddrCounts[node.nKeyedNetGroup].size(); @@ -1069,7 +964,7 @@ static bool AttemptToEvictConnection() { // Disconnect from the network group with the most connections NodeId evicted = vEvictionCandidates.front().id; LOCK(cs_vNodes); - for(std::vector::const_iterator it(vNodes.begin()); it != vNodes.end(); ++it) { + for (std::vector::const_iterator it(vNodes.begin()); it != vNodes.end(); ++it) { if ((*it)->GetId() == evicted) { (*it)->fDisconnect = true; return true; @@ -1078,36 +973,34 @@ static bool AttemptToEvictConnection() { return false; } -static void AcceptConnection(const ListenSocket& hListenSocket) { +static void AcceptConnection(const ListenSocket &hListenSocket) { struct sockaddr_storage sockaddr; socklen_t len = sizeof(sockaddr); - SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len); + SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr *) &sockaddr, &len); CAddress addr; int nInbound = 0; int nMaxInbound = nMaxConnections - (MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS); if (hSocket != INVALID_SOCKET) - if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) + if (!addr.SetSockAddr((const struct sockaddr *) &sockaddr)) LogPrintf("Warning: Unknown socket family\n"); bool whitelisted = hListenSocket.whitelisted || CNode::IsWhitelistedRange(addr); { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode->fInbound) - nInbound++; + BOOST_FOREACH(CNode * pnode, vNodes) + if (pnode->fInbound) + nInbound++; } - if (hSocket == INVALID_SOCKET) - { + if (hSocket == INVALID_SOCKET) { int nErr = WSAGetLastError(); if (nErr != WSAEWOULDBLOCK) LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr)); return; } - if (!IsSelectableSocket(hSocket)) - { + if (!IsSelectableSocket(hSocket)) { LogPrintf("connection from %s dropped: non-selectable socket\n", addr.ToString()); CloseSocket(hSocket); return; @@ -1119,18 +1012,16 @@ static void AcceptConnection(const ListenSocket& hListenSocket) { #ifdef WIN32 setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&set, sizeof(int)); #else - setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&set, sizeof(int)); + setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (void *) &set, sizeof(int)); #endif - if (CNode::IsBanned(addr) && !whitelisted) - { + if (CNode::IsBanned(addr) && !whitelisted) { LogPrintf("connection from %s dropped (banned)\n", addr.ToString()); CloseSocket(hSocket); return; } - if (nInbound >= nMaxInbound) - { + if (nInbound >= nMaxInbound) { if (!AttemptToEvictConnection()) { // No connection to evict, disconnect the new connection LogPrint("net", "failed to find an eviction candidate - connection dropped (full)\n"); @@ -1139,7 +1030,7 @@ static void AcceptConnection(const ListenSocket& hListenSocket) { } } - CNode* pnode = new CNode(hSocket, addr, "", true); + CNode *pnode = new CNode(hSocket, addr, "", true); pnode->AddRef(); pnode->fWhitelisted = whitelisted; @@ -1151,23 +1042,21 @@ static void AcceptConnection(const ListenSocket& hListenSocket) { } } -void ThreadSocketHandler() -{ +void ThreadSocketHandler() { unsigned int nPrevNodeCount = 0; - while (true) - { + while (true) { // // Disconnect nodes // { LOCK(cs_vNodes); // Disconnect unused nodes - std::vector vNodesCopy = vNodes; - BOOST_FOREACH(CNode* pnode, vNodesCopy) + std::vector < CNode * > vNodesCopy = vNodes; + BOOST_FOREACH(CNode * pnode, vNodesCopy) { if (pnode->fDisconnect || - (pnode->GetRefCount() <= 0 && pnode->vRecvMsg.empty() && pnode->nSendSize == 0 && pnode->ssSend.empty())) - { + (pnode->GetRefCount() <= 0 && pnode->vRecvMsg.empty() && pnode->nSendSize == 0 && + pnode->ssSend.empty())) { // remove from vNodes vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end()); @@ -1186,35 +1075,31 @@ void ThreadSocketHandler() } { // Delete disconnected nodes - std::list vNodesDisconnectedCopy = vNodesDisconnected; - BOOST_FOREACH(CNode* pnode, vNodesDisconnectedCopy) + std::list < CNode * > vNodesDisconnectedCopy = vNodesDisconnected; + BOOST_FOREACH(CNode * pnode, vNodesDisconnectedCopy) { // wait until threads are done using it - if (pnode->GetRefCount() <= 0) - { + if (pnode->GetRefCount() <= 0) { bool fDelete = false; { TRY_LOCK(pnode->cs_vSend, lockSend); - if (lockSend) - { + if (lockSend) { TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); - if (lockRecv) - { + if (lockRecv) { TRY_LOCK(pnode->cs_inventory, lockInv); if (lockInv) fDelete = true; } } } - if (fDelete) - { + if (fDelete) { vNodesDisconnected.remove(pnode); delete pnode; } } } } - if(vNodes.size() != nPrevNodeCount) { + if (vNodes.size() != nPrevNodeCount) { nPrevNodeCount = vNodes.size(); uiInterface.NotifyNumConnectionsChanged(nPrevNodeCount); } @@ -1223,7 +1108,7 @@ void ThreadSocketHandler() // Find which sockets have data to receive // struct timeval timeout; - timeout.tv_sec = 0; + timeout.tv_sec = 0; timeout.tv_usec = 50000; // frequency to poll pnode->vSend fd_set fdsetRecv; @@ -1235,7 +1120,8 @@ void ThreadSocketHandler() SOCKET hSocketMax = 0; bool have_fds = false; - BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket) { + BOOST_FOREACH( + const ListenSocket &hListenSocket, vhListenSocket) { FD_SET(hListenSocket.socket, &fdsetRecv); hSocketMax = std::max(hSocketMax, hListenSocket.socket); have_fds = true; @@ -1243,7 +1129,7 @@ void ThreadSocketHandler() { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + BOOST_FOREACH(CNode * pnode, vNodes) { if (pnode->hSocket == INVALID_SOCKET) continue; @@ -1276,8 +1162,8 @@ void ThreadSocketHandler() { TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); if (lockRecv && ( - pnode->vRecvMsg.empty() || !pnode->vRecvMsg.front().complete() || - pnode->GetTotalRecvSize() <= ReceiveFloodSize())) + pnode->vRecvMsg.empty() || !pnode->vRecvMsg.front().complete() || + pnode->GetTotalRecvSize() <= ReceiveFloodSize())) FD_SET(pnode->hSocket, &fdsetRecv); } } @@ -1287,10 +1173,8 @@ void ThreadSocketHandler() &fdsetRecv, &fdsetSend, &fdsetError, &timeout); boost::this_thread::interruption_point(); - if (nSelect == SOCKET_ERROR) - { - if (have_fds) - { + if (nSelect == SOCKET_ERROR) { + if (have_fds) { int nErr = WSAGetLastError(); LogPrintf("socket select error %s\n", NetworkErrorString(nErr)); for (unsigned int i = 0; i <= hSocketMax; i++) @@ -1298,16 +1182,16 @@ void ThreadSocketHandler() } FD_ZERO(&fdsetSend); FD_ZERO(&fdsetError); - MilliSleep(timeout.tv_usec/1000); + MilliSleep(timeout.tv_usec / 1000); } // // Accept new connections // - BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket) + BOOST_FOREACH( + const ListenSocket &hListenSocket, vhListenSocket) { - if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv)) - { + if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv)) { AcceptConnection(hListenSocket); } } @@ -1315,14 +1199,14 @@ void ThreadSocketHandler() // // Service each socket // - std::vector vNodesCopy; + std::vector < CNode * > vNodesCopy; { LOCK(cs_vNodes); vNodesCopy = vNodes; - BOOST_FOREACH(CNode* pnode, vNodesCopy) - pnode->AddRef(); + BOOST_FOREACH(CNode * pnode, vNodesCopy) + pnode->AddRef(); } - BOOST_FOREACH(CNode* pnode, vNodesCopy) + BOOST_FOREACH(CNode * pnode, vNodesCopy) { boost::this_thread::interruption_point(); @@ -1331,36 +1215,29 @@ void ThreadSocketHandler() // if (pnode->hSocket == INVALID_SOCKET) continue; - if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError)) - { + if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError)) { TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); - if (lockRecv) - { + if (lockRecv) { { // typical socket buffer is 8K-64K char pchBuf[0x10000]; int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); - if (nBytes > 0) - { + if (nBytes > 0) { if (!pnode->ReceiveMsgBytes(pchBuf, nBytes)) pnode->CloseSocketDisconnect(); pnode->nLastRecv = GetTime(); pnode->nRecvBytes += nBytes; pnode->RecordBytesRecv(nBytes); - } - else if (nBytes == 0) - { + } else if (nBytes == 0) { // socket closed gracefully if (!pnode->fDisconnect) LogPrint("net", "socket closed\n"); pnode->CloseSocketDisconnect(); - } - else if (nBytes < 0) - { + } else if (nBytes < 0) { // error int nErr = WSAGetLastError(); - if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) - { + if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && + nErr != WSAEINPROGRESS) { if (!pnode->fDisconnect) LogPrintf("socket recv error %s\n", NetworkErrorString(nErr)); pnode->CloseSocketDisconnect(); @@ -1375,8 +1252,7 @@ void ThreadSocketHandler() // if (pnode->hSocket == INVALID_SOCKET) continue; - if (FD_ISSET(pnode->hSocket, &fdsetSend)) - { + if (FD_ISSET(pnode->hSocket, &fdsetSend)) { TRY_LOCK(pnode->cs_vSend, lockSend); if (lockSend) SocketSendData(pnode); @@ -1386,25 +1262,20 @@ void ThreadSocketHandler() // Inactivity checking // int64_t nTime = GetTime(); - if (nTime - pnode->nTimeConnected > 60) - { - if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) - { - LogPrint("net", "socket no message in first 60 seconds, %d %d from %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->id); + if (nTime - pnode->nTimeConnected > 60) { + if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) { + LogPrint("net", "socket no message in first 60 seconds, %d %d from %d\n", pnode->nLastRecv != 0, + pnode->nLastSend != 0, pnode->id); pnode->fDisconnect = true; - } - else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL) - { + } else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL) { LogPrintf("socket sending timeout: %is\n", nTime - pnode->nLastSend); pnode->fDisconnect = true; - } - else if (nTime - pnode->nLastRecv > (pnode->nVersion > BIP0031_VERSION ? TIMEOUT_INTERVAL : 90*60)) - { + } else if (nTime - pnode->nLastRecv > + (pnode->nVersion > BIP0031_VERSION ? TIMEOUT_INTERVAL : 90 * 60)) { LogPrintf("socket receive timeout: %is\n", nTime - pnode->nLastRecv); pnode->fDisconnect = true; - } - else if (pnode->nPingNonceSent && pnode->nPingUsecStart + TIMEOUT_INTERVAL * 1000000 < GetTimeMicros()) - { + } else if (pnode->nPingNonceSent && + pnode->nPingUsecStart + TIMEOUT_INTERVAL * 1000000 < GetTimeMicros()) { LogPrintf("ping timeout: %fs\n", 0.000001 * (GetTimeMicros() - pnode->nPingUsecStart)); pnode->fDisconnect = true; } @@ -1412,20 +1283,13 @@ void ThreadSocketHandler() } { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodesCopy) - pnode->Release(); + BOOST_FOREACH(CNode * pnode, vNodesCopy) + pnode->Release(); } } } - - - - - - - #ifdef USE_UPNP void ThreadMapPort() { @@ -1533,19 +1397,15 @@ void MapPort(bool fUseUPnP) } #else -void MapPort(bool) -{ + +void MapPort(bool) { // Intentionally left blank. } -#endif - - - +#endif -static std::string GetDNSHost(const CDNSSeedData& data, ServiceFlags* requiredServiceBits) -{ +static std::string GetDNSHost(const CDNSSeedData &data, ServiceFlags *requiredServiceBits) { //use default host for non-filter-capable seeds or if we use the default service bits (NODE_NETWORK) if (!data.supportsServiceBitsFiltering || *requiredServiceBits == NODE_NETWORK) { *requiredServiceBits = NODE_NETWORK; @@ -1557,8 +1417,7 @@ static std::string GetDNSHost(const CDNSSeedData& data, ServiceFlags* requiredSe } -void ThreadDNSAddressSeed() -{ +void ThreadDNSAddressSeed() { // goal: only query DNS seeds if address need is acute // Avoiding DNS seeds when we don't need them improves user privacy by // creating fewer identifying DNS requests, reduces trust by giving seeds @@ -1578,25 +1437,27 @@ void ThreadDNSAddressSeed() } } - const std::vector &vSeeds = Params().DNSSeeds(); + const std::vector &vSeeds = Params().DNSSeeds(); int found = 0; LogPrintf("Loading addresses from DNS seeds (could take a while)\n"); - BOOST_FOREACH(const CDNSSeedData &seed, vSeeds) { + BOOST_FOREACH( + const CDNSSeedData &seed, vSeeds) { if (HaveNameProxy()) { AddOneShot(seed.host); } else { - std::vector vIPs; - std::vector vAdd; + std::vector vIPs; + std::vector vAdd; ServiceFlags requiredServiceBits = nRelevantServices; - if (LookupHost(GetDNSHost(seed, &requiredServiceBits).c_str(), vIPs, 0, true)) - { - BOOST_FOREACH(const CNetAddr& ip, vIPs) + if (LookupHost(GetDNSHost(seed, &requiredServiceBits).c_str(), vIPs, 0, true)) { + BOOST_FOREACH( + const CNetAddr &ip, vIPs) { - int nOneDay = 24*3600; + int nOneDay = 24 * 3600; CAddress addr = CAddress(CService(ip, Params().GetDefaultPort()), requiredServiceBits); - addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay); // use a random age between 3 and 7 days old + addr.nTime = + GetTime() - 3 * nOneDay - GetRand(4 * nOneDay); // use a random age between 3 and 7 days old vAdd.push_back(addr); found++; } @@ -1617,35 +1478,22 @@ void ThreadDNSAddressSeed() } - - - - - - - - - - -void DumpAddresses() -{ +void DumpAddresses() { int64_t nStart = GetTimeMillis(); CAddrDB adb; adb.Write(addrman); LogPrint("net", "Flushed %d addresses to peers.dat %dms\n", - addrman.size(), GetTimeMillis() - nStart); + addrman.size(), GetTimeMillis() - nStart); } -void DumpData() -{ +void DumpData() { DumpAddresses(); DumpBanlist(); } -void static ProcessOneShot() -{ +void static ProcessOneShot() { std::string strDest; { LOCK(cs_vOneShots); @@ -1662,20 +1510,17 @@ void static ProcessOneShot() } } -void ThreadOpenConnections() -{ +void ThreadOpenConnections() { // Connect to specific addresses - if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) - { - for (int64_t nLoop = 0;; nLoop++) - { + if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) { + for (int64_t nLoop = 0;; nLoop++) { ProcessOneShot(); - BOOST_FOREACH(const std::string& strAddr, mapMultiArgs["-connect"]) + BOOST_FOREACH( + const std::string &strAddr, mapMultiArgs["-connect"]) { CAddress addr(CService(), NODE_NONE); OpenNetworkConnection(addr, false, NULL, strAddr.c_str()); - for (int i = 0; i < 10 && i < nLoop; i++) - { + for (int i = 0; i < 10 && i < nLoop; i++) { MilliSleep(500); } } @@ -1687,9 +1532,8 @@ void ThreadOpenConnections() int64_t nStart = GetTime(); // Minimum time before next feeler connection (in microseconds). - int64_t nNextFeeler = PoissonNextSend(nStart*1000*1000, FEELER_INTERVAL); - while (true) - { + int64_t nNextFeeler = PoissonNextSend(nStart * 1000 * 1000, FEELER_INTERVAL); + while (true) { ProcessOneShot(); MilliSleep(500); @@ -1715,10 +1559,11 @@ void ThreadOpenConnections() // Only connect out to one peer per network group (/16 for IPv4). // Do this here so we don't have to critsect vNodes inside mapAddresses critsect. int nOutbound = 0; - std::set > setConnected; + std::set > setConnected; { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) { + BOOST_FOREACH(CNode * pnode, vNodes) + { if (!pnode->fInbound) { setConnected.insert(pnode->addr.GetGroup()); nOutbound++; @@ -1751,8 +1596,7 @@ void ThreadOpenConnections() int64_t nANow = GetAdjustedTime(); int nTries = 0; - while (true) - { + while (true) { CAddrInfo addr = addrman.Select(fFeeler); // if we selected an invalid address, restart @@ -1778,7 +1622,8 @@ void ThreadOpenConnections() continue; // only consider nodes missing relevant services after 40 failed attempts and only if less than half the outbound are up. - if ((addr.nServices & nRelevantServices) != nRelevantServices && (nTries < 40 || nOutbound >= (MAX_OUTBOUND_CONNECTIONS >> 1))) + if ((addr.nServices & nRelevantServices) != nRelevantServices && + (nTries < 40 || nOutbound >= (MAX_OUTBOUND_CONNECTIONS >> 1))) continue; // do not allow non-default ports, unless after 50 invalid addresses selected already @@ -1798,40 +1643,43 @@ void ThreadOpenConnections() LogPrint("net", "Making feeler connection to %s\n", addrConnect.ToString()); } - OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, NULL, false, fFeeler); + OpenNetworkConnection(addrConnect, (int) setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, + NULL, false, fFeeler); } } } -std::vector GetAddedNodeInfo() -{ - std::vector ret; +std::vector GetAddedNodeInfo() { + std::vector ret; - std::list lAddresses(0); + std::list lAddresses(0); { LOCK(cs_vAddedNodes); ret.reserve(vAddedNodes.size()); - BOOST_FOREACH(const std::string& strAddNode, vAddedNodes) - lAddresses.push_back(strAddNode); + BOOST_FOREACH( + const std::string &strAddNode, vAddedNodes) + lAddresses.push_back(strAddNode); } // Build a map of all already connected addresses (by IP:port and by name) to inbound/outbound and resolved CService std::map mapConnected; - std::map> mapConnectedByName; + std::map > mapConnectedByName; { LOCK(cs_vNodes); - for (const CNode* pnode : vNodes) { + for (const CNode *pnode : vNodes) { if (pnode->addr.IsValid()) { mapConnected[pnode->addr] = pnode->fInbound; } if (!pnode->addrName.empty()) { - mapConnectedByName[pnode->addrName] = std::make_pair(pnode->fInbound, static_cast(pnode->addr)); + mapConnectedByName[pnode->addrName] = std::make_pair(pnode->fInbound, + static_cast(pnode->addr)); } } } - BOOST_FOREACH(const std::string& strAddNode, lAddresses) { + BOOST_FOREACH( + const std::string &strAddNode, lAddresses) { CService service(strAddNode, Params().GetDefaultPort()); if (service.IsValid()) { // strAddNode is an IP:port @@ -1855,17 +1703,15 @@ std::vector GetAddedNodeInfo() return ret; } -void ThreadOpenAddedConnections() -{ +void ThreadOpenAddedConnections() { { LOCK(cs_vAddedNodes); vAddedNodes = mapMultiArgs["-addnode"]; } - for (unsigned int i = 0; true; i++) - { - std::vector vInfo = GetAddedNodeInfo(); - for (const AddedNodeInfo& info : vInfo) { + for (unsigned int i = 0; true; i++) { + std::vector vInfo = GetAddedNodeInfo(); + for (const AddedNodeInfo &info : vInfo) { if (!info.fConnected) { CSemaphoreGrant grant(*semOutbound); // If strAddedNode is an IP/port, decode it immediately, so @@ -1881,21 +1727,21 @@ void ThreadOpenAddedConnections() } // if successful, this moves the passed grant to the constructed node -bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler) -{ +bool OpenNetworkConnection(const CAddress &addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, + const char *pszDest, bool fOneShot, bool fFeeler) { // // Initiate outbound network connection // boost::this_thread::interruption_point(); if (!pszDest) { if (IsLocal(addrConnect) || - FindNode((CNetAddr)addrConnect) || CNode::IsBanned(addrConnect) || + FindNode((CNetAddr) addrConnect) || CNode::IsBanned(addrConnect) || FindNode(addrConnect.ToStringIPPort())) return false; } else if (FindNode(std::string(pszDest))) return false; - CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure); + CNode *pnode = ConnectNode(addrConnect, pszDest, fCountFailure); boost::this_thread::interruption_point(); if (!pnode) @@ -1912,25 +1758,24 @@ bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSem } -void ThreadMessageHandler() -{ +void ThreadMessageHandler() { boost::mutex condition_mutex; boost::unique_lock lock(condition_mutex); - while (true) - { - std::vector vNodesCopy; + while (true) { + std::vector < CNode * > vNodesCopy; { LOCK(cs_vNodes); vNodesCopy = vNodes; - BOOST_FOREACH(CNode* pnode, vNodesCopy) { + BOOST_FOREACH(CNode * pnode, vNodesCopy) + { pnode->AddRef(); } } bool fSleep = true; - BOOST_FOREACH(CNode* pnode, vNodesCopy) + BOOST_FOREACH(CNode * pnode, vNodesCopy) { if (pnode->fDisconnect) continue; @@ -1938,15 +1783,13 @@ void ThreadMessageHandler() // Receive messages { TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); - if (lockRecv) - { + if (lockRecv) { if (!GetNodeSignals().ProcessMessages(pnode)) pnode->CloseSocketDisconnect(); - if (pnode->nSendSize < SendBufferSize()) - { - if (!pnode->vRecvGetData.empty() || (!pnode->vRecvMsg.empty() && pnode->vRecvMsg[0].complete())) - { + if (pnode->nSendSize < SendBufferSize()) { + if (!pnode->vRecvGetData.empty() || + (!pnode->vRecvMsg.empty() && pnode->vRecvMsg[0].complete())) { fSleep = false; } } @@ -1965,44 +1808,38 @@ void ThreadMessageHandler() { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodesCopy) - pnode->Release(); + BOOST_FOREACH(CNode * pnode, vNodesCopy) + pnode->Release(); } if (fSleep) - messageHandlerCondition.timed_wait(lock, boost::posix_time::microsec_clock::universal_time() + boost::posix_time::milliseconds(100)); + messageHandlerCondition.timed_wait(lock, boost::posix_time::microsec_clock::universal_time() + + boost::posix_time::milliseconds(100)); } } - - - - -bool BindListenPort(const CService &addrBind, std::string& strError, bool fWhitelisted) -{ +bool BindListenPort(const CService &addrBind, std::string &strError, bool fWhitelisted) { strError = ""; int nOne = 1; // Create socket for listening for incoming connections struct sockaddr_storage sockaddr; socklen_t len = sizeof(sockaddr); - if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len)) - { + if (!addrBind.GetSockAddr((struct sockaddr *) &sockaddr, &len)) { strError = strprintf("Error: Bind address family for %s not supported", addrBind.ToString()); LogPrintf("%s\n", strError); return false; } - SOCKET hListenSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP); - if (hListenSocket == INVALID_SOCKET) - { - strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %s)", NetworkErrorString(WSAGetLastError())); + SOCKET hListenSocket = socket(((struct sockaddr *) &sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP); + if (hListenSocket == INVALID_SOCKET) { + strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %s)", + NetworkErrorString(WSAGetLastError())); LogPrintf("%s\n", strError); return false; } - if (!IsSelectableSocket(hListenSocket)) - { + if (!IsSelectableSocket(hListenSocket)) { strError = "Error: Couldn't create a listenable socket for incoming connections"; LogPrintf("%s\n", strError); return false; @@ -2016,9 +1853,9 @@ bool BindListenPort(const CService &addrBind, std::string& strError, bool fWhite #endif // Allow binding if the port is still in TIME_WAIT state after // the program was closed and restarted. - setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int)); + setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void *) &nOne, sizeof(int)); // Disable Nagle's algorithm - setsockopt(hListenSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&nOne, sizeof(int)); + setsockopt(hListenSocket, IPPROTO_TCP, TCP_NODELAY, (void *) &nOne, sizeof(int)); #else setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&nOne, sizeof(int)); setsockopt(hListenSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&nOne, sizeof(int)); @@ -2026,7 +1863,8 @@ bool BindListenPort(const CService &addrBind, std::string& strError, bool fWhite // Set to non-blocking, incoming connections will also inherit this if (!SetSocketNonBlocking(hListenSocket, true)) { - strError = strprintf("BindListenPort: Setting listening socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError())); + strError = strprintf("BindListenPort: Setting listening socket to non-blocking failed, error %s\n", + NetworkErrorString(WSAGetLastError())); LogPrintf("%s\n", strError); return false; } @@ -2047,13 +1885,14 @@ bool BindListenPort(const CService &addrBind, std::string& strError, bool fWhite #endif } - if (::bind(hListenSocket, (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR) - { + if (::bind(hListenSocket, (struct sockaddr *) &sockaddr, len) == SOCKET_ERROR) { int nErr = WSAGetLastError(); if (nErr == WSAEADDRINUSE) - strError = strprintf(_("Unable to bind to %s on this computer. %s is probably already running."), addrBind.ToString(), _(PACKAGE_NAME)); + strError = strprintf(_("Unable to bind to %s on this computer. %s is probably already running."), + addrBind.ToString(), _(PACKAGE_NAME)); else - strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToString(), NetworkErrorString(nErr)); + strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), + addrBind.ToString(), NetworkErrorString(nErr)); LogPrintf("%s\n", strError); CloseSocket(hListenSocket); return false; @@ -2061,9 +1900,9 @@ bool BindListenPort(const CService &addrBind, std::string& strError, bool fWhite LogPrintf("Bound to %s\n", addrBind.ToString()); // Listen for incoming connections - if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) - { - strError = strprintf(_("Error: Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError())); + if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) { + strError = strprintf(_("Error: Listening for incoming connections failed (listen returned error %s)"), + NetworkErrorString(WSAGetLastError())); LogPrintf("%s\n", strError); CloseSocket(hListenSocket); return false; @@ -2077,8 +1916,7 @@ bool BindListenPort(const CService &addrBind, std::string& strError, bool fWhite return true; } -void static Discover(boost::thread_group& threadGroup) -{ +void static Discover(boost::thread_group &threadGroup) { if (!fDiscover) return; @@ -2099,25 +1937,20 @@ void static Discover(boost::thread_group& threadGroup) } #else // Get local host ip - struct ifaddrs* myaddrs; - if (getifaddrs(&myaddrs) == 0) - { - for (struct ifaddrs* ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) - { + struct ifaddrs *myaddrs; + if (getifaddrs(&myaddrs) == 0) { + for (struct ifaddrs *ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr == NULL) continue; if ((ifa->ifa_flags & IFF_UP) == 0) continue; if (strcmp(ifa->ifa_name, "lo") == 0) continue; if (strcmp(ifa->ifa_name, "lo0") == 0) continue; - if (ifa->ifa_addr->sa_family == AF_INET) - { - struct sockaddr_in* s4 = (struct sockaddr_in*)(ifa->ifa_addr); + if (ifa->ifa_addr->sa_family == AF_INET) { + struct sockaddr_in *s4 = (struct sockaddr_in *) (ifa->ifa_addr); CNetAddr addr(s4->sin_addr); if (AddLocal(addr, LOCAL_IF)) LogPrintf("%s: IPv4 %s: %s\n", __func__, ifa->ifa_name, addr.ToString()); - } - else if (ifa->ifa_addr->sa_family == AF_INET6) - { - struct sockaddr_in6* s6 = (struct sockaddr_in6*)(ifa->ifa_addr); + } else if (ifa->ifa_addr->sa_family == AF_INET6) { + struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) (ifa->ifa_addr); CNetAddr addr(s6->sin6_addr); if (AddLocal(addr, LOCAL_IF)) LogPrintf("%s: IPv6 %s: %s\n", __func__, ifa->ifa_name, addr.ToString()); @@ -2128,8 +1961,7 @@ void static Discover(boost::thread_group& threadGroup) #endif } -void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) -{ +void StartNode(boost::thread_group &threadGroup, CScheduler &scheduler) { uiInterface.InitMessage(_("Loading addresses...")); // Load addresses from peers.dat int64_t nStart = GetTimeMillis(); @@ -2155,7 +1987,7 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) CNode::SweepBanned(); // sweep out unused entries LogPrint("net", "Loaded %d banned node ips/subnets from banlist.dat %dms\n", - banmap.size(), GetTimeMillis() - nStart); + banmap.size(), GetTimeMillis() - nStart); } else { LogPrintf("Invalid or missing banlist.dat; recreating\n"); CNode::SetBannedSetDirty(true); // force write @@ -2205,16 +2037,14 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) scheduler.scheduleEvery(&DumpData, DUMP_ADDRESSES_INTERVAL); } -bool StopNode() -{ +bool StopNode() { LogPrintf("StopNode()\n"); MapPort(false); if (semOutbound) - for (int i=0; i<(MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS); i++) + for (int i = 0; i < (MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS); i++) semOutbound->post(); - if (fAddressesInitialized) - { + if (fAddressesInitialized) { DumpData(); fAddressesInitialized = false; } @@ -2222,27 +2052,25 @@ bool StopNode() return true; } -class CNetCleanup -{ +class CNetCleanup { public: CNetCleanup() {} - ~CNetCleanup() - { + ~CNetCleanup() { // Close sockets - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode->hSocket != INVALID_SOCKET) - CloseSocket(pnode->hSocket); - BOOST_FOREACH(ListenSocket& hListenSocket, vhListenSocket) - if (hListenSocket.socket != INVALID_SOCKET) - if (!CloseSocket(hListenSocket.socket)) - LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError())); + BOOST_FOREACH(CNode * pnode, vNodes) + if (pnode->hSocket != INVALID_SOCKET) + CloseSocket(pnode->hSocket); + BOOST_FOREACH(ListenSocket & hListenSocket, vhListenSocket) + if (hListenSocket.socket != INVALID_SOCKET) + if (!CloseSocket(hListenSocket.socket)) + LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError())); // clean up some globals (to help leak detection) - BOOST_FOREACH(CNode *pnode, vNodes) - delete pnode; - BOOST_FOREACH(CNode *pnode, vNodesDisconnected) - delete pnode; + BOOST_FOREACH(CNode * pnode, vNodes) + delete pnode; + BOOST_FOREACH(CNode * pnode, vNodesDisconnected) + delete pnode; vNodes.clear(); vNodesDisconnected.clear(); vhListenSocket.clear(); @@ -2257,14 +2085,13 @@ class CNetCleanup #endif } } -instance_of_cnetcleanup; + instance_of_cnetcleanup; -void RelayTransaction(const CTransaction& tx) -{ +void RelayTransaction(const CTransaction &tx) { CInv inv(MSG_TX, tx.GetHash()); LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + BOOST_FOREACH(CNode * pnode, vNodes) { pnode->PushInventory(inv); } @@ -2272,27 +2099,24 @@ void RelayTransaction(const CTransaction& tx) void RelayInv(CInv &inv, const int minProtoVersion) { LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + BOOST_FOREACH(CNode * pnode, vNodes) { if (pnode->nVersion >= minProtoVersion) pnode->PushInventory(inv); } } -void CNode::RecordBytesRecv(uint64_t bytes) -{ +void CNode::RecordBytesRecv(uint64_t bytes) { LOCK(cs_totalBytesRecv); nTotalBytesRecv += bytes; } -void CNode::RecordBytesSent(uint64_t bytes) -{ +void CNode::RecordBytesSent(uint64_t bytes) { LOCK(cs_totalBytesSent); nTotalBytesSent += bytes; uint64_t now = GetTime(); - if (nMaxOutboundCycleStartTime + nMaxOutboundTimeframe < now) - { + if (nMaxOutboundCycleStartTime + nMaxOutboundTimeframe < now) { // timeframe expired, reset cycle nMaxOutboundCycleStartTime = now; nMaxOutboundTotalBytesSentInCycle = 0; @@ -2302,26 +2126,22 @@ void CNode::RecordBytesSent(uint64_t bytes) nMaxOutboundTotalBytesSentInCycle += bytes; } -void CNode::SetMaxOutboundTarget(uint64_t limit) -{ +void CNode::SetMaxOutboundTarget(uint64_t limit) { LOCK(cs_totalBytesSent); nMaxOutboundLimit = limit; } -uint64_t CNode::GetMaxOutboundTarget() -{ +uint64_t CNode::GetMaxOutboundTarget() { LOCK(cs_totalBytesSent); return nMaxOutboundLimit; } -uint64_t CNode::GetMaxOutboundTimeframe() -{ +uint64_t CNode::GetMaxOutboundTimeframe() { LOCK(cs_totalBytesSent); return nMaxOutboundTimeframe; } -uint64_t CNode::GetMaxOutboundTimeLeftInCycle() -{ +uint64_t CNode::GetMaxOutboundTimeLeftInCycle() { LOCK(cs_totalBytesSent); if (nMaxOutboundLimit == 0) return 0; @@ -2334,11 +2154,9 @@ uint64_t CNode::GetMaxOutboundTimeLeftInCycle() return (cycleEndTime < now) ? 0 : cycleEndTime - GetTime(); } -void CNode::SetMaxOutboundTimeframe(uint64_t timeframe) -{ +void CNode::SetMaxOutboundTimeframe(uint64_t timeframe) { LOCK(cs_totalBytesSent); - if (nMaxOutboundTimeframe != timeframe) - { + if (nMaxOutboundTimeframe != timeframe) { // reset measure-cycle in case of changing // the timeframe nMaxOutboundCycleStartTime = GetTime(); @@ -2346,76 +2164,69 @@ void CNode::SetMaxOutboundTimeframe(uint64_t timeframe) nMaxOutboundTimeframe = timeframe; } -bool CNode::OutboundTargetReached(bool historicalBlockServingLimit) -{ +bool CNode::OutboundTargetReached(bool historicalBlockServingLimit) { LOCK(cs_totalBytesSent); if (nMaxOutboundLimit == 0) return false; - if (historicalBlockServingLimit) - { + if (historicalBlockServingLimit) { // keep a large enough buffer to at least relay each block once uint64_t timeLeftInCycle = GetMaxOutboundTimeLeftInCycle(); uint64_t buffer = timeLeftInCycle / 600 * MAX_BLOCK_SERIALIZED_SIZE; if (buffer >= nMaxOutboundLimit || nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit - buffer) return true; - } - else if (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit) + } else if (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit) return true; return false; } -uint64_t CNode::GetOutboundTargetBytesLeft() -{ +uint64_t CNode::GetOutboundTargetBytesLeft() { LOCK(cs_totalBytesSent); if (nMaxOutboundLimit == 0) return 0; - return (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit) ? 0 : nMaxOutboundLimit - nMaxOutboundTotalBytesSentInCycle; + return (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit) ? 0 : nMaxOutboundLimit - + nMaxOutboundTotalBytesSentInCycle; } -uint64_t CNode::GetTotalBytesRecv() -{ +uint64_t CNode::GetTotalBytesRecv() { LOCK(cs_totalBytesRecv); return nTotalBytesRecv; } -uint64_t CNode::GetTotalBytesSent() -{ +uint64_t CNode::GetTotalBytesSent() { LOCK(cs_totalBytesSent); return nTotalBytesSent; } -void CNode::Fuzz(int nChance) -{ +void CNode::Fuzz(int nChance) { if (!fSuccessfullyConnected) return; // Don't fuzz initial handshake if (GetRand(nChance) != 0) return; // Fuzz 1 of every nChance messages - switch (GetRand(3)) - { - case 0: - // xor a random byte with a random value: - if (!ssSend.empty()) { - CDataStream::size_type pos = GetRand(ssSend.size()); - ssSend[pos] ^= (unsigned char)(GetRand(256)); - } - break; - case 1: - // delete a random byte: - if (!ssSend.empty()) { - CDataStream::size_type pos = GetRand(ssSend.size()); - ssSend.erase(ssSend.begin()+pos); - } - break; - case 2: - // insert a random byte at a random position + switch (GetRand(3)) { + case 0: + // xor a random byte with a random value: + if (!ssSend.empty()) { + CDataStream::size_type pos = GetRand(ssSend.size()); + ssSend[pos] ^= (unsigned char) (GetRand(256)); + } + break; + case 1: + // delete a random byte: + if (!ssSend.empty()) { + CDataStream::size_type pos = GetRand(ssSend.size()); + ssSend.erase(ssSend.begin() + pos); + } + break; + case 2: + // insert a random byte at a random position { CDataStream::size_type pos = GetRand(ssSend.size()); - char ch = (char)GetRand(256); - ssSend.insert(ssSend.begin()+pos, ch); + char ch = (char) GetRand(256); + ssSend.insert(ssSend.begin() + pos, ch); } - break; + break; } // Chance of more than one change half the time: // (more changes exponentially less likely): @@ -2426,16 +2237,14 @@ void CNode::Fuzz(int nChance) // CAddrDB // -CAddrDB::CAddrDB() -{ +CAddrDB::CAddrDB() { pathAddr = GetDataDir() / "peers.dat"; } -bool CAddrDB::Write(const CAddrMan& addr) -{ +bool CAddrDB::Write(const CAddrMan &addr) { // Generate random temporary filename unsigned short randv = 0; - GetRandBytes((unsigned char*)&randv, sizeof(randv)); + GetRandBytes((unsigned char *) &randv, sizeof(randv)); std::string tmpfn = strprintf("peers.dat.%04x", randv); // serialize addresses, checksum data up to that point, then append csum @@ -2456,7 +2265,7 @@ bool CAddrDB::Write(const CAddrMan& addr) try { fileout << ssPeers; } - catch (const std::exception& e) { + catch (const std::exception &e) { return error("%s: Serialize or I/O error - %s", __func__, e.what()); } FileCommit(fileout.Get()); @@ -2469,8 +2278,7 @@ bool CAddrDB::Write(const CAddrMan& addr) return true; } -bool CAddrDB::Read(CAddrMan& addr) -{ +bool CAddrDB::Read(CAddrMan &addr) { // open input file, and associate with CAutoFile FILE *file = fopen(pathAddr.string().c_str(), "rb"); CAutoFile filein(file, SER_DISK, CLIENT_VERSION); @@ -2489,10 +2297,10 @@ bool CAddrDB::Read(CAddrMan& addr) // read data and checksum from file try { - filein.read((char *)&vchData[0], dataSize); + filein.read((char *) &vchData[0], dataSize); filein >> hashIn; } - catch (const std::exception& e) { + catch (const std::exception &e) { return error("%s: Deserialize or I/O error - %s", __func__, e.what()); } filein.fclose(); @@ -2507,8 +2315,7 @@ bool CAddrDB::Read(CAddrMan& addr) return Read(addr, ssPeers); } -bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers) -{ +bool CAddrDB::Read(CAddrMan &addr, CDataStream &ssPeers) { unsigned char pchMsgTmp[4]; try { // de-serialize file header (network specific magic number) and .. @@ -2521,7 +2328,7 @@ bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers) // de-serialize address data into one CAddrMan object ssPeers >> addr; } - catch (const std::exception& e) { + catch (const std::exception &e) { // de-serialization has failed, ensure addrman is left in a clean state addr.Clear(); return error("%s: Deserialize or I/O error - %s", __func__, e.what()); @@ -2530,16 +2337,16 @@ bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers) return true; } -unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER); } -unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER); } +unsigned int ReceiveFloodSize() { return 1000 * GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER); } -CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNameIn, bool fInboundIn) : - ssSend(SER_NETWORK, INIT_PROTO_VERSION), - addr(addrIn), - nKeyedNetGroup(CalculateKeyedNetGroup(addrIn)), - addrKnown(5000, 0.001), - filterInventoryKnown(50000, 0.000001) -{ +unsigned int SendBufferSize() { return 1000 * GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER); } + +CNode::CNode(SOCKET hSocketIn, const CAddress &addrIn, const std::string &addrNameIn, bool fInboundIn) : + ssSend(SER_NETWORK, INIT_PROTO_VERSION), + addr(addrIn), + nKeyedNetGroup(CalculateKeyedNetGroup(addrIn)), + addrKnown(5000, 0.001), + filterInventoryKnown(50000, 0.000001) { nServices = NODE_NONE; nServicesExpected = NODE_NONE; hSocket = hSocketIn; @@ -2589,8 +2396,9 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa // znode fZnode = false; - BOOST_FOREACH(const std::string &msg, getAllNetMessageTypes()) - mapRecvBytesPerMsgCmd[msg] = 0; + BOOST_FOREACH( + const std::string &msg, getAllNetMessageTypes()) + mapRecvBytesPerMsgCmd[msg] = 0; mapRecvBytesPerMsgCmd[NET_MESSAGE_COMMAND_OTHER] = 0; { @@ -2610,8 +2418,7 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa GetNodeSignals().InitializeNode(GetId(), this); } -CNode::~CNode() -{ +CNode::~CNode() { CloseSocket(hSocket); if (pfilter) @@ -2620,8 +2427,7 @@ CNode::~CNode() GetNodeSignals().FinalizeNode(GetId()); } -void CNode::AskFor(const CInv& inv) -{ +void CNode::AskFor(const CInv &inv) { if (mapAskFor.size() > MAPASKFOR_MAX_SZ || setAskFor.size() > SETASKFOR_MAX_SZ) return; // a peer may not have multiple non-responded queue positions for a single inv item @@ -2636,7 +2442,8 @@ void CNode::AskFor(const CInv& inv) nRequestTime = it->second; else nRequestTime = 0; - LogPrint("net", "askfor %s %d (%s) peer=%d\n", inv.ToString(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000), id); + LogPrint("net", "askfor %s %d (%s) peer=%d\n", inv.ToString(), nRequestTime, + DateTimeStrFormat("%H:%M:%S", nRequestTime / 1000000), id); // Make sure not to reuse time indexes to keep things in the same order int64_t nNow = GetTimeMicros() - 1000000; @@ -2654,16 +2461,14 @@ void CNode::AskFor(const CInv& inv) mapAskFor.insert(std::make_pair(nRequestTime, inv)); } -void CNode::BeginMessage(const char* pszCommand) EXCLUSIVE_LOCK_FUNCTION(cs_vSend) -{ +void CNode::BeginMessage(const char *pszCommand) EXCLUSIVE_LOCK_FUNCTION(cs_vSend) { ENTER_CRITICAL_SECTION(cs_vSend); assert(ssSend.size() == 0); ssSend << CMessageHeader(Params().MessageStart(), pszCommand, 0); LogPrint("net", "sending: %s ", SanitizeString(pszCommand)); } -void CNode::AbortMessage() UNLOCK_FUNCTION(cs_vSend) -{ +void CNode::AbortMessage() UNLOCK_FUNCTION(cs_vSend) { ssSend.clear(); LEAVE_CRITICAL_SECTION(cs_vSend); @@ -2671,13 +2476,11 @@ void CNode::AbortMessage() UNLOCK_FUNCTION(cs_vSend) LogPrint("net", "(aborted)\n"); } -void CNode::EndMessage(const char* pszCommand) UNLOCK_FUNCTION(cs_vSend) -{ +void CNode::EndMessage(const char *pszCommand) UNLOCK_FUNCTION(cs_vSend) { // The -*messagestest options are intentionally not documented in the help message, // since they are only used during development to debug the networking code and are // not intended for end-users. - if (mapArgs.count("-dropmessagestest") && GetRand(GetArg("-dropmessagestest", 2)) == 0) - { + if (mapArgs.count("-dropmessagestest") && GetRand(GetArg("-dropmessagestest", 2)) == 0) { LogPrint("net", "dropmessages DROPPING SEND MESSAGE\n"); AbortMessage(); return; @@ -2685,14 +2488,13 @@ void CNode::EndMessage(const char* pszCommand) UNLOCK_FUNCTION(cs_vSend) if (mapArgs.count("-fuzzmessagestest")) Fuzz(GetArg("-fuzzmessagestest", 10)); - if (ssSend.size() == 0) - { + if (ssSend.size() == 0) { LEAVE_CRITICAL_SECTION(cs_vSend); return; } // Set the size unsigned int nSize = ssSend.size() - CMessageHeader::HEADER_SIZE; - WriteLE32((uint8_t*)&ssSend[CMessageHeader::MESSAGE_SIZE_OFFSET], nSize); + WriteLE32((uint8_t * ) & ssSend[CMessageHeader::MESSAGE_SIZE_OFFSET], nSize); //log total amount of bytes per command mapSendBytesPerMsgCmd[std::string(pszCommand)] += nSize + CMessageHeader::HEADER_SIZE; @@ -2701,8 +2503,8 @@ void CNode::EndMessage(const char* pszCommand) UNLOCK_FUNCTION(cs_vSend) uint256 hash = Hash(ssSend.begin() + CMessageHeader::HEADER_SIZE, ssSend.end()); unsigned int nChecksum = 0; memcpy(&nChecksum, &hash, sizeof(nChecksum)); - assert(ssSend.size () >= CMessageHeader::CHECKSUM_OFFSET + sizeof(nChecksum)); - memcpy((char*)&ssSend[CMessageHeader::CHECKSUM_OFFSET], &nChecksum, sizeof(nChecksum)); + assert(ssSend.size() >= CMessageHeader::CHECKSUM_OFFSET + sizeof(nChecksum)); + memcpy((char *) &ssSend[CMessageHeader::CHECKSUM_OFFSET], &nChecksum, sizeof(nChecksum)); LogPrint("net", "(%d bytes) peer=%d\n", nSize, id); @@ -2721,16 +2523,14 @@ void CNode::EndMessage(const char* pszCommand) UNLOCK_FUNCTION(cs_vSend) // CBanDB // -CBanDB::CBanDB() -{ +CBanDB::CBanDB() { pathBanlist = GetDataDir() / "banlist.dat"; } -bool CBanDB::Write(const banmap_t& banSet) -{ +bool CBanDB::Write(const banmap_t &banSet) { // Generate random temporary filename unsigned short randv = 0; - GetRandBytes((unsigned char*)&randv, sizeof(randv)); + GetRandBytes((unsigned char *) &randv, sizeof(randv)); std::string tmpfn = strprintf("banlist.dat.%04x", randv); // serialize banlist, checksum data up to that point, then append csum @@ -2751,7 +2551,7 @@ bool CBanDB::Write(const banmap_t& banSet) try { fileout << ssBanlist; } - catch (const std::exception& e) { + catch (const std::exception &e) { return error("%s: Serialize or I/O error - %s", __func__, e.what()); } FileCommit(fileout.Get()); @@ -2764,8 +2564,7 @@ bool CBanDB::Write(const banmap_t& banSet) return true; } -bool CBanDB::Read(banmap_t& banSet) -{ +bool CBanDB::Read(banmap_t &banSet) { // open input file, and associate with CAutoFile FILE *file = fopen(pathBanlist.string().c_str(), "rb"); CAutoFile filein(file, SER_DISK, CLIENT_VERSION); @@ -2784,10 +2583,10 @@ bool CBanDB::Read(banmap_t& banSet) // read data and checksum from file try { - filein.read((char *)&vchData[0], dataSize); + filein.read((char *) &vchData[0], dataSize); filein >> hashIn; } - catch (const std::exception& e) { + catch (const std::exception &e) { return error("%s: Deserialize or I/O error - %s", __func__, e.what()); } filein.fclose(); @@ -2811,7 +2610,7 @@ bool CBanDB::Read(banmap_t& banSet) // de-serialize address data into one CAddrMan object ssBanlist >> banSet; } - catch (const std::exception& e) { + catch (const std::exception &e) { return error("%s: Deserialize or I/O error - %s", __func__, e.what()); } @@ -2819,11 +2618,12 @@ bool CBanDB::Read(banmap_t& banSet) } int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds) { - return nNow + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5); + return nNow + (int64_t)( + log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * + -1000000.0 + 0.5); } -/* static */ uint64_t CNode::CalculateKeyedNetGroup(const CAddress& ad) -{ +/* static */ uint64_t CNode::CalculateKeyedNetGroup(const CAddress &ad) { static const uint64_t k0 = GetRand(std::numeric_limits::max()); static const uint64_t k1 = GetRand(std::numeric_limits::max()); @@ -2832,22 +2632,20 @@ int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds) { return CSipHasher(k0, k1).Write(&vchNetGroup[0], vchNetGroup.size()).Finalize(); } -std::vector CopyNodeVector() -{ - std::vector vecNodesCopy; +std::vector CopyNodeVector() { + std::vector < CNode * > vecNodesCopy; LOCK(cs_vNodes); - for(size_t i = 0; i < vNodes.size(); ++i) { - CNode* pnode = vNodes[i]; + for (size_t i = 0; i < vNodes.size(); ++i) { + CNode *pnode = vNodes[i]; pnode->AddRef(); vecNodesCopy.push_back(pnode); } return vecNodesCopy; } -void ReleaseNodeVector(const std::vector& vecNodes) -{ - for(size_t i = 0; i < vecNodes.size(); ++i) { - CNode* pnode = vecNodes[i]; +void ReleaseNodeVector(const std::vector &vecNodes) { + for (size_t i = 0; i < vecNodes.size(); ++i) { + CNode *pnode = vecNodes[i]; pnode->Release(); } } \ No newline at end of file diff --git a/src/net.h b/src/net.h index 9a190f0657..eeaf5b6571 100644 --- a/src/net.h +++ b/src/net.h @@ -92,7 +92,7 @@ CNode* FindNode(const std::string& addrName); CNode* FindNode(const CService& ip); CNode* FindNode(const NodeId id); //TODO: Remove this -CNode* ConnectNodeDash(CAddress addrConnect, const char *pszDest = NULL, bool fConnectToZnode = false); +CNode* ConnectNode(CAddress addrConnect, const char *pszDest = NULL, bool fCountFailure = false, bool fConnectToZnode = false); bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false, bool fFeeler = false); void MapPort(bool fUseUPnP); diff --git a/src/pow.cpp b/src/pow.cpp index a53599458f..397a2dfa51 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -4,17 +4,17 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "pow.h" - +#include "main.h" #include "arith_uint256.h" #include "chain.h" #include "primitives/block.h" +#include "consensus/consensus.h" #include "uint256.h" #include #include "util.h" #include "chainparams.h" #include "libzerocoin/bitcoin_bignum/bignum.h" #include "fixed.h" -#include "consensus/consensus.h" static CBigNum bnProofOfWorkLimit(~arith_uint256(0) >> 8); @@ -34,9 +34,14 @@ double GetDifficultyHelper(unsigned int nBits) { return dDiff; } -//btzc, zcoin GetNextWorkRequired +// zcoin GetNextWorkRequired unsigned int GetNextWorkRequired(const CBlockIndex *pindexLast, const CBlockHeader *pblock, const Consensus::Params ¶ms) { - if (pindexLast == NULL) { + + if(ENABLED_LOWEST_DIFF){ + return bnProofOfWorkLimit.GetCompact(); + } + + if (pindexLast == NULL) { return bnProofOfWorkLimit.GetCompact(); } @@ -50,28 +55,21 @@ unsigned int GetNextWorkRequired(const CBlockIndex *pindexLast, const CBlockHead if (fTestNet) { // If the new block's timestamp is more than nTargetSpacing*6 // then allow mining of a min-difficulty block - if (pblock->nTime > pindexLast->nTime + params.nPowTargetTimespan * 6) { + if (pblock->nTime > pindexLast->nTime + params.nPowTargetTimespan * 1) { return bnProofOfWorkLimit.GetCompact(); } } // 9/29/2016 - Reset to Lyra2(2,block_height,256) due to ASIC KnC Miner Scrypt // 36 block look back, reset to mininmum diff - if (!fTestNet && pindexLast->nHeight + 1 >= HF_LYRA2VAR_HEIGHT && pindexLast->nHeight + 1 <= (HF_LYRA2VAR_HEIGHT + 35)) { + if (!fTestNet && pindexLast->nHeight + 1 >= HF_LYRA2VAR_HEIGHT && pindexLast->nHeight + 1 <= HF_LYRA2VAR_HEIGHT + 36 - 1) { return bnProofOfWorkLimit.GetCompact(); } // reset to minimum diff at testnet after scrypt_n, 6 block look back - if (fTestNet && pindexLast->nHeight + 1 >= HF_LYRA2VAR_HEIGHT_TESTNET && pindexLast->nHeight + 1 <= (HF_LYRA2VAR_HEIGHT_TESTNET + 5)) { + if (fTestNet && pindexLast->nHeight + 1 >= HF_LYRA2VAR_HEIGHT_TESTNET && pindexLast->nHeight + 1 <= HF_LYRA2VAR_HEIGHT_TESTNET + 6 - 1) { return bnProofOfWorkLimit.GetCompact(); } - // reset to minimum diff at testnet for lyra2 with matrix height = 8192 until lyra2z - if(fTestNet && pindexLast->nHeight + 1 >= HF_LYRA2_HEIGHT_TESTNET && pindexLast->nHeight + 1 <= (HF_LYRA2Z_HEIGHT_TESTNET - 1)){ - printf("Lyra2 with 8192 matrix height.\n"); - return bnProofOfWorkLimit.GetCompact(); - } - - // 02/11/2017 - Increase diff to match with new hashrates of Lyra2Z algo if ((!fTestNet && pindexLast->nHeight + 1 == HF_LYRA2Z_HEIGHT) || (fTestNet && pindexLast->nHeight + 1 == HF_LYRA2Z_HEIGHT_TESTNET)) { CBigNum bnNew; @@ -83,12 +81,6 @@ unsigned int GetNextWorkRequired(const CBlockIndex *pindexLast, const CBlockHead return bnNew.GetCompact(); } - // 04/09/2017 - Reset diff on testnet for MTP, 6 blocks look back - /* - if(fTestNet && pindexLast->nHeight + 1 >= HF_MTP_HEIGHT_TESTNET && pindexLast->nHeight + 1 <= HF_MTP_HEIGHT_TESTNET + 5) { - return bnProofOfWorkLimit.GetCompact(); - }*/ - if ((pindexLast->nHeight + 1) % params.DifficultyAdjustmentInterval() != 0) // Retarget every nInterval blocks { return pindexLast->nBits; diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index 28bfda5254..2579d76a7e 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -5,7 +5,7 @@ #include "primitives/block.h" #include "consensus/consensus.h" - +#include "main.h" #include "hash.h" #include "tinyformat.h" #include "utilstrencodings.h" diff --git a/src/primitives/block.h b/src/primitives/block.h index d5aba0e761..05a47c55f2 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -113,6 +113,8 @@ class CBlock : public CBlockHeader std::vector vtx; // memory only + mutable CTxOut txoutZnode; // masternode payment + mutable std::vector voutSuperblock; // superblock payment mutable bool fChecked; CBlock() @@ -138,6 +140,8 @@ class CBlock : public CBlockHeader { CBlockHeader::SetNull(); vtx.clear(); + txoutZnode = CTxOut(); + voutSuperblock.clear(); fChecked = false; } diff --git a/src/primitives/precomputed_hash.h b/src/primitives/precomputed_hash.h index c6c6a149a6..b0a29ebdcc 100644 --- a/src/primitives/precomputed_hash.h +++ b/src/primitives/precomputed_hash.h @@ -5139,4 +5139,4 @@ void buildMapPoWHash() { for (int i=1; i<20500; i++) { mapPoWHash.insert(make_pair(i, uint256S(precomputedHash[i]))); } -}; \ No newline at end of file +}; diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 6742f20a38..c2d7519ade 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -4,7 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "primitives/transaction.h" - +#include "script/interpreter.h" #include "hash.h" #include "tinyformat.h" #include "utilstrencodings.h" @@ -152,6 +152,11 @@ uint256 CTransaction::GetWitnessHash() const return SerializeHash(*this, SER_GETHASH, 0); } +uint256 CTransaction::GetNormalizedHash() const +{ + return SignatureHash(CScript(), *this, 0, SIGHASH_ALL, 0, SIGVERSION_BASE); +} + CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0) { } CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), wit(tx.wit), nLockTime(tx.nLockTime) { diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 089f3b1426..ffd953462e 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -139,6 +139,7 @@ class CTxOut public: CAmount nValue; CScript scriptPubKey; + int nRounds; CTxOut() { @@ -159,6 +160,7 @@ class CTxOut { nValue = -1; scriptPubKey.clear(); + nRounds = -10; // an initial value, should be no way to get this by calculations } bool IsNull() const @@ -216,7 +218,8 @@ class CTxOut friend bool operator==(const CTxOut& a, const CTxOut& b) { return (a.nValue == b.nValue && - a.scriptPubKey == b.scriptPubKey); + a.scriptPubKey == b.scriptPubKey && + a.nRounds == b.nRounds); } friend bool operator!=(const CTxOut& a, const CTxOut& b) @@ -433,6 +436,9 @@ class CTransaction // Compute a hash that includes both transaction and witness data uint256 GetWitnessHash() const; + // Compute a hash without changing later in 0.8 + uint256 GetNormalizedHash() const; + // Return sum of txouts. CAmount GetValueOut() const; // GetValueIn() is a method on CCoinsViewCache, because diff --git a/src/protocol.cpp b/src/protocol.cpp index 66851058a9..0c9af62b16 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -67,6 +67,7 @@ const char *MNGOVERNANCESYNC="govsync"; const char *MNGOVERNANCEOBJECT="govobj"; const char *MNGOVERNANCEOBJECTVOTE="govobjvote"; const char *MNVERIFY="mnv"; +const char *TXLOCKREQUEST="ix"; }; @@ -101,6 +102,7 @@ const static std::string allNetMessageTypes[] = { NetMsgType::GETBLOCKTXN, NetMsgType::BLOCKTXN, //znode + NetMsgType::TXLOCKREQUEST, NetMsgType::ZNODEPAYMENTVOTE, NetMsgType::SPORK, NetMsgType::GETSPORKS, diff --git a/src/protocol.h b/src/protocol.h index 05cb7ea1e1..9fcbf54165 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -261,6 +261,8 @@ extern const char *DSSIGNFINALTX; extern const char *DSCOMPLETE; extern const char *DSFINALTX; extern const char *TXLOCKVOTE; +extern const char *DSTX; +extern const char *TXLOCKREQUEST; }; @@ -354,6 +356,7 @@ enum GetDataMsg MSG_ZNODE_ANNOUNCE, MSG_ZNODE_PING, MSG_ZNODE_VERIFY, + MSG_TXLOCK_REQUEST, MSG_DSTX, DSQUEUE, }; diff --git a/src/pubkey.cpp b/src/pubkey.cpp index be4ee27cd4..6392f8457a 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -6,6 +6,7 @@ #include #include +#include namespace { diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 5341a17420..7143e6f0f2 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -258,74 +258,74 @@ BitcoinGUI::~BitcoinGUI() void BitcoinGUI::createActions() { - QActionGroup *tabGroup = new QActionGroup(this); - - overviewAction = new QAction(platformStyle->SingleColorIcon(":/icons/overview"), tr("&Overview"), this); - overviewAction->setStatusTip(tr("Show general overview of wallet")); - overviewAction->setToolTip(overviewAction->statusTip()); - overviewAction->setCheckable(true); - overviewAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_1)); - tabGroup->addAction(overviewAction); - - sendCoinsAction = new QAction(platformStyle->SingleColorIcon(":/icons/send"), tr("&Send"), this); - sendCoinsAction->setStatusTip(tr("Send coins to a Zcoin address")); - sendCoinsAction->setToolTip(sendCoinsAction->statusTip()); - sendCoinsAction->setCheckable(true); - sendCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_2)); - tabGroup->addAction(sendCoinsAction); - - sendCoinsMenuAction = new QAction(platformStyle->TextColorIcon(":/icons/send"), sendCoinsAction->text(), this); - sendCoinsMenuAction->setStatusTip(sendCoinsAction->statusTip()); - sendCoinsMenuAction->setToolTip(sendCoinsMenuAction->statusTip()); - - receiveCoinsAction = new QAction(platformStyle->SingleColorIcon(":/icons/receiving_addresses"), tr("&Receive"), this); - receiveCoinsAction->setStatusTip(tr("Request payments (generates QR codes and zcoin: URIs)")); - receiveCoinsAction->setToolTip(receiveCoinsAction->statusTip()); - receiveCoinsAction->setCheckable(true); - receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3)); - tabGroup->addAction(receiveCoinsAction); - - receiveCoinsMenuAction = new QAction(platformStyle->TextColorIcon(":/icons/receiving_addresses"), receiveCoinsAction->text(), this); - receiveCoinsMenuAction->setStatusTip(receiveCoinsAction->statusTip()); - receiveCoinsMenuAction->setToolTip(receiveCoinsMenuAction->statusTip()); - - historyAction = new QAction(platformStyle->SingleColorIcon(":/icons/history"), tr("&Transactions"), this); - historyAction->setStatusTip(tr("Browse transaction history")); - historyAction->setToolTip(historyAction->statusTip()); - historyAction->setCheckable(true); - historyAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_4)); - tabGroup->addAction(historyAction); - - zerocoinAction = new QAction(platformStyle->SingleColorIcon(":/icons/zerocoin"), tr("&Zerocoin"), this); - zerocoinAction->setStatusTip(tr("Show the list of public coin that have been minted")); - zerocoinAction->setToolTip(zerocoinAction->statusTip()); - zerocoinAction->setCheckable(true); - zerocoinAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_5)); - tabGroup->addAction(zerocoinAction); - - zerocoinMenuAction = new QAction(platformStyle->TextColorIcon(":/icons/zerocoin"), zerocoinAction->text(), this); - zerocoinMenuAction->setStatusTip(zerocoinAction->statusTip()); - zerocoinMenuAction->setToolTip(zerocoinMenuAction->statusTip()); + QActionGroup *tabGroup = new QActionGroup(this); + + overviewAction = new QAction(platformStyle->SingleColorIcon(":/icons/overview"), tr("&Overview"), this); + overviewAction->setStatusTip(tr("Show general overview of wallet")); + overviewAction->setToolTip(overviewAction->statusTip()); + overviewAction->setCheckable(true); + overviewAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_1)); + tabGroup->addAction(overviewAction); + + sendCoinsAction = new QAction(platformStyle->SingleColorIcon(":/icons/send"), tr("&Send"), this); + sendCoinsAction->setStatusTip(tr("Send coins to a Zcoin address")); + sendCoinsAction->setToolTip(sendCoinsAction->statusTip()); + sendCoinsAction->setCheckable(true); + sendCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_2)); + tabGroup->addAction(sendCoinsAction); + + sendCoinsMenuAction = new QAction(platformStyle->TextColorIcon(":/icons/send"), sendCoinsAction->text(), this); + sendCoinsMenuAction->setStatusTip(sendCoinsAction->statusTip()); + sendCoinsMenuAction->setToolTip(sendCoinsMenuAction->statusTip()); + + receiveCoinsAction = new QAction(platformStyle->SingleColorIcon(":/icons/receiving_addresses"), tr("&Receive"), this); + receiveCoinsAction->setStatusTip(tr("Request payments (generates QR codes and zcoin: URIs)")); + receiveCoinsAction->setToolTip(receiveCoinsAction->statusTip()); + receiveCoinsAction->setCheckable(true); + receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3)); + tabGroup->addAction(receiveCoinsAction); + + receiveCoinsMenuAction = new QAction(platformStyle->TextColorIcon(":/icons/receiving_addresses"), receiveCoinsAction->text(), this); + receiveCoinsMenuAction->setStatusTip(receiveCoinsAction->statusTip()); + receiveCoinsMenuAction->setToolTip(receiveCoinsMenuAction->statusTip()); + + historyAction = new QAction(platformStyle->SingleColorIcon(":/icons/history"), tr("&Transactions"), this); + historyAction->setStatusTip(tr("Browse transaction history")); + historyAction->setToolTip(historyAction->statusTip()); + historyAction->setCheckable(true); + historyAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_4)); + tabGroup->addAction(historyAction); + + zerocoinAction = new QAction(platformStyle->SingleColorIcon(":/icons/zerocoin"), tr("&Zerocoin"), this); + zerocoinAction->setStatusTip(tr("Show the list of public coin that have been minted")); + zerocoinAction->setToolTip(zerocoinAction->statusTip()); + zerocoinAction->setCheckable(true); + zerocoinAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_5)); + tabGroup->addAction(zerocoinAction); + + zerocoinMenuAction = new QAction(platformStyle->TextColorIcon(":/icons/zerocoin"), zerocoinAction->text(), this); + zerocoinMenuAction->setStatusTip(zerocoinAction->statusTip()); + zerocoinMenuAction->setToolTip(zerocoinMenuAction->statusTip()); #ifdef ENABLE_WALLET - // 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())); - connect(overviewAction, SIGNAL(triggered()), this, SLOT(gotoOverviewPage())); - connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); - connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(gotoSendCoinsPage())); - connect(sendCoinsMenuAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); - connect(sendCoinsMenuAction, SIGNAL(triggered()), this, SLOT(gotoSendCoinsPage())); - connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); - connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(gotoReceiveCoinsPage())); - connect(receiveCoinsMenuAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); - connect(receiveCoinsMenuAction, SIGNAL(triggered()), this, SLOT(gotoReceiveCoinsPage())); - connect(historyAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); - connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryPage())); - connect(zerocoinAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); - connect(zerocoinAction, SIGNAL(triggered()), this, SLOT(gotoZerocoinPage())); + // 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())); + connect(overviewAction, SIGNAL(triggered()), this, SLOT(gotoOverviewPage())); + connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(gotoSendCoinsPage())); + connect(sendCoinsMenuAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(sendCoinsMenuAction, SIGNAL(triggered()), this, SLOT(gotoSendCoinsPage())); + connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(gotoReceiveCoinsPage())); + connect(receiveCoinsMenuAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(receiveCoinsMenuAction, SIGNAL(triggered()), this, SLOT(gotoReceiveCoinsPage())); + connect(historyAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryPage())); + connect(zerocoinAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); + connect(zerocoinAction, SIGNAL(triggered()), this, SLOT(gotoZerocoinPage())); #endif // ENABLE_WALLET quitAction = new QAction(platformStyle->TextColorIcon(":/icons/quit"), tr("E&xit"), this); diff --git a/src/random.cpp b/src/random.cpp index d9a8cc145e..9a9eb30e75 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -197,3 +197,21 @@ void seed_insecure_rand(bool fDeterministic) insecure_rand_Rw = tmp; } } + +InsecureRand::InsecureRand(bool _fDeterministic) + : nRz(11), + nRw(11), + fDeterministic(_fDeterministic) +{ + // The seed values have some unlikely fixed points which we avoid. + if(fDeterministic) return; + uint32_t nTmp; + do { + GetRandBytes((unsigned char*)&nTmp, 4); + } while (nTmp == 0 || nTmp == 0x9068ffffU); + nRz = nTmp; + do { + GetRandBytes((unsigned char*)&nTmp, 4); + } while (nTmp == 0 || nTmp == 0x464fffffU); + nRw = nTmp; +} diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index dbb8fa0726..772c55eb92 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -627,9 +627,13 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) int i = 0; unsigned int COUNT_SPEND_ZC_TX = 0; unsigned int MAX_SPEND_ZC_TX_PER_BLOCK = 0; - if(chainActive.Height() + 1 > HF_ZEROSPEND_FIX){ + if(chainActive.Height() + 1 > OLD_LIMIT_SPEND_TXS){ MAX_SPEND_ZC_TX_PER_BLOCK = 1; } + + if(chainActive.Height() + 1 > SWITCH_TO_MORE_SPEND_TXS){ + MAX_SPEND_ZC_TX_PER_BLOCK = 5; + } BOOST_FOREACH (CTransaction& tx, pblock->vtx) { uint256 txHash = tx.GetHash(); setTxIndex[txHash] = i++; diff --git a/src/rpc/rpcznode.cpp b/src/rpc/rpcznode.cpp index 5ac3a49ba8..4e932762b0 100644 --- a/src/rpc/rpcznode.cpp +++ b/src/rpc/rpcznode.cpp @@ -1,7 +1,3 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - #include "activeznode.h" #include "darksend.h" #include "init.h" @@ -143,8 +139,8 @@ UniValue znode(const UniValue ¶ms, bool fHelp) { CService addr = CService(strAddress); -// CNode *pnode = ConnectNodeDash(CAddress(addr, NODE_NETWORK), NULL); -// if (!pnode) + CNode *pnode = ConnectNode(CAddress(addr, NODE_NETWORK), NULL); + if (!pnode) throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Couldn't connect to znode %s", strAddress)); return "successfully connected"; @@ -299,8 +295,7 @@ UniValue znode(const UniValue ¶ms, bool fHelp) { UniValue resultsObj(UniValue::VOBJ); - BOOST_FOREACH(CZnodeConfig::CZnodeEntry - mne, znodeConfig.getEntries()) { + BOOST_FOREACH(CZnodeConfig::CZnodeEntry mne, znodeConfig.getEntries()) { std::string strError; CTxIn vin = CTxIn(uint256S(mne.getTxHash()), uint32_t(atoi(mne.getOutputIndex().c_str()))); @@ -372,7 +367,7 @@ UniValue znode(const UniValue ¶ms, bool fHelp) { if (strCommand == "outputs") { // Find possible candidates std::vector vPossibleCoins; - pwalletMain->AvailableCoinsDash(vPossibleCoins, true, NULL, false, ONLY_1000); + pwalletMain->AvailableCoins(vPossibleCoins, true, NULL, false, ONLY_1000); UniValue obj(UniValue::VOBJ); BOOST_FOREACH(COutput & out, vPossibleCoins) diff --git a/src/serialize.h b/src/serialize.h index ea8a54bd3f..4ddbc2ff0b 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -230,6 +230,26 @@ template inline void Serialize(Stream& s, bool a, int, int=0) template inline void Unserialize(Stream& s, bool& a, int, int=0) { char f=ser_readdata8(s); a=f; } +inline unsigned int GetSerializeSize(const unsigned char a[32], int, int=0){ + return sizeof(unsigned char) * 32; +} + +template inline void Serialize(Stream& s, const unsigned char a[32], int, int=0) +{ + int r; + for( r = 0; r < 32; r++){ + WRITEDATA(s, a[r]); + } +} + +template inline void Unserialize(Stream& s, unsigned char a[32], int, int=0) +{ + int r; + for( r = 0; r < 32; r++){ + READDATA(s, a[r]); + } + +} diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 39c2a7a41e..4b7e5f12a2 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2690,6 +2690,42 @@ UniValue mintzerocoin(const UniValue& params, bool fHelp) // stored in a secure location (wallet) at the client. libzerocoin::PrivateCoin newCoin(ZCParams, denomination); + std::list listPubCoin = std::list(); + CWalletDB walletdb(pwalletMain->strWalletFile); + walletdb.ListPubCoin(listPubCoin); + + int currentId = 1; + unsigned int countExistingItems = 0; + + BOOST_FOREACH(const CZerocoinEntry &pubCoinIdItem, listPubCoin) { + //LogPrintf("denomination = %d, id = %d, height = %d\n", pubCoinIdItem.denomination, pubCoinIdItem.id, pubCoinIdItem.nHeight); + if (pubCoinIdItem.id > 0) { + if(pubCoinIdItem.nHeight <= chainActive.Height()){ + if (pubCoinIdItem.denomination == denomination) { + countExistingItems++; + if (pubCoinIdItem.id > currentId) { + currentId = pubCoinIdItem.id; + countExistingItems = 1; + } + } + }else{ + break; + } + } + } + + if (countExistingItems > 9) { + currentId++; + } + + if (((denomination == libzerocoin::ZQ_LOVELACE) && (currentId >= ZC_V2_SWITCH_ID_1)) + || ((denomination == libzerocoin::ZQ_GOLDWASSER) && (currentId >= ZC_V2_SWITCH_ID_10)) + || ((denomination == libzerocoin::ZQ_RACKOFF) && (currentId >= ZC_V2_SWITCH_ID_25)) + || ((denomination == libzerocoin::ZQ_PEDERSEN) && (currentId >= ZC_V2_SWITCH_ID_50)) + || ((denomination == libzerocoin::ZQ_WILLIAMSON) && (currentId >= ZC_V2_SWITCH_ID_100))) { + newCoin.setVersion(2); + } + // Get a copy of the 'public' portion of the coin. You should // embed this into a Zerocoin 'MINT' transaction along with a series diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 6488b46d99..fa82be69fe 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -26,6 +26,8 @@ #include "utilmoneystr.h" #include "validation.h" #include "darksend.h" +#include "instantx.h" +#include "znode.h" #include "random.h" #include @@ -73,6 +75,33 @@ struct CompareValueOnly { } }; +struct CompareByPriority +{ + bool operator()(const COutput& t1, + const COutput& t2) const + { + return t1.Priority() > t2.Priority(); + } +}; + +struct CompareByAmount +{ + bool operator()(const CompactTallyItem& t1, const CompactTallyItem& t2) const + { + return t1.nAmount > t2.nAmount; + } +}; + +int COutput::Priority() const +{ + BOOST_FOREACH(CAmount d, vecPrivateSendDenominations) + 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); +} + std::string COutput::ToString() const { return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->vout[i].nValue)); } @@ -268,8 +297,7 @@ bool CWallet::Unlock(const SecureString &strWalletPassphrase) { { LOCK(cs_wallet); - BOOST_FOREACH( - const MasterKeyMap::value_type &pMasterKey, mapMasterKeys) + BOOST_FOREACH(const MasterKeyMap::value_type &pMasterKey, mapMasterKeys) { if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) @@ -389,8 +417,7 @@ set CWallet::GetConflicts(const uint256 &txid) const { std::pair range; - BOOST_FOREACH( - const CTxIn &txin, wtx.vin) + BOOST_FOREACH(const CTxIn &txin, wtx.vin) { if (mapTxSpends.count(txin.prevout) <= 1) continue; // No conflict if zero or one spends @@ -526,8 +553,7 @@ void CWallet::AddToSpends(const uint256 &wtxid) { if (thisTx.IsCoinBase() || thisTx.IsZerocoinSpend()) // Coinbases don't spend anything! return; - BOOST_FOREACH( - const CTxIn &txin, thisTx.vin) + BOOST_FOREACH(const CTxIn &txin, thisTx.vin) AddToSpends(txin.prevout, wtxid); } @@ -689,8 +715,7 @@ bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bFo for (map::iterator it = mapWallet.begin(); it != mapWallet.end() && account.vchPubKey.IsValid(); ++it) - BOOST_FOREACH( - const CTxOut &txout, (*it).second.vout) + BOOST_FOREACH(const CTxOut &txout, (*it).second.vout) if (txout.scriptPubKey == scriptPubKey) { bForceNew = true; break; @@ -715,8 +740,7 @@ bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bFo void CWallet::MarkDirty() { { LOCK(cs_wallet); - BOOST_FOREACH(PAIRTYPE( - const uint256, CWalletTx)&item, mapWallet) + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)&item, mapWallet) item.second.MarkDirty(); } } @@ -934,8 +958,7 @@ bool CWallet::AbandonTransaction(const uint256 &hashTx) { } // If a transaction changes 'conflicted' state, that changes the balance // available of the outputs it spends. So force those to be recomputed - BOOST_FOREACH( - const CTxIn &txin, wtx.vin) + BOOST_FOREACH(const CTxIn &txin, wtx.vin) { if (mapWallet.count(txin.prevout.hash)) mapWallet[txin.prevout.hash].MarkDirty(); @@ -1100,8 +1123,7 @@ bool CWallet::IsFromMe(const CTransaction &tx) const { CAmount CWallet::GetDebit(const CTransaction &tx, const isminefilter &filter) const { CAmount nDebit = 0; - BOOST_FOREACH( - const CTxIn &txin, tx.vin) + BOOST_FOREACH(const CTxIn &txin, tx.vin) { nDebit += GetDebit(txin, filter); if (!MoneyRange(nDebit)) @@ -1112,8 +1134,7 @@ CAmount CWallet::GetDebit(const CTransaction &tx, const isminefilter &filter) co CAmount CWallet::GetCredit(const CTransaction &tx, const isminefilter &filter) const { CAmount nCredit = 0; - BOOST_FOREACH( - const CTxOut &txout, tx.vout) + BOOST_FOREACH(const CTxOut &txout, tx.vout) { nCredit += GetCredit(txout, filter); if (!MoneyRange(nCredit)) @@ -1124,8 +1145,7 @@ CAmount CWallet::GetCredit(const CTransaction &tx, const isminefilter &filter) c CAmount CWallet::GetChange(const CTransaction &tx) const { CAmount nChange = 0; - BOOST_FOREACH( - const CTxOut &txout, tx.vout) + BOOST_FOREACH(const CTxOut &txout, tx.vout) { nChange += GetChange(txout); if (!MoneyRange(nChange)) @@ -1288,15 +1308,13 @@ void CWalletTx::GetAccountAmounts(const string &strAccount, CAmount &nReceived, GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); if (strAccount == strSentAccount) { - BOOST_FOREACH( - const COutputEntry &s, listSent) + BOOST_FOREACH(const COutputEntry &s, listSent) nSent += s.amount; nFee = allFee; } { LOCK(pwallet->cs_wallet); - BOOST_FOREACH( - const COutputEntry &r, listReceived) + BOOST_FOREACH(const COutputEntry &r, listReceived) { if (pwallet->mapAddressBook.count(r.destination)) { map::const_iterator mi = pwallet->mapAddressBook.find(r.destination); @@ -1377,7 +1395,7 @@ void CWallet::ReacceptWalletTransactions() { int nDepth = wtx.GetDepthInMainChain(); - if ((wtx.IsCoinBase() || wtx.IsZerocoinSpend()) && (nDepth == 0 && !wtx.isAbandoned())) + if ((wtx.IsCoinBase() || wtx.IsZerocoinSpend()) && (nDepth == 0 && !wtx.isAbandoned())) continue; if (nDepth == 0 && !wtx.isAbandoned()) { @@ -1585,8 +1603,7 @@ bool CWalletTx::IsTrusted() const { return false; // Trusted if all inputs are from us and are in the mempool: - BOOST_FOREACH( - const CTxIn &txin, vin) + BOOST_FOREACH(const CTxIn &txin, vin) { // Transactions not sent by us: not trusted const CWalletTx *parent = pwallet->GetWalletTx(txin.prevout.hash); @@ -1612,10 +1629,8 @@ std::vector CWallet::ResendWalletTransactionsBefore(int64_t nTime) { LOCK(cs_wallet); // Sort them in chronological order - multimap < unsigned - int, CWalletTx * > mapSorted; - BOOST_FOREACH(PAIRTYPE( - const uint256, CWalletTx)&item, mapWallet) + multimap < unsigned int, CWalletTx * > mapSorted; + BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)&item, mapWallet) { CWalletTx &wtx = item.second; // Don't rebroadcast if newer than nTime: @@ -1623,8 +1638,7 @@ std::vector CWallet::ResendWalletTransactionsBefore(int64_t nTime) { continue; mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); } - BOOST_FOREACH(PAIRTYPE( - const unsigned int, CWalletTx *)&item, mapSorted) + BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx *)&item, mapSorted) { CWalletTx &wtx = *item.second; if (wtx.RelayWalletTransaction()) @@ -1680,36 +1694,34 @@ CAmount CWallet::GetBalance() const { return nTotal; } -CAmount CWallet::GetAnonymizableBalance(bool fSkipDenominated) const -{ - if(fLiteMode) return 0; +CAmount CWallet::GetAnonymizableBalance(bool fSkipDenominated) const { + if (fLiteMode) return 0; - std::vector vecTally; - if(!SelectCoinsGrouppedByAddresses(vecTally, fSkipDenominated)) return 0; + std::vector vecTally; + if (!SelectCoinsGrouppedByAddresses(vecTally, fSkipDenominated)) return 0; CAmount nTotal = 0; - BOOST_FOREACH(CompactTallyItem& item, vecTally) { + BOOST_FOREACH(CompactTallyItem & item, vecTally) + { bool fIsDenominated = IsDenominatedAmount(item.nAmount); - if(fSkipDenominated && fIsDenominated) continue; + if (fSkipDenominated && fIsDenominated) continue; // assume that the fee to create denoms be PRIVATESEND_COLLATERAL at max - if(item.nAmount >= vecPrivateSendDenominations.back() + (fIsDenominated ? 0 : PRIVATESEND_COLLATERAL)) + if (item.nAmount >= vecPrivateSendDenominations.back() + (fIsDenominated ? 0 : PRIVATESEND_COLLATERAL)) nTotal += item.nAmount; } return nTotal; } -CAmount CWallet::GetAnonymizedBalance() const -{ - if(fLiteMode) return 0; +CAmount CWallet::GetAnonymizedBalance() const { + if (fLiteMode) return 0; CAmount nTotal = 0; { LOCK2(cs_main, cs_wallet); - for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - const CWalletTx* pcoin = &(*it).second; + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { + const CWalletTx *pcoin = &(*it).second; if (pcoin->IsTrusted()) nTotal += 0; @@ -1720,8 +1732,7 @@ CAmount CWallet::GetAnonymizedBalance() const return nTotal; } -CAmount CWalletTx::GetAnonymizedCredit(bool fUseCache) const -{ +CAmount CWalletTx::GetAnonymizedCredit(bool fUseCache) const { if (pwallet == 0) return 0; @@ -1734,16 +1745,15 @@ CAmount CWalletTx::GetAnonymizedCredit(bool fUseCache) const CAmount nCredit = 0; uint256 hashTx = GetHash(); - for (unsigned int i = 0; i < vout.size(); i++) - { + for (unsigned int i = 0; i < vout.size(); i++) { const CTxOut &txout = vout[i]; const CTxIn txin = CTxIn(hashTx, i); - if(pwallet->IsSpent(hashTx, i) || !pwallet->IsDenominated(txin)) continue; + if (pwallet->IsSpent(hashTx, i) || !pwallet->IsDenominated(txin)) continue; // const int nRounds = pwallet->GetInputPrivateSendRounds(txin); const int nRounds = 0; - if(nRounds >= nPrivateSendRounds){ + if (nRounds >= nPrivateSendRounds) { nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE); if (!MoneyRange(nCredit)) throw std::runtime_error("CWalletTx::GetAnonymizedCredit() : value out of range"); @@ -1756,12 +1766,11 @@ CAmount CWalletTx::GetAnonymizedCredit(bool fUseCache) const } -CAmount CWallet::GetNeedsToBeAnonymizedBalance(CAmount nMinBalance) const -{ - if(fLiteMode) return 0; +CAmount CWallet::GetNeedsToBeAnonymizedBalance(CAmount nMinBalance) const { + if (fLiteMode) return 0; CAmount nAnonymizedBalance = GetAnonymizedBalance(); - CAmount nNeedsToAnonymizeBalance = nPrivateSendAmount*COIN - nAnonymizedBalance; + CAmount nNeedsToAnonymizeBalance = nPrivateSendAmount * COIN - nAnonymizedBalance; // try to overshoot target DS balance up to nMinBalance nNeedsToAnonymizeBalance += nMinBalance; @@ -1769,10 +1778,10 @@ CAmount CWallet::GetNeedsToBeAnonymizedBalance(CAmount nMinBalance) const CAmount nAnonymizableBalance = GetAnonymizableBalance(); // anonymizable balance is way too small - if(nAnonymizableBalance < nMinBalance) return 0; + if (nAnonymizableBalance < nMinBalance) return 0; // not enough funds to anonymze amount we want, try the max we can - if(nNeedsToAnonymizeBalance > nAnonymizableBalance) nNeedsToAnonymizeBalance = nAnonymizableBalance; + if (nNeedsToAnonymizeBalance > nAnonymizableBalance) nNeedsToAnonymizeBalance = nAnonymizableBalance; // we should never exceed the pool max if (nNeedsToAnonymizeBalance > PRIVATESEND_POOL_MAX) nNeedsToAnonymizeBalance = PRIVATESEND_POOL_MAX; @@ -1780,16 +1789,14 @@ CAmount CWallet::GetNeedsToBeAnonymizedBalance(CAmount nMinBalance) const return nNeedsToAnonymizeBalance; } -CAmount CWallet::GetDenominatedBalance(bool unconfirmed) const -{ - if(fLiteMode) return 0; +CAmount CWallet::GetDenominatedBalance(bool unconfirmed) const { + if (fLiteMode) return 0; CAmount nTotal = 0; { LOCK2(cs_main, cs_wallet); - for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - const CWalletTx* pcoin = &(*it).second; + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { + const CWalletTx *pcoin = &(*it).second; // nTotal += pcoin->GetDenominatedCredit(unconfirmed); } @@ -1799,7 +1806,6 @@ CAmount CWallet::GetDenominatedBalance(bool unconfirmed) const } - CAmount CWallet::GetUnconfirmedBalance() const { CAmount nTotal = 0; { @@ -1852,13 +1858,100 @@ CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const { return nTotal; } -bool CWallet::IsDenominated(const CTxIn &txin) const +// Recursively determine the rounds of a given input (How deep is the PrivateSend chain for a given input) +int CWallet::GetRealInputPrivateSendRounds(CTxIn txin, int nRounds) const +{ + static std::map mDenomWtxes; + + if(nRounds >= 16) return 15; // 16 rounds max + + uint256 hash = txin.prevout.hash; + unsigned int nout = txin.prevout.n; + + const CWalletTx* wtx = GetWalletTx(hash); + if(wtx != NULL) + { + std::map::const_iterator mdwi = mDenomWtxes.find(hash); + if (mdwi == mDenomWtxes.end()) { + // not known yet, let's add it + LogPrint("privatesend", "GetRealInputPrivateSendRounds INSERTING %s\n", hash.ToString()); + mDenomWtxes[hash] = CMutableTransaction(*wtx); + } else if(mDenomWtxes[hash].vout[nout].nRounds != -10) { + // found and it's not an initial value, just return it + return mDenomWtxes[hash].vout[nout].nRounds; + } + + + // bounds check + if (nout >= wtx->vout.size()) { + // should never actually hit this + LogPrint("privatesend", "GetRealInputPrivateSendRounds UPDATED %s %3d %3d\n", hash.ToString(), nout, -4); + return -4; + } + + if (IsCollateralAmount(wtx->vout[nout].nValue)) { + mDenomWtxes[hash].vout[nout].nRounds = -3; + LogPrint("privatesend", "GetRealInputPrivateSendRounds UPDATED %s %3d %3d\n", hash.ToString(), nout, mDenomWtxes[hash].vout[nout].nRounds); + return mDenomWtxes[hash].vout[nout].nRounds; + } + + //make sure the final output is non-denominate + if (!IsDenominatedAmount(wtx->vout[nout].nValue)) { //NOT DENOM + mDenomWtxes[hash].vout[nout].nRounds = -2; + LogPrint("privatesend", "GetRealInputPrivateSendRounds UPDATED %s %3d %3d\n", hash.ToString(), nout, mDenomWtxes[hash].vout[nout].nRounds); + return mDenomWtxes[hash].vout[nout].nRounds; + } + + bool fAllDenoms = true; + BOOST_FOREACH(CTxOut out, wtx->vout) { + fAllDenoms = fAllDenoms && IsDenominatedAmount(out.nValue); + } + + // this one is denominated but there is another non-denominated output found in the same tx + if (!fAllDenoms) { + mDenomWtxes[hash].vout[nout].nRounds = 0; + LogPrint("privatesend", "GetRealInputPrivateSendRounds UPDATED %s %3d %3d\n", hash.ToString(), nout, mDenomWtxes[hash].vout[nout].nRounds); + return mDenomWtxes[hash].vout[nout].nRounds; + } + + int nShortest = -10; // an initial value, should be no way to get this by calculations + bool fDenomFound = false; + // only denoms here so let's look up + BOOST_FOREACH(CTxIn txinNext, wtx->vin) { + if (IsMine(txinNext)) { + int n = GetRealInputPrivateSendRounds(txinNext, nRounds + 1); + // denom found, find the shortest chain or initially assign nShortest with the first found value + if(n >= 0 && (n < nShortest || nShortest == -10)) { + nShortest = n; + fDenomFound = true; + } + } + } + mDenomWtxes[hash].vout[nout].nRounds = fDenomFound + ? (nShortest >= 15 ? 16 : nShortest + 1) // good, we a +1 to the shortest one but only 16 rounds max allowed + : 0; // too bad, we are the fist one in that chain + LogPrint("privatesend", "GetRealInputPrivateSendRounds UPDATED %s %3d %3d\n", hash.ToString(), nout, mDenomWtxes[hash].vout[nout].nRounds); + return mDenomWtxes[hash].vout[nout].nRounds; + } + + return nRounds - 1; +} + +// respect current settings +int CWallet::GetInputPrivateSendRounds(CTxIn txin) const { LOCK(cs_wallet); + int realPrivateSendRounds = GetRealInputPrivateSendRounds(txin, 0); + return realPrivateSendRounds > nPrivateSendRounds ? nPrivateSendRounds : realPrivateSendRounds; +} + + +bool CWallet::IsDenominated(const CTxIn &txin) const { + LOCK(cs_wallet); map::const_iterator mi = mapWallet.find(txin.prevout.hash); if (mi != mapWallet.end()) { - const CWalletTx& prev = (*mi).second; + const CWalletTx &prev = (*mi).second; if (txin.prevout.n < prev.vout.size()) { return IsDenominatedAmount(prev.vout[txin.prevout.n].nValue); } @@ -1867,32 +1960,31 @@ bool CWallet::IsDenominated(const CTxIn &txin) const return false; } -bool CWallet::IsDenominatedAmount(CAmount nInputAmount) const -{ -// BOOST_FOREACH(CAmount d, vecPrivateSendDenominations) -// if(nInputAmount == d) -// return true; +bool CWallet::IsDenominatedAmount(CAmount nInputAmount) const { + BOOST_FOREACH(CAmount d, vecPrivateSendDenominations) + if(nInputAmount == d) + return true; return false; } -int CWallet::CountInputsWithAmount(CAmount nInputAmount) -{ +int CWallet::CountInputsWithAmount(CAmount nInputAmount) { CAmount nTotal = 0; { LOCK2(cs_main, cs_wallet); - for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - const CWalletTx* pcoin = &(*it).second; - if (pcoin->IsTrusted()){ + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { + const CWalletTx *pcoin = &(*it).second; + if (pcoin->IsTrusted()) { int nDepth = pcoin->GetDepthInMainChain(false); for (unsigned int i = 0; i < pcoin->vout.size(); i++) { COutput out = COutput(pcoin, i, nDepth, true, true); CTxIn txin = CTxIn(out.tx->GetHash(), out.i); - if(out.tx->vout[out.i].nValue != nInputAmount) continue; - if(!IsDenominatedAmount(pcoin->vout[i].nValue)) continue; - if(IsSpent(out.tx->GetHash(), i) || IsMine(pcoin->vout[i]) != ISMINE_SPENDABLE || !IsDenominated(txin)) continue; + if (out.tx->vout[out.i].nValue != nInputAmount) continue; + if (!IsDenominatedAmount(pcoin->vout[i].nValue)) continue; + if (IsSpent(out.tx->GetHash(), i) || IsMine(pcoin->vout[i]) != ISMINE_SPENDABLE || + !IsDenominated(txin)) + continue; nTotal++; } @@ -1903,21 +1995,19 @@ int CWallet::CountInputsWithAmount(CAmount nInputAmount) return nTotal; } -bool CWallet::HasCollateralInputs(bool fOnlyConfirmed) const -{ - vector vCoins; - AvailableCoinsDash(vCoins, fOnlyConfirmed, NULL, false, ONLY_PRIVATESEND_COLLATERAL); +bool CWallet::HasCollateralInputs(bool fOnlyConfirmed) const { + vector vCoins; + AvailableCoins(vCoins, fOnlyConfirmed, NULL, false, ONLY_PRIVATESEND_COLLATERAL); return !vCoins.empty(); } -bool CWallet::IsCollateralAmount(CAmount nInputAmount) const -{ +bool CWallet::IsCollateralAmount(CAmount nInputAmount) const { // collateral inputs should always be a 2x..4x of PRIVATESEND_COLLATERAL - return nInputAmount >= PRIVATESEND_COLLATERAL * 2 && - nInputAmount <= PRIVATESEND_COLLATERAL * 4 && - nInputAmount % PRIVATESEND_COLLATERAL == 0; + return nInputAmount >= PRIVATESEND_COLLATERAL * 2 && + nInputAmount <= PRIVATESEND_COLLATERAL * 4 && + nInputAmount % PRIVATESEND_COLLATERAL == 0; } CAmount CWallet::GetImmatureWatchOnlyBalance() const { @@ -1933,7 +2023,7 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const { } void CWallet::AvailableCoins(vector &vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, - bool fIncludeZeroValue) const { + bool fIncludeZeroValue, AvailableCoinsType nCoinType, bool fUseInstantSend) const { vCoins.clear(); { @@ -1942,51 +2032,6 @@ void CWallet::AvailableCoins(vector &vCoins, bool fOnlyConfirmed, cons const uint256 &wtxid = it->first; const CWalletTx *pcoin = &(*it).second; - if (!CheckFinalTx(*pcoin)) - continue; - - if (fOnlyConfirmed && !pcoin->IsTrusted()) - continue; - - if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) - continue; - - int nDepth = pcoin->GetDepthInMainChain(); - if (nDepth < 0) - continue; - - // We should not consider coins which aren't at least in our mempool - // It's possible for these to be conflicted via ancestors which we may never be able to detect - if (nDepth == 0 && !pcoin->InMempool()) - continue; - - for (unsigned int i = 0; i < pcoin->vout.size(); i++) { - isminetype mine = IsMine(pcoin->vout[i]); - if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && - !IsLockedCoin((*it).first, i) && (pcoin->vout[i].nValue > nMinimumInputValue) && - (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || - coinControl->IsSelected(COutPoint((*it).first, i)))) - vCoins.push_back(COutput(pcoin, i, nDepth, - ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || - (coinControl && coinControl->fAllowWatchOnly && - (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO), - (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO)); - } - } - } -} - -void CWallet::AvailableCoinsDash(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue, AvailableCoinsType nCoinType, bool fUseInstantSend) const -{ - vCoins.clear(); - - { - LOCK2(cs_main, cs_wallet); - for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) - { - const uint256& wtxid = it->first; - const CWalletTx* pcoin = &(*it).second; - if (!CheckFinalTx(*pcoin)) continue; @@ -2008,28 +2053,29 @@ void CWallet::AvailableCoinsDash(vector& vCoins, bool fOnlyConfirmed, c for (unsigned int i = 0; i < pcoin->vout.size(); i++) { bool found = false; - if(nCoinType == ONLY_DENOMINATED) { + if (nCoinType == ONLY_DENOMINATED) { found = IsDenominatedAmount(pcoin->vout[i].nValue); - } else if(nCoinType == ONLY_NOT1000IFMN) { - found = !(fZNode && pcoin->vout[i].nValue == 1000*COIN); - } else if(nCoinType == ONLY_NONDENOMINATED_NOT1000IFMN) { + } else if (nCoinType == ONLY_NOT1000IFMN) { + found = !(fZNode && pcoin->vout[i].nValue == ZNODE_COIN_REQUIRED * COIN); + } else if (nCoinType == ONLY_NONDENOMINATED_NOT1000IFMN) { if (IsCollateralAmount(pcoin->vout[i].nValue)) continue; // do not use collateral amounts found = !IsDenominatedAmount(pcoin->vout[i].nValue); - if(found && fZNode) found = pcoin->vout[i].nValue != 1000*COIN; // do not use Hot MN funds - } else if(nCoinType == ONLY_1000) { - found = pcoin->vout[i].nValue == 1000*COIN; - } else if(nCoinType == ONLY_PRIVATESEND_COLLATERAL) { + if (found && fZNode) found = pcoin->vout[i].nValue != ZNODE_COIN_REQUIRED * COIN; // do not use Hot MN funds + } else if (nCoinType == ONLY_1000) { + found = pcoin->vout[i].nValue == ZNODE_COIN_REQUIRED * COIN; + } else if (nCoinType == ONLY_PRIVATESEND_COLLATERAL) { found = IsCollateralAmount(pcoin->vout[i].nValue); } else { found = true; } - if(!found) continue; + if (!found) continue; isminetype mine = IsMine(pcoin->vout[i]); + if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && - (!IsLockedCoin((*it).first, i) || nCoinType == ONLY_1000) && - (pcoin->vout[i].nValue > 0 || fIncludeZeroValue) && - (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i)))) + !IsLockedCoin((*it).first, i) && (pcoin->vout[i].nValue > nMinimumInputValue) && + (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || + coinControl->IsSelected(COutPoint((*it).first, i)))) vCoins.push_back(COutput(pcoin, i, nDepth, ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (coinControl && coinControl->fAllowWatchOnly && @@ -2041,34 +2087,33 @@ void CWallet::AvailableCoinsDash(vector& vCoins, bool fOnlyConfirmed, c } -bool CWallet::SelectCoinsDark(CAmount nValueMin, CAmount nValueMax, std::vector& vecTxInRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax) const -{ - CCoinControl *coinControl=NULL; +bool CWallet::SelectCoinsDark(CAmount nValueMin, CAmount nValueMax, std::vector &vecTxInRet, CAmount &nValueRet, + int nPrivateSendRoundsMin, int nPrivateSendRoundsMax) const { + CCoinControl *coinControl = NULL; vecTxInRet.clear(); nValueRet = 0; - vector vCoins; - AvailableCoinsDash(vCoins, true, coinControl, false, nPrivateSendRoundsMin < 0 ? ONLY_NONDENOMINATED_NOT1000IFMN : ONLY_DENOMINATED); + vector vCoins; + AvailableCoins(vCoins, true, coinControl, false, nPrivateSendRoundsMin < 0 ? ONLY_NONDENOMINATED_NOT1000IFMN : ONLY_DENOMINATED); //order the array so largest nondenom are first, then denominations, then very small inputs. -// sort(vCoins.rbegin(), vCoins.rend(), CompareByPriority()); + sort(vCoins.rbegin(), vCoins.rend(), CompareByPriority()); - BOOST_FOREACH(const COutput& out, vCoins) + BOOST_FOREACH(const COutput &out, vCoins) { //do not allow inputs less than 1/10th of minimum value - if(out.tx->vout[out.i].nValue < nValueMin/10) continue; + if (out.tx->vout[out.i].nValue < nValueMin / 10) continue; //do not allow collaterals to be selected - if(IsCollateralAmount(out.tx->vout[out.i].nValue)) continue; - if(fZNode && out.tx->vout[out.i].nValue == 1000*COIN) continue; //znode input + if (IsCollateralAmount(out.tx->vout[out.i].nValue)) continue; + if (fZNode && out.tx->vout[out.i].nValue == ZNODE_COIN_REQUIRED * COIN) continue; //masternode input - if(nValueRet + out.tx->vout[out.i].nValue <= nValueMax){ - CTxIn txin = CTxIn(out.tx->GetHash(),out.i); + if (nValueRet + out.tx->vout[out.i].nValue <= nValueMax) { + CTxIn txin = CTxIn(out.tx->GetHash(), out.i); -// int nRounds = GetInputPrivateSendRounds(txin); - int nRounds = 0; - if(nRounds >= nPrivateSendRoundsMax) continue; - if(nRounds < nPrivateSendRoundsMin) continue; + int nRounds = GetInputPrivateSendRounds(txin); + if (nRounds >= nPrivateSendRoundsMax) continue; + if (nRounds < nPrivateSendRoundsMin) continue; txin.prevPubKey = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey nValueRet += out.tx->vout[out.i].nValue; @@ -2079,37 +2124,56 @@ bool CWallet::SelectCoinsDark(CAmount nValueMin, CAmount nValueMax, std::vector< return nValueRet >= nValueMin; } -// znode -bool CWallet::GetZnodeVinAndKeys(CTxIn& txinRet, CPubKey& pubKeyRet, CKey& keyRet, std::string strTxHash, std::string strOutputIndex) +// masternode +bool CWallet::GetCollateralTxIn(CTxIn& txinRet, CAmount& nValueRet) const { + vector vCoins; + + AvailableCoins(vCoins); + + BOOST_FOREACH(const COutput& out, vCoins) + { + if(IsCollateralAmount(out.tx->vout[out.i].nValue)) + { + txinRet = CTxIn(out.tx->GetHash(), out.i); + txinRet.prevPubKey = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey + nValueRet = out.tx->vout[out.i].nValue; + return true; + } + } + + return false; +} + +bool CWallet::GetZnodeVinAndKeys(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; - AvailableCoinsDash(vPossibleCoins, true, NULL, false, ONLY_1000); - if(vPossibleCoins.empty()) { - LogPrintf("CWallet::GetZnodeVinAndKeys -- Could not locate any valid znode vin\n"); + std::vector vPossibleCoins; + AvailableCoins(vPossibleCoins, true, NULL, false, ONLY_1000); + 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 + 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! + BOOST_FOREACH(COutput & out, vPossibleCoins) + if (out.tx->GetHash() == txHash && out.i == nOutputIndex) // found it! return GetVinAndKeysFromOutput(out, txinRet, pubKeyRet, keyRet); - LogPrintf("CWallet::GetZnodeVinAndKeys -- Could not locate specified znode vin\n"); + LogPrintf("CWallet::GetMasternodeVinAndKeys -- Could not locate specified masternode vin\n"); return false; } -bool CWallet::GetVinAndKeysFromOutput(COutput out, CTxIn& txinRet, CPubKey& pubKeyRet, CKey& keyRet) -{ +bool CWallet::GetVinAndKeysFromOutput(COutput out, CTxIn &txinRet, CPubKey &pubKeyRet, CKey &keyRet) { // wait for reindex and/or import to finish if (fImporting || fReindex) return false; @@ -2129,7 +2193,7 @@ bool CWallet::GetVinAndKeysFromOutput(COutput out, CTxIn& txinRet, CPubKey& pubK } if (!GetKey(keyID, keyRet)) { - LogPrintf ("CWallet::GetVinAndKeysFromOutput -- Private key for address is not known\n"); + LogPrintf("CWallet::GetVinAndKeysFromOutput -- Private key for address is not known\n"); return false; } @@ -2201,9 +2265,10 @@ void CWallet::ListAvailableCoinsMintCoins(vector &vCoins, bool fOnlyCo } } -static void ApproximateBestSubset(vector >> vValue, const CAmount &nTotalLower, - const CAmount &nTargetValue, - vector &vfBest, CAmount &nBest, int iterations = 1000) { +static void ApproximateBestSubset(vector >> vValue, + const CAmount &nTotalLower, + const CAmount &nTargetValue, + vector &vfBest, CAmount &nBest, int iterations = 1000) { vector vfIncluded; vfBest.assign(vValue.size(), true); @@ -2243,7 +2308,8 @@ static void ApproximateBestSubset(vector vCoins, - set > &setCoinsRet, CAmount &nValueRet) const { + set > &setCoinsRet, + CAmount &nValueRet) const { setCoinsRet.clear(); nValueRet = 0; @@ -2256,8 +2322,7 @@ bool CWallet::SelectCoinsMinConf(const CAmount &nTargetValue, const int nConfMin random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); - BOOST_FOREACH( - const COutput &output, vCoins) + BOOST_FOREACH(const COutput &output, vCoins) { if (!output.fSpendable) continue; @@ -2338,22 +2403,49 @@ bool CWallet::SelectCoinsMinConf(const CAmount &nTargetValue, const int nConfMin bool CWallet::SelectCoins(const vector &vAvailableCoins, const CAmount &nTargetValue, set > &setCoinsRet, CAmount &nValueRet, - const CCoinControl *coinControl) const { + const CCoinControl *coinControl, AvailableCoinsType nCoinType, bool fUseInstantSend) const { vector vCoins(vAvailableCoins); // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs) { - BOOST_FOREACH( - const COutput &out, vCoins) + BOOST_FOREACH(const COutput &out, vCoins) { if (!out.fSpendable) continue; + if (nCoinType == ONLY_DENOMINATED) { + CTxIn txin = CTxIn(out.tx->GetHash(), out.i); + int nRounds = GetInputPrivateSendRounds(txin); + // make sure it's actually anonymized + if (nRounds < nPrivateSendRounds) continue; + } nValueRet += out.tx->vout[out.i].nValue; setCoinsRet.insert(make_pair(out.tx, out.i)); } return (nValueRet >= nTargetValue); } + //if we're doing only denominated, we need to round up to the nearest smallest denomination + if (nCoinType == ONLY_DENOMINATED) { + CAmount nSmallestDenom = vecPrivateSendDenominations.back(); + // Make outputs by looping through denominations, from large to small + BOOST_FOREACH(CAmount nDenom, vecPrivateSendDenominations) + { + BOOST_FOREACH(const COutput &out, vCoins) + { + //make sure it's the denom we're looking for, round the amount up to smallest denom + if (out.tx->vout[out.i].nValue == nDenom && nValueRet + nDenom < nTargetValue + nSmallestDenom) { + CTxIn txin = CTxIn(out.tx->GetHash(), out.i); + int nRounds = GetInputPrivateSendRounds(txin); + // make sure it's actually anonymized + if (nRounds < nPrivateSendRounds) continue; + nValueRet += nDenom; + setCoinsRet.insert(make_pair(out.tx, out.i)); + } + } + } + return (nValueRet >= nTargetValue); + } + // calculate value from preset inputs and store them set > setPresetCoins; CAmount nValueFromPresetInputs = 0; @@ -2361,8 +2453,7 @@ bool CWallet::SelectCoins(const vector &vAvailableCoins, const CAmount std::vector vPresetInputs; if (coinControl) coinControl->ListSelected(vPresetInputs); - BOOST_FOREACH( - const COutPoint &outpoint, vPresetInputs) + BOOST_FOREACH(const COutPoint &outpoint, vPresetInputs) { map::const_iterator it = mapWallet.find(outpoint.hash); if (it != mapWallet.end()) { @@ -2393,7 +2484,8 @@ bool CWallet::SelectCoins(const vector &vAvailableCoins, const CAmount SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 6, 0, vCoins, setCoinsRet, nValueRet) || SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 1, 0, vCoins, setCoinsRet, nValueRet) || (bSpendZeroConfChange && - SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, 2, vCoins, setCoinsRet, nValueRet)) || + SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, 2, vCoins, setCoinsRet, + nValueRet)) || (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::min((size_t) 4, nMaxChainLength / 3), vCoins, setCoinsRet, nValueRet)) || @@ -2401,10 +2493,12 @@ bool CWallet::SelectCoins(const vector &vAvailableCoins, const CAmount SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength / 2, vCoins, setCoinsRet, nValueRet)) || (bSpendZeroConfChange && - SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength, vCoins, setCoinsRet, + SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength, vCoins, + setCoinsRet, nValueRet)) || (bSpendZeroConfChange && !fRejectLongChains && - SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::numeric_limits::max(), + SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, + std::numeric_limits::max(), vCoins, setCoinsRet, nValueRet)); // because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset @@ -2422,8 +2516,7 @@ bool CWallet::FundTransaction(CMutableTransaction &tx, CAmount &nFeeRet, bool ov vector vecSend; // Turn the txout set into a CRecipient vector - BOOST_FOREACH( - const CTxOut &txOut, tx.vout) + BOOST_FOREACH(const CTxOut &txOut, tx.vout) { CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, false}; vecSend.push_back(recipient); @@ -2436,9 +2529,8 @@ bool CWallet::FundTransaction(CMutableTransaction &tx, CAmount &nFeeRet, bool ov coinControl.fOverrideFeeRate = overrideEstimatedFeeRate; coinControl.nFeeRate = specificFeeRate; - BOOST_FOREACH( - const CTxIn &txin, tx.vin) - coinControl.Select(txin.prevout); + BOOST_FOREACH(const CTxIn &txin, tx.vin) + coinControl.Select(txin.prevout); CReserveKey reservekey(this); CWalletTx wtx; @@ -2449,8 +2541,7 @@ bool CWallet::FundTransaction(CMutableTransaction &tx, CAmount &nFeeRet, bool ov tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.vout[nChangePosInOut]); // Add new txins (keeping original txin scriptSig/order) - BOOST_FOREACH( - const CTxIn &txin, wtx.vin) + BOOST_FOREACH(const CTxIn &txin, wtx.vin) { if (!coinControl.IsSelected(txin.prevout)) { tx.vin.push_back(txin); @@ -2465,12 +2556,11 @@ bool CWallet::FundTransaction(CMutableTransaction &tx, CAmount &nFeeRet, bool ov return true; } -bool CWallet::ConvertList(std::vector vecTxIn, std::vector& vecAmounts) -{ +bool CWallet::ConvertList(std::vector vecTxIn, std::vector &vecAmounts) { BOOST_FOREACH(CTxIn txin, vecTxIn) { if (mapWallet.count(txin.prevout.hash)) { - CWalletTx& wtx = mapWallet[txin.prevout.hash]; - if(txin.prevout.n < wtx.vout.size()){ + CWalletTx &wtx = mapWallet[txin.prevout.hash]; + if (txin.prevout.n < wtx.vout.size()) { vecAmounts.push_back(wtx.vout[txin.prevout.n].nValue); } } else { @@ -2480,14 +2570,15 @@ bool CWallet::ConvertList(std::vector vecTxIn, std::vector& vecA return true; } -bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount nValueMax, std::vector& vecTxInRet, std::vector& vCoinsRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax) -{ +bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount nValueMax, + std::vector &vecTxInRet, std::vector &vCoinsRet, + CAmount &nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax) { vecTxInRet.clear(); vCoinsRet.clear(); nValueRet = 0; - vector vCoins; - AvailableCoinsDash(vCoins, true, NULL, false, ONLY_DENOMINATED); + vector vCoins; + AvailableCoins(vCoins, true, NULL, false, ONLY_DENOMINATED); std::random_shuffle(vCoins.rbegin(), vCoins.rend(), GetRandInt); @@ -2504,30 +2595,27 @@ bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount int nDenomResult = 0; -// InsecureRand insecureRand; - BOOST_FOREACH(const COutput& out, vCoins) + InsecureRand insecureRand; + BOOST_FOREACH(const COutput &out, vCoins) { - // znode-like input should not be selected by AvailableCoins now anyway + // masternode-like input should not be selected by AvailableCoins now anyway //if(out.tx->vout[out.i].nValue == 1000*COIN) continue; - if(nValueRet + out.tx->vout[out.i].nValue <= nValueMax){ + if (nValueRet + out.tx->vout[out.i].nValue <= nValueMax) { CTxIn txin = CTxIn(out.tx->GetHash(), out.i); - int nRounds = 0; -// int nRounds = GetInputPrivateSendRounds(txin); - if(nRounds >= nPrivateSendRoundsMax) continue; - if(nRounds < nPrivateSendRoundsMin) continue; + int nRounds = GetInputPrivateSendRounds(txin); + if (nRounds >= nPrivateSendRoundsMax) continue; + if (nRounds < nPrivateSendRoundsMin) continue; BOOST_FOREACH(int nBit, vecBits) { - if(out.tx->vout[out.i].nValue == vecPrivateSendDenominations[nBit]) { - if(nValueRet >= nValueMin) { + if (out.tx->vout[out.i].nValue == vecPrivateSendDenominations[nBit]) { + if (nValueRet >= nValueMin) { //randomly reduce the max amount we'll submit (for anonymity) -// nValueMax -= (nValueMax/5); - nValueMax -= nValueMax; + nValueMax -= insecureRand(nValueMax/5); //on average use 50% of the inputs or less -// int r = insecureRand(vCoins.size()); - int r = vCoins.size(); - if((int)vecTxInRet.size() > r) return true; + int r = insecureRand(vCoins.size()); + if ((int) vecTxInRet.size() > r) return true; } txin.prevPubKey = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey nValueRet += out.tx->vout[out.i].nValue; @@ -2542,8 +2630,7 @@ bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount return nValueRet >= nValueMin && nDenom == nDenomResult; } -bool CWallet::CreateCollateralTransaction(CMutableTransaction& txCollateral, std::string& strReason) -{ +bool CWallet::CreateCollateralTransaction(CMutableTransaction &txCollateral, std::string &strReason) { txCollateral.vin.clear(); txCollateral.vout.clear(); @@ -2551,10 +2638,10 @@ bool CWallet::CreateCollateralTransaction(CMutableTransaction& txCollateral, std CAmount nValue = 0; CTxIn txinCollateral; -// if (!GetCollateralTxIn(txinCollateral, nValue)) { -// strReason = "PrivateSend requires a collateral transaction and could not locate an acceptable input!"; -// return false; -// } + if (!GetCollateralTxIn(txinCollateral, nValue)) { + strReason = "PrivateSend requires a collateral transaction and could not locate an acceptable input!"; + return false; + } // make our change address CScript scriptChange; @@ -2569,7 +2656,8 @@ bool CWallet::CreateCollateralTransaction(CMutableTransaction& txCollateral, std CTxOut txout = CTxOut(nValue - PRIVATESEND_COLLATERAL, scriptChange); txCollateral.vout.push_back(txout); - if(!SignSignature(*this, txinCollateral.prevPubKey, txCollateral, 0, NULL, int(SIGHASH_ALL|SIGHASH_ANYONECANPAY))) { + if (!SignSignature(*this, txinCollateral.prevPubKey, txCollateral, 0, NULL, + int(SIGHASH_ALL | SIGHASH_ANYONECANPAY))) { strReason = "Unable to sign collateral transaction!"; return false; } @@ -2577,57 +2665,57 @@ bool CWallet::CreateCollateralTransaction(CMutableTransaction& txCollateral, std return true; } -bool CWallet::SelectCoinsGrouppedByAddresses(std::vector& vecTallyRet, bool fSkipDenominated, bool fAnonymizable) const -{ +bool CWallet::SelectCoinsGrouppedByAddresses(std::vector &vecTallyRet, bool fSkipDenominated, + bool fAnonymizable) const { LOCK2(cs_main, cs_wallet); isminefilter filter = ISMINE_SPENDABLE; // try to use cache - if(fAnonymizable) { -// if(fSkipDenominated && fAnonymizableTallyCachedNonDenom) { -// vecTallyRet = vecAnonymizableTallyCachedNonDenom; -// LogPrint("selectcoins", "SelectCoinsGrouppedByAddresses - using cache for non-denom inputs\n"); -// return vecTallyRet.size() > 0; -// } -// if(!fSkipDenominated && fAnonymizableTallyCached) { -// vecTallyRet = vecAnonymizableTallyCached; -// LogPrint("selectcoins", "SelectCoinsGrouppedByAddresses - using cache for all inputs\n"); -// return vecTallyRet.size() > 0; -// } + if (fAnonymizable) { + if(fSkipDenominated && fAnonymizableTallyCachedNonDenom) { + vecTallyRet = vecAnonymizableTallyCachedNonDenom; + LogPrint("selectcoins", "SelectCoinsGrouppedByAddresses - using cache for non-denom inputs\n"); + return vecTallyRet.size() > 0; + } + if(!fSkipDenominated && fAnonymizableTallyCached) { + vecTallyRet = vecAnonymizableTallyCached; + LogPrint("selectcoins", "SelectCoinsGrouppedByAddresses - using cache for all inputs\n"); + return vecTallyRet.size() > 0; + } } // Tally - map mapTally; + map mapTally; for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { - const CWalletTx& wtx = (*it).second; + const CWalletTx &wtx = (*it).second; - if(wtx.IsCoinBase() && wtx.GetBlocksToMaturity() > 0) continue; - if(!fAnonymizable && !wtx.IsTrusted()) continue; + if (wtx.IsCoinBase() && wtx.GetBlocksToMaturity() > 0) continue; + if (!fAnonymizable && !wtx.IsTrusted()) continue; for (unsigned int i = 0; i < wtx.vout.size(); i++) { CTxDestination address; if (!ExtractDestination(wtx.vout[i].scriptPubKey, address)) continue; isminefilter mine = ::IsMine(*this, address); - if(!(mine & filter)) continue; + if (!(mine & filter)) continue; - if(IsSpent(wtx.GetHash(), i) || IsLockedCoin(wtx.GetHash(), i)) continue; + if (IsSpent(wtx.GetHash(), i) || IsLockedCoin(wtx.GetHash(), i)) continue; - if(fSkipDenominated && IsDenominatedAmount(wtx.vout[i].nValue)) continue; + if (fSkipDenominated && IsDenominatedAmount(wtx.vout[i].nValue)) continue; - if(fAnonymizable) { + if (fAnonymizable) { // ignore collaterals - if(IsCollateralAmount(wtx.vout[i].nValue)) continue; - if(fZNode && wtx.vout[i].nValue == 1000*COIN) continue; + if (IsCollateralAmount(wtx.vout[i].nValue)) continue; + if (fZNode && wtx.vout[i].nValue == ZNODE_COIN_REQUIRED * COIN) continue; // ignore outputs that are 10 times smaller then the smallest denomination // otherwise they will just lead to higher fee / lower priority - if(wtx.vout[i].nValue <= vecPrivateSendDenominations.back()/10) continue; + if (wtx.vout[i].nValue <= vecPrivateSendDenominations.back() / 10) continue; // ignore anonymized -// if(GetInputPrivateSendRounds(CTxIn(wtx.GetHash(), i)) >= nPrivateSendRounds) continue; + if(GetInputPrivateSendRounds(CTxIn(wtx.GetHash(), i)) >= nPrivateSendRounds) continue; } - CompactTallyItem& item = mapTally[address]; + CompactTallyItem &item = mapTally[address]; item.address = address; item.nAmount += wtx.vout[i].nValue; item.vecTxIn.push_back(CTxIn(wtx.GetHash(), i)); @@ -2636,29 +2724,29 @@ bool CWallet::SelectCoinsGrouppedByAddresses(std::vector& vecT // construct resulting vector vecTallyRet.clear(); - BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CompactTallyItem)& item, mapTally) { - if(fAnonymizable && item.second.nAmount < vecPrivateSendDenominations.back()) continue; + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CompactTallyItem)&item, mapTally) { + if (fAnonymizable && item.second.nAmount < vecPrivateSendDenominations.back()) continue; vecTallyRet.push_back(item.second); } // order by amounts per address, from smallest to largest -// sort(vecTallyRet.rbegin(), vecTallyRet.rend(), CompareByAmount()); + sort(vecTallyRet.rbegin(), vecTallyRet.rend(), CompareByAmount()); // cache anonymizable for later use - if(fAnonymizable) { - if(fSkipDenominated) { -// vecAnonymizableTallyCachedNonDenom = vecTallyRet; -// fAnonymizableTallyCachedNonDenom = true; + if (fAnonymizable) { + if (fSkipDenominated) { + vecAnonymizableTallyCachedNonDenom = vecTallyRet; + fAnonymizableTallyCachedNonDenom = true; } else { -// vecAnonymizableTallyCached = vecTallyRet; -// fAnonymizableTallyCached = true; + vecAnonymizableTallyCached = vecTallyRet; + fAnonymizableTallyCached = true; } } // debug std::string strMessage = "SelectCoinsGrouppedByAddresses - vecTallyRet:\n"; - BOOST_FOREACH(CompactTallyItem& item, vecTallyRet) - strMessage += strprintf(" %s %f\n", item.address.ToString().c_str(), float(item.nAmount)/COIN); + BOOST_FOREACH(CompactTallyItem & item, vecTallyRet) + strMessage += strprintf(" %s %f\n", item.address.ToString().c_str(), float(item.nAmount) / COIN); LogPrint("selectcoins", "%s", strMessage); return vecTallyRet.size() > 0; @@ -2667,7 +2755,7 @@ bool CWallet::SelectCoinsGrouppedByAddresses(std::vector& vecT bool CWallet::CreateTransaction(const vector &vecSend, CWalletTx &wtxNew, CReserveKey &reservekey, CAmount &nFeeRet, int &nChangePosInOut, std::string &strFailReason, const CCoinControl *coinControl, - bool sign) { + bool sign, AvailableCoinsType nCoinType, bool fUseInstantSend) { LogPrintf("CreateTransaction()\n"); CAmount nValue = 0; int nChangePosRequest = nChangePosInOut; @@ -2727,7 +2815,7 @@ bool CWallet::CreateTransaction(const vector &vecSend, CWalletTx &w LOCK2(cs_main, cs_wallet); { std::vector vAvailableCoins; - AvailableCoins(vAvailableCoins, true, coinControl); + AvailableCoins(vAvailableCoins, true, coinControl, false, nCoinType, fUseInstantSend); nFeeRet = payTxFee.GetFeePerK(); // Start with no fee and loop until there is enough fee @@ -2763,8 +2851,7 @@ bool CWallet::CreateTransaction(const vector &vecSend, CWalletTx &w if (txout.nValue < 0) strFailReason = _("The transaction amount is too small to pay the fee"); else - strFailReason = _( - "The transaction amount is too small to send after the fee has been deducted"); + strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); } else strFailReason = _("Transaction amount too small"); return false; @@ -2776,7 +2863,27 @@ bool CWallet::CreateTransaction(const vector &vecSend, CWalletTx &w set > setCoins; CAmount nValueIn = 0; if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl)) { - strFailReason = _("Insufficient funds"); + if (nCoinType == ONLY_NOT1000IFMN) { + strFailReason = _("Unable to locate enough funds for this transaction that are not equal 1000 DASH."); + } else if (nCoinType == ONLY_NONDENOMINATED_NOT1000IFMN) { + strFailReason = _("Unable to locate enough PrivateSend non-denominated funds for this transaction that are not equal 1000 DASH."); + } else if (nCoinType == ONLY_DENOMINATED) { + strFailReason = _("Unable to locate enough PrivateSend denominated funds for this transaction."); + strFailReason += _("PrivateSend uses exact denominated amounts to send funds, you might simply need to anonymize some more coins."); + } else if (nValueIn < nValueToSelect) { + strFailReason = _("Insufficient funds."); + } + if (fUseInstantSend) { + if (nValueIn > sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE) * COIN) { + strFailReason += " " + strprintf(_("InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH."), + sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)); + } else { + // could be not true but most likely that's the reason + strFailReason += " " + strprintf(_("InstantSend requires inputs with at least %d confirmations, you might need to wait a few minutes and try again."), + INSTANTSEND_CONFIRMATIONS_REQUIRED); + } + } + return false; } BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) @@ -2795,75 +2902,83 @@ bool CWallet::CreateTransaction(const vector &vecSend, CWalletTx &w const CAmount nChange = nValueIn - nValueToSelect; if (nChange > 0) { - // Fill a vout to ourself - // TODO: pass in scriptChange instead of reservekey so - // change transaction isn't always pay-to-bitcoin-address - CScript scriptChange; - - // coin control: send change to custom address - if (coinControl && !boost::get(&coinControl->destChange)) - scriptChange = GetScriptForDestination(coinControl->destChange); - - // no coin control: send change to newly generated address - else { - // Note: We use a new key here to keep it from being obvious which side is the change. - // The drawback is that by not reusing a previous key, the change may be lost if a - // backup is restored, if the backup doesn't have the new private key for the change. - // If we reused the old key, it would be possible to add code to look for and - // rediscover unknown transactions that were written with keys of ours to recover - // post-backup change. + //over pay for denominated transactions + if (nCoinType == ONLY_DENOMINATED) { + nFeeRet += nChange; + wtxNew.mapValue["DS"] = "1"; + // recheck skipped denominations during next mixing + darkSendPool.ClearSkippedDenominations(); + } else { + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange = GetScriptForDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey; + bool ret; + ret = reservekey.GetReservedKey(vchPubKey); + if (!ret) { + strFailReason = _("Keypool ran out, please call keypoolrefill first"); + return false; + } - // Reserve a new key pair from key pool - CPubKey vchPubKey; - bool ret; - ret = reservekey.GetReservedKey(vchPubKey); - if (!ret) { - strFailReason = _("Keypool ran out, please call keypoolrefill first"); - return false; + scriptChange = GetScriptForDestination(vchPubKey.GetID()); } - scriptChange = GetScriptForDestination(vchPubKey.GetID()); - } - - CTxOut newTxOut(nChange, scriptChange); - - // We do not move dust-change to fees, because the sender would end up paying more than requested. - // This would be against the purpose of the all-inclusive feature. - // So instead we raise the change and deduct from the recipient. - if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(::minRelayTxFee)) { - CAmount nDust = newTxOut.GetDustThreshold(::minRelayTxFee) - newTxOut.nValue; - newTxOut.nValue += nDust; // raise change until no more dust - for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient - { - if (vecSend[i].fSubtractFeeFromAmount) { - txNew.vout[i].nValue -= nDust; - if (txNew.vout[i].IsDust(::minRelayTxFee)) { - strFailReason = _( - "The transaction amount is too small to send after the fee has been deducted"); - return false; + CTxOut newTxOut(nChange, scriptChange); + + // We do not move dust-change to fees, because the sender would end up paying more than requested. + // This would be against the purpose of the all-inclusive feature. + // So instead we raise the change and deduct from the recipient. + if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(::minRelayTxFee)) { + CAmount nDust = newTxOut.GetDustThreshold(::minRelayTxFee) - newTxOut.nValue; + newTxOut.nValue += nDust; // raise change until no more dust + for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient + { + if (vecSend[i].fSubtractFeeFromAmount) { + txNew.vout[i].nValue -= nDust; + if (txNew.vout[i].IsDust(::minRelayTxFee)) { + strFailReason = _( + "The transaction amount is too small to send after the fee has been deducted"); + return false; + } + break; } - break; } } - } - // Never create dust outputs; if we would, just - // add the dust to the fee. - if (newTxOut.IsDust(::minRelayTxFee)) { - nChangePosInOut = -1; - nFeeRet += nChange; - reservekey.ReturnKey(); - } else { - if (nChangePosInOut == -1) { - // Insert change txn at random position: - nChangePosInOut = GetRandInt(txNew.vout.size() + 1); - } else if ((unsigned int) nChangePosInOut > txNew.vout.size()) { - strFailReason = _("Change index out of range"); - return false; - } + // Never create dust outputs; if we would, just + // add the dust to the fee. + if (newTxOut.IsDust(::minRelayTxFee)) { + nChangePosInOut = -1; + nFeeRet += nChange; + reservekey.ReturnKey(); + } else { + if (nChangePosInOut == -1) { + // Insert change txn at random position: + nChangePosInOut = GetRandInt(txNew.vout.size() + 1); + } else if ((unsigned int) nChangePosInOut > txNew.vout.size()) { + strFailReason = _("Change index out of range"); + return false; + } - vector::iterator position = txNew.vout.begin() + nChangePosInOut; - txNew.vout.insert(position, newTxOut); + vector::iterator position = txNew.vout.begin() + nChangePosInOut; + txNew.vout.insert(position, newTxOut); + } } } else reservekey.ReturnKey(); @@ -2884,9 +2999,9 @@ bool CWallet::CreateTransaction(const vector &vecSend, CWalletTx &w const CScript &scriptPubKey = coin.first->vout[coin.second].scriptPubKey; SignatureData sigdata; if (sign) - signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, - coin.first->vout[coin.second].nValue, - SIGHASH_ALL), scriptPubKey, sigdata); + signSuccess = ProduceSignature( + TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->vout[coin.second].nValue, SIGHASH_ALL), + scriptPubKey, sigdata); else signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata); @@ -2905,7 +3020,7 @@ bool CWallet::CreateTransaction(const vector &vecSend, CWalletTx &w // Remove scriptSigs if we used dummy signatures for fee calculation if (!sign) { BOOST_FOREACH(CTxIn & vin, txNew.vin) - vin.scriptSig = CScript(); + vin.scriptSig = CScript(); txNew.wit.SetNull(); } @@ -2933,6 +3048,9 @@ bool CWallet::CreateTransaction(const vector &vecSend, CWalletTx &w fAllowFree = true;// Allow free for send int64_t nMinFee = wtxNew.GetMinFee(1, fAllowFree, GMF_SEND); int64_t nFeeNeeded = nPayFee; + if (fUseInstantSend) { + nFeeNeeded = std::max(nFeeNeeded, CTxLockRequest(txNew).GetMinFee()); + } if (nFeeNeeded < nMinFee) { nFeeNeeded = nMinFee; } @@ -3010,8 +3128,7 @@ bool CWallet::CommitTransaction(CWalletTx &wtxNew, CReserveKey &reservekey) { // Notify that old coins are spent set < CWalletTx * > setCoins; - BOOST_FOREACH( - const CTxIn &txin, wtxNew.vin) + BOOST_FOREACH(const CTxIn &txin, wtxNew.vin) { CWalletTx &coin = mapWallet[txin.prevout.hash]; coin.BindWallet(this); @@ -3041,8 +3158,7 @@ bool CWallet::CommitTransaction(CWalletTx &wtxNew, CReserveKey &reservekey) { return true; } -bool CWallet::EraseFromWallet(uint256 hash) -{ +bool CWallet::EraseFromWallet(uint256 hash) { if (!fFileBacked) return false; { @@ -3098,6 +3214,43 @@ bool CWallet::CreateZerocoinMintModel(string &stringError, string denomAmount) { // stored in a secure location (wallet) at the client. libzerocoin::PrivateCoin newCoin(ZCParams, denomination); + std::list listPubCoin = std::list(); + CWalletDB walletdb(strWalletFile); + walletdb.ListPubCoin(listPubCoin); + + int currentId = 1; + unsigned int countExistingItems = 0; + + BOOST_FOREACH(const CZerocoinEntry &pubCoinIdItem, listPubCoin) { + //LogPrintf("denomination = %d, id = %d, height = %d\n", pubCoinIdItem.denomination, pubCoinIdItem.id, pubCoinIdItem.nHeight); + if (pubCoinIdItem.id > 0) { + if (pubCoinIdItem.nHeight <= chainActive.Height()) { + if (pubCoinIdItem.denomination == denomination) { + countExistingItems++; + if (pubCoinIdItem.id > currentId) { + currentId = pubCoinIdItem.id; + countExistingItems = 1; + } + } + } else { + break; + } + } + } + + if (countExistingItems > 9) { + currentId++; + } + + if (((denomination == libzerocoin::ZQ_LOVELACE) && (currentId >= ZC_V2_SWITCH_ID_1)) + || ((denomination == libzerocoin::ZQ_GOLDWASSER) && (currentId >= ZC_V2_SWITCH_ID_10)) + || ((denomination == libzerocoin::ZQ_RACKOFF) && (currentId >= ZC_V2_SWITCH_ID_25)) + || ((denomination == libzerocoin::ZQ_PEDERSEN) && (currentId >= ZC_V2_SWITCH_ID_50)) + || ((denomination == libzerocoin::ZQ_WILLIAMSON) && (currentId >= ZC_V2_SWITCH_ID_100))) { + newCoin.setVersion(2); + } + + // Get a copy of the 'public' portion of the coin. You should // embed this into a Zerocoin 'MINT' transaction along with a series // of currency inputs totaling the assigned value of one zerocoin. @@ -3106,7 +3259,8 @@ bool CWallet::CreateZerocoinMintModel(string &stringError, string denomAmount) { // Validate if (pubCoin.validate()) { //TODOS - CScript scriptSerializedCoin = CScript() << OP_ZEROCOINMINT << pubCoin.getValue().getvch().size() << pubCoin.getValue().getvch(); + CScript scriptSerializedCoin = + CScript() << OP_ZEROCOINMINT << pubCoin.getValue().getvch().size() << pubCoin.getValue().getvch(); // Wallet comments CWalletTx wtx; @@ -3138,29 +3292,29 @@ bool CWallet::CreateZerocoinMintModel(string &stringError, string denomAmount) { /** * btzc: CreateZerocoinSpendModel */ -bool CWallet::CreateZerocoinSpendModel(string &stringError, string denomAmount){ - if(!fFileBacked) +bool CWallet::CreateZerocoinSpendModel(string &stringError, string denomAmount) { + if (!fFileBacked) return false; int64_t nAmount = 0; libzerocoin::CoinDenomination denomination; // Amount - if(denomAmount == "1"){ + if (denomAmount == "1") { denomination = libzerocoin::ZQ_LOVELACE; nAmount = roundint64(1 * COIN); - }else if(denomAmount == "10"){ + } else if (denomAmount == "10") { denomination = libzerocoin::ZQ_GOLDWASSER; nAmount = roundint64(10 * COIN); - }else if(denomAmount == "25"){ + } else if (denomAmount == "25") { denomination = libzerocoin::ZQ_RACKOFF; nAmount = roundint64(25 * COIN); - }else if(denomAmount == "50"){ + } else if (denomAmount == "50") { denomination = libzerocoin::ZQ_PEDERSEN; nAmount = roundint64(50 * COIN); - }else if(denomAmount == "100"){ + } else if (denomAmount == "100") { denomination = libzerocoin::ZQ_WILLIAMSON; nAmount = roundint64(100 * COIN); - }else{ + } else { return false; } @@ -3191,8 +3345,10 @@ bool CWallet::CreateZerocoinSpendModel(string &stringError, string denomAmount){ * @param coinControl * @return */ -bool CWallet::CreateZerocoinMintTransaction(const vector &vecSend, CWalletTx &wtxNew, CReserveKey &reservekey, - CAmount &nFeeRet, int &nChangePosInOut, std::string &strFailReason, const CCoinControl *coinControl, bool sign) { +bool CWallet::CreateZerocoinMintTransaction(const vector &vecSend, CWalletTx &wtxNew, + CReserveKey &reservekey, + CAmount &nFeeRet, int &nChangePosInOut, std::string &strFailReason, + const CCoinControl *coinControl, bool sign) { CAmount nValue = 0; int nChangePosRequest = nChangePosInOut; unsigned int nSubtractFeeFromAmount = 0; @@ -3294,8 +3450,7 @@ bool CWallet::CreateZerocoinMintTransaction(const vector &vecSend, CAmount nChange = nValueIn - nValueToSelect; // NOTE: this depends on the exact behaviour of GetMinFee - if (nFeeRet < CTransaction::nMinTxFee && nChange > 0 && nChange < CENT) - { + if (nFeeRet < CTransaction::nMinTxFee && nChange > 0 && nChange < CENT) { int64_t nMoveToFee = min(nChange, CTransaction::nMinTxFee - nFeeRet); nChange -= nMoveToFee; nFeeRet += nMoveToFee; @@ -3307,14 +3462,13 @@ bool CWallet::CreateZerocoinMintTransaction(const vector &vecSend, CScript scriptChange; // coin control: send change to custom address - if (coinControl && !boost::get(&coinControl->destChange)) - { + if (coinControl && !boost::get(&coinControl->destChange)) { scriptChange = GetScriptForDestination(coinControl->destChange); } // send change to one of the specified change addresses - else if (mapArgs.count("-change") && mapMultiArgs["-change"].size() > 0) - { - CBitcoinAddress address(mapMultiArgs["-change"][GetRandInt(mapMultiArgs["-change"].size())]); + else if (mapArgs.count("-change") && mapMultiArgs["-change"].size() > 0) { + CBitcoinAddress + address(mapMultiArgs["-change"][GetRandInt(mapMultiArgs["-change"].size())]); CKeyID keyID; if (!address.GetKeyID(keyID)) { strFailReason = _("Bad change address"); @@ -3388,14 +3542,14 @@ bool CWallet::CreateZerocoinMintTransaction(const vector &vecSend, // // Note how the sequence number is set to max()-1 so that the // nLockTime set above actually works. - BOOST_FOREACH(const PAIRTYPE(const CWalletTx *, unsigned int) & coin, setCoins) - txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), + BOOST_FOREACH(const PAIRTYPE(const CWalletTx *, unsigned int) &coin, setCoins) + txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second, CScript(), std::numeric_limits < unsigned int > ::max() - 1)); // Sign int nIn = 0; CTransaction txNewConst(txNew); - BOOST_FOREACH(const PAIRTYPE(const CWalletTx *, unsigned int) & coin, setCoins) + BOOST_FOREACH(const PAIRTYPE(const CWalletTx *, unsigned int) &coin, setCoins) { bool signSuccess; const CScript &scriptPubKey = coin.first->vout[coin.second].scriptPubKey; @@ -3403,7 +3557,8 @@ bool CWallet::CreateZerocoinMintTransaction(const vector &vecSend, if (sign) signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->vout[coin.second].nValue, - SIGHASH_ALL), scriptPubKey, sigdata); + SIGHASH_ALL), scriptPubKey, + sigdata); else signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata); @@ -3492,13 +3647,19 @@ bool CWallet::CreateZerocoinMintTransaction(const vector &vecSend, } return true; } -bool CWallet::CreateZerocoinMintTransaction(CScript pubCoin, int64_t nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl){ + +bool +CWallet::CreateZerocoinMintTransaction(CScript pubCoin, int64_t nValue, CWalletTx &wtxNew, CReserveKey &reservekey, + int64_t &nFeeRet, std::string &strFailReason, + const CCoinControl *coinControl) { vector vecSend; CRecipient recipient = {pubCoin, nValue, false}; vecSend.push_back(recipient); int nChangePosRet = -1; - return CreateZerocoinMintTransaction(vecSend, wtxNew, reservekey, nFeeRet, nChangePosRet, strFailReason, coinControl); + return CreateZerocoinMintTransaction(vecSend, wtxNew, reservekey, nFeeRet, nChangePosRet, strFailReason, + coinControl); } + /** * btzc: * @brief CWallet::CreateZerocoinSpendTransaction @@ -3514,19 +3675,19 @@ bool CWallet::CreateZerocoinMintTransaction(CScript pubCoin, int64_t nValue, CWa * @return */ bool CWallet::CreateZerocoinSpendTransaction(int64_t nValue, libzerocoin::CoinDenomination denomination, - CWalletTx& wtxNew, CReserveKey& reservekey, CBigNum& coinSerial, uint256& txHash, CBigNum& zcSelectedValue, bool& zcSelectedIsUsed, std::string& strFailReason) -{ - if (nValue < 0) - { - strFailReason = _("Transaction amounts must be positive"); - return false; + CWalletTx &wtxNew, CReserveKey &reservekey, CBigNum &coinSerial, + uint256 &txHash, CBigNum &zcSelectedValue, bool &zcSelectedIsUsed, + std::string &strFailReason) { + if (nValue < 0) { + strFailReason = _("Transaction amounts must be positive"); + return false; } wtxNew.BindWallet(this); CMutableTransaction txNew; txNew.nLockTime = chainActive.Height(); - if (GetRandInt(10) == 0) - txNew.nLockTime = std::max(0, (int) txNew.nLockTime - GetRandInt(100)); + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int) txNew.nLockTime - GetRandInt(100)); { LOCK2(cs_main, cs_wallet); { @@ -3544,7 +3705,7 @@ bool CWallet::CreateZerocoinSpendTransaction(int64_t nValue, libzerocoin::CoinDe CTxOut newTxOut(nValue, scriptChange); // Insert change txn at random position: - vector::iterator position = txNew.vout.begin() + GetRandInt(txNew.vout.size()+1); + vector::iterator position = txNew.vout.begin() + GetRandInt(txNew.vout.size() + 1); txNew.vout.insert(position, newTxOut); // LogPrintf("txNew:%s\n", txNew.ToString()); LogPrintf("txNew.GetHash():%s\n", txNew.GetHash().ToString()); @@ -3568,11 +3729,11 @@ bool CWallet::CreateZerocoinSpendTransaction(int64_t nValue, libzerocoin::CoinDe // 2. Get pubcoin from the private coin // 3. Compute Accomulator by you self by getting pubcoins value from wallet, but it must not include the public coin of the private that we select // 4. Generate withness with follwing stmt - // libzerocoin::AccumulatorWitness witness(params, accumulator, newCoin.getPublicCoin()); - // Add the public half of "newCoin" to the Accumulator itself. - // accumulator += newCoin.getPublicCoin(); + // libzerocoin::AccumulatorWitness witness(params, accumulator, newCoin.getPublicCoin()); + // Add the public half of "newCoin" to the Accumulator itself. + // accumulator += newCoin.getPublicCoin(); // 1. Selection a private coin that doesn't be used in wallet - list listPubCoin; + list listPubCoin; CWalletDB(strWalletFile).ListPubCoin(listPubCoin); listPubCoin.sort(CompHeight); CZerocoinEntry zerocoinSelected; @@ -3580,43 +3741,44 @@ bool CWallet::CreateZerocoinSpendTransaction(int64_t nValue, libzerocoin::CoinDe // GET MIN ID int currentId = INT_MAX; - BOOST_FOREACH(const CZerocoinEntry& minIdPubcoin, listPubCoin) { + BOOST_FOREACH(const CZerocoinEntry &minIdPubcoin, listPubCoin) { if (minIdPubcoin.id < currentId - && minIdPubcoin.denomination == denomination - && minIdPubcoin.IsUsed == false - && minIdPubcoin.randomness != 0 - && minIdPubcoin.serialNumber != 0 - && minIdPubcoin.id != -1 - && minIdPubcoin.nHeight != -1 - && minIdPubcoin.nHeight != INT_MAX - && minIdPubcoin.nHeight >= 1 - && chainActive.Height() > -1 - && minIdPubcoin.nHeight + 6 <= chainActive.Height() + && minIdPubcoin.denomination == denomination + && minIdPubcoin.IsUsed == false + && minIdPubcoin.randomness != 0 + && minIdPubcoin.serialNumber != 0 + && minIdPubcoin.id != -1 + && minIdPubcoin.nHeight != -1 + && minIdPubcoin.nHeight != INT_MAX + && minIdPubcoin.nHeight >= 1 + && chainActive.Height() > -1 + && minIdPubcoin.nHeight + 6 <= chainActive.Height() ) { currentId = minIdPubcoin.id; } } - BOOST_FOREACH(const CZerocoinEntry& zerocoinItem, listPubCoin){ - if(zerocoinItem.IsUsed == false - && zerocoinItem.denomination == denomination - && zerocoinItem.randomness != 0 - && zerocoinItem.serialNumber != 0 - && zerocoinItem.id == currentId - && zerocoinItem.nHeight != -1 - && zerocoinItem.nHeight != INT_MAX - && zerocoinItem.nHeight >= 1 - && chainActive.Height() > -1 - && zerocoinItem.nHeight + 6 <= chainActive.Height() - ){ + BOOST_FOREACH(const CZerocoinEntry &zerocoinItem, listPubCoin){ + if (zerocoinItem.IsUsed == false + && zerocoinItem.denomination == denomination + && zerocoinItem.randomness != 0 + && zerocoinItem.serialNumber != 0 + && zerocoinItem.id == currentId + && zerocoinItem.nHeight != -1 + && zerocoinItem.nHeight != INT_MAX + && zerocoinItem.nHeight >= 1 + && chainActive.Height() > -1 + && zerocoinItem.nHeight + 6 <= chainActive.Height() + ) { zerocoinSelected = zerocoinItem; selectedPubcoin = true; break; } } - if(!selectedPubcoin){ - strFailReason = _("it has to have at least two mint coins with at least 7 confirmation in order to spend a coin"); + if (!selectedPubcoin) { + strFailReason = _( + "it has to have at least two mint coins with at least 6 confirmation in order to spend a coin"); return false; } @@ -3633,26 +3795,28 @@ bool CWallet::CreateZerocoinSpendTransaction(int64_t nValue, libzerocoin::CoinDe } LogPrintf("listPubcoin.size()=%s\n", listPubCoin.size()); - LogPrintf("Consider: zerocoinSelected.value=%s\n, zerocoinSelected.id=%s\n",zerocoinSelected.value.ToString(), zerocoinSelected.id); + LogPrintf("Consider: zerocoinSelected.value=%s\n, zerocoinSelected.id=%s\n", + zerocoinSelected.value.ToString(), zerocoinSelected.id); // 3. Compute Accomulator by yourself by getting at least 9 pubcoins from wallet, but it must not include the public coin of the selected private coin int countUseablePubcoin = 0; - BOOST_FOREACH(const CZerocoinEntry& zerocoinItem, listPubCoin){ + BOOST_FOREACH(const CZerocoinEntry &zerocoinItem, listPubCoin){ // Count pubcoins in same block - if(zerocoinItem.value != zerocoinSelected.value - && zerocoinItem.id == zerocoinSelected.id - && chainActive.Height() > -1 - && zerocoinItem.nHeight + 6 < chainActive.Height() - && zerocoinItem.nHeight >= 1 - && zerocoinItem.nHeight != INT_MAX - && zerocoinItem.denomination == denomination - && zerocoinItem.nHeight != -1){ + if (zerocoinItem.value != zerocoinSelected.value + && zerocoinItem.id == zerocoinSelected.id + && chainActive.Height() > -1 + && zerocoinItem.nHeight + 6 <= chainActive.Height() + && zerocoinItem.nHeight >= 1 + && zerocoinItem.nHeight != INT_MAX + && zerocoinItem.denomination == denomination + && zerocoinItem.nHeight != -1) { LogPrintf("VALID COIN - PUBCOIN ID: %d HEIGHT: %d\n", zerocoinItem.id, zerocoinItem.nHeight); libzerocoin::PublicCoin pubCoinTemp(ZCParams, zerocoinItem.value, denomination); - if(pubCoinTemp.validate()){ + if (pubCoinTemp.validate()) { countUseablePubcoin++; //if(countUseablePubcoin == 9) break; - LogPrintf("USEABLE - COIN NO: %d PUBCOIN ID: %d HEIGHT: %d\n", countUseablePubcoin, zerocoinItem.id, zerocoinItem.nHeight); + LogPrintf("USEABLE - COIN NO: %d PUBCOIN ID: %d HEIGHT: %d\n", countUseablePubcoin, + zerocoinItem.id, zerocoinItem.nHeight); accumulator += pubCoinTemp; } } @@ -3660,30 +3824,48 @@ bool CWallet::CreateZerocoinSpendTransaction(int64_t nValue, libzerocoin::CoinDe LogPrintf("USEABLE PUBCOINS: %d\n", countUseablePubcoin); - if(countUseablePubcoin < 1){ // You have to have at least two mint zerocoins. + if (countUseablePubcoin < 1) { // You have to have at least two mint zerocoins. strFailReason = _("at least two mint coins with are using calculating accumulator"); return false; } // 4. Generate withness with follwing stmt - // libzerocoin::AccumulatorWitness witness(params, accumulator, newCoin.getPublicCoin()); - // Add the public half of "newCoin" to the Accumulator itself. - // accumulator += newCoin.getPublicCoin(); + // libzerocoin::AccumulatorWitness witness(params, accumulator, newCoin.getPublicCoin()); + // Add the public half of "newCoin" to the Accumulator itself. + // accumulator += newCoin.getPublicCoin(); libzerocoin::AccumulatorWitness witness(ZCParams, accumulator, pubCoinSelected); accumulator += pubCoinSelected; /* - // At this point we should generate a ZEROCOIN_SPEND transaction to - // send to the network. This network should include a set of outputs - // totalling to the value of one zerocoin (minus transaction fees). - // - // The format of this transaction is up to the implementer. Here we'll - // assume you've formatted this transaction and placed the hash into - // "transactionHash". We'll also assume "accumulatorHash" contains the - // hash of the last block whose transactions are in the accumulator.*/ - arith_uint256 transactionHash = 0; + // At this point we should generate a ZEROCOIN_SPEND transaction to + // send to the network. This network should include a set of outputs + // totalling to the value of one zerocoin (minus transaction fees). + // + // The format of this transaction is up to the implementer. Here we'll + // assume you've formatted this transaction and placed the hash into + // "transactionHash". We'll also assume "accumulatorHash" contains the + // hash of the last block whose transactions are in the accumulator.*/ + uint256 transactionHash = ArithToUint256(0); arith_uint256 accumulatorID = 0; + CTxIn newTxIn; + newTxIn.nSequence = zerocoinSelected.id; + newTxIn.scriptSig = CScript(); + newTxIn.prevout.SetNull(); + LogPrintf("txNew.vin.size(): %d\n", txNew.vin.size()); + txNew.vin.push_back(newTxIn); + LogPrintf("txNew.vin.size(): %d\n", txNew.vin.size()); + + if (((denomination == libzerocoin::ZQ_LOVELACE) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_1)) + || ((denomination == libzerocoin::ZQ_GOLDWASSER) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_10)) + || ((denomination == libzerocoin::ZQ_RACKOFF) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_25)) + || ((denomination == libzerocoin::ZQ_PEDERSEN) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_50)) + || ((denomination == libzerocoin::ZQ_WILLIAMSON) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_100))) { + transactionHash = wtxNew.GetNormalizedHash(); + accumulatorID = zerocoinSelected.id; + } + + // Place "transactionHash" and "accumulatorBlockHash" into a new // SpendMetaData object. libzerocoin::SpendMetaData metaData(accumulatorID, transactionHash); @@ -3691,15 +3873,29 @@ bool CWallet::CreateZerocoinSpendTransaction(int64_t nValue, libzerocoin::CoinDe // Construct the CoinSpend object. This acts like a signature on the // transaction. libzerocoin::PrivateCoin privateCoin(ZCParams, denomination); + if (((denomination == libzerocoin::ZQ_LOVELACE) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_1)) + || ((denomination == libzerocoin::ZQ_GOLDWASSER) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_10)) + || ((denomination == libzerocoin::ZQ_RACKOFF) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_25)) + || ((denomination == libzerocoin::ZQ_PEDERSEN) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_50)) + || ((denomination == libzerocoin::ZQ_WILLIAMSON) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_100))) { + privateCoin.setVersion(2); + } privateCoin.setPublicCoin(pubCoinSelected); privateCoin.setRandomness(zerocoinSelected.randomness); privateCoin.setSerialNumber(zerocoinSelected.serialNumber); libzerocoin::CoinSpend spend(ZCParams, privateCoin, accumulator, witness, metaData); + if (((denomination == libzerocoin::ZQ_LOVELACE) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_1)) + || ((denomination == libzerocoin::ZQ_GOLDWASSER) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_10)) + || ((denomination == libzerocoin::ZQ_RACKOFF) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_25)) + || ((denomination == libzerocoin::ZQ_PEDERSEN) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_50)) + || ((denomination == libzerocoin::ZQ_WILLIAMSON) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_100))) { + spend.setVersion(2); + } // This is a sanity check. The CoinSpend object should always verify, // but why not check before we put it onto the wire? if (!spend.Verify(accumulator, metaData)) { - strFailReason = _("the new spend coin transaction did not verify"); + strFailReason = _("the spend coin transaction did not verify"); return false; } @@ -3724,6 +3920,13 @@ bool CWallet::CreateZerocoinSpendTransaction(int64_t nValue, libzerocoin::CoinDe // Deserialize the CoinSpend intro a fresh object libzerocoin::CoinSpend newSpend(ZCParams, serializedCoinSpend); + if (((denomination == libzerocoin::ZQ_LOVELACE) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_1)) + || ((denomination == libzerocoin::ZQ_GOLDWASSER) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_10)) + || ((denomination == libzerocoin::ZQ_RACKOFF) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_25)) + || ((denomination == libzerocoin::ZQ_PEDERSEN) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_50)) + || ((denomination == libzerocoin::ZQ_WILLIAMSON) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_100))) { + newSpend.setVersion(2); + } // Create a new metadata object to contain the hash of the received // ZEROCOIN_SPEND transaction. If we were a real client we'd actually @@ -3737,29 +3940,35 @@ bool CWallet::CreateZerocoinSpendTransaction(int64_t nValue, libzerocoin::CoinDe // Verify that the spend is valid with respect to the Accumulator // and the Metadata if (!newSpend.Verify(accumulator, newMetadata)) { - strFailReason = _("the new spend coin transaction did not verify"); + strFailReason = _("the newSpend coin transaction did not verify"); return false; } std::vector > data; data = serializedCoinSpend2.vch; - CTxIn newTxIn; - newTxIn.nSequence = zerocoinSelected.id; - newTxIn.scriptSig = CScript() << OP_ZEROCOINSPEND << data.size(); - newTxIn.scriptSig.insert(newTxIn.scriptSig.end(), data.begin(), data.end()); + CScript tmp = CScript() << OP_ZEROCOINSPEND << data.size(); + tmp.insert(tmp.end(), data.begin(), data.end()); + txNew.vin[0].scriptSig.clear(); + txNew.vin[0].scriptSig.insert(txNew.vin[0].scriptSig.begin(), tmp.begin(), tmp.end()); - newTxIn.prevout.SetNull(); - txNew.vin.push_back(newTxIn); std::vector > dataTxIn; - dataTxIn.insert(dataTxIn.end(), newTxIn.scriptSig.begin() + 4, newTxIn.scriptSig.end()); + dataTxIn.insert(dataTxIn.end(), tmp.begin() + 4, tmp.end()); CDataStream serializedCoinSpendChecking(SER_NETWORK, PROTOCOL_VERSION); serializedCoinSpendChecking.vch = dataTxIn; // Deserialize the CoinSpend intro a fresh object libzerocoin::CoinSpend newSpendChecking(ZCParams, serializedCoinSpendChecking); + if (((denomination == libzerocoin::ZQ_LOVELACE) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_1)) + || ((denomination == libzerocoin::ZQ_GOLDWASSER) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_10)) + || ((denomination == libzerocoin::ZQ_RACKOFF) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_25)) + || ((denomination == libzerocoin::ZQ_PEDERSEN) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_50)) + || ((denomination == libzerocoin::ZQ_WILLIAMSON) && (zerocoinSelected.id >= ZC_V2_SWITCH_ID_100))) { + newSpendChecking.setVersion(2); + } + if (!newSpendChecking.Verify(accumulator, newMetadata)) { strFailReason = _("the transaction did not verify"); return false; @@ -3776,14 +3985,14 @@ bool CWallet::CreateZerocoinSpendTransaction(int64_t nValue, libzerocoin::CoinDe } /*zerocoinSelected.IsUsed = true; - zerocoinSelected.randomness = 0; - zerocoinSelected.serialNumber = 0; - CWalletDB(strWalletFile).WriteZerocoinEntry(zerocoinSelected);*/ + zerocoinSelected.randomness = 0; + zerocoinSelected.serialNumber = 0; + CWalletDB(strWalletFile).WriteZerocoinEntry(zerocoinSelected);*/ - std::list listCoinSpendSerial; + std::list listCoinSpendSerial; CWalletDB(strWalletFile).ListCoinSpendSerial(listCoinSpendSerial); - BOOST_FOREACH(const CZerocoinSpendEntry& item, listCoinSpendSerial){ - if(spend.getCoinSerialNumber() == item.coinSerial){ + BOOST_FOREACH(const CZerocoinSpendEntry &item, listCoinSpendSerial){ + if (spend.getCoinSerialNumber() == item.coinSerial) { // THIS SELECEDTED COIN HAS BEEN USED, SO UPDATE ITS STATUS CZerocoinEntry pubCoinTx; pubCoinTx.nHeight = zerocoinSelected.nHeight; @@ -3796,7 +4005,8 @@ bool CWallet::CreateZerocoinSpendTransaction(int64_t nValue, libzerocoin::CoinDe CWalletDB(strWalletFile).WriteZerocoinEntry(pubCoinTx); LogPrintf("CreateZerocoinSpendTransaction() -> NotifyZerocoinChanged\n"); LogPrintf("pubcoin=%s, isUsed=Used\n", zerocoinSelected.value.GetHex()); - pwalletMain->NotifyZerocoinChanged(pwalletMain, zerocoinSelected.value.GetHex(), "Used", CT_UPDATED); + pwalletMain->NotifyZerocoinChanged(pwalletMain, zerocoinSelected.value.GetHex(), "Used", + CT_UPDATED); strFailReason = _("the coin spend has been used"); return false; } @@ -3815,7 +4025,7 @@ bool CWallet::CreateZerocoinSpendTransaction(int64_t nValue, libzerocoin::CoinDe entry.id = zerocoinSelected.id; entry.denomination = zerocoinSelected.denomination; LogPrintf("WriteCoinSpendSerialEntry, serialNumber=%s\n", coinSerial.ToString()); - if(!CWalletDB(strWalletFile).WriteCoinSpendSerialEntry(entry)){ + if (!CWalletDB(strWalletFile).WriteCoinSpendSerialEntry(entry)) { strFailReason = _("it cannot write coin serial number into wallet"); } } @@ -3824,8 +4034,7 @@ bool CWallet::CreateZerocoinSpendTransaction(int64_t nValue, libzerocoin::CoinDe return true; } -bool CWallet::CommitZerocoinSpendTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) -{ +bool CWallet::CommitZerocoinSpendTransaction(CWalletTx &wtxNew, CReserveKey &reservekey) { { LOCK2(cs_main, cs_wallet); LogPrintf("CommitZerocoinSpendTransaction:\n%s", wtxNew.ToString()); @@ -3834,7 +4043,7 @@ bool CWallet::CommitZerocoinSpendTransaction(CWalletTx& wtxNew, CReserveKey& res // This is only to keep the database open to defeat the auto-flush for the // duration of this scope. This is the only place where this optimization // maybe makes sense; please don't do it anywhere else. - CWalletDB* pwalletdb = fFileBacked ? new CWalletDB(strWalletFile, "w") : NULL; + CWalletDB *pwalletdb = fFileBacked ? new CWalletDB(strWalletFile, "w") : NULL; // Take key pair from key pool so it won't be used again reservekey.KeepKey(); @@ -3854,7 +4063,8 @@ bool CWallet::CommitZerocoinSpendTransaction(CWalletTx& wtxNew, CReserveKey& res CValidationState state; // Broadcast if (!wtxNew.AcceptToMemoryPool(false, maxTxFee, state, false)) { - LogPrintf("CommitZerocoinSpendTransaction(): Transaction cannot be broadcast immediately, %s\n", state.GetRejectReason()); + LogPrintf("CommitZerocoinSpendTransaction(): Transaction cannot be broadcast immediately, %s\n", + state.GetRejectReason()); // TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure. } else { wtxNew.RelayWalletTransaction(false); @@ -3863,6 +4073,7 @@ bool CWallet::CommitZerocoinSpendTransaction(CWalletTx& wtxNew, CReserveKey& res } return true; } + /** * btzc: * @brief CWallet::MintZerocoin @@ -3872,41 +4083,42 @@ bool CWallet::CommitZerocoinSpendTransaction(CWalletTx& wtxNew, CReserveKey& res * @param fAskFee * @return */ -string CWallet::MintZerocoin(CScript pubCoin, int64_t nValue, CWalletTx& wtxNew, bool fAskFee) -{ +string CWallet::MintZerocoin(CScript pubCoin, int64_t nValue, CWalletTx &wtxNew, bool fAskFee) { LogPrintf("MintZerocoin: value = %s\n", nValue); // Check amount if (nValue <= 0) return _("Invalid amount"); - LogPrintf("CWallet.MintZerocoin() nValue = %s, payTxFee.GetFee(1000) = %s, GetBalance() = %s \n", nValue, payTxFee.GetFee(1000), GetBalance()); + LogPrintf("CWallet.MintZerocoin() nValue = %s, payTxFee.GetFee(1000) = %s, GetBalance() = %s \n", nValue, + payTxFee.GetFee(1000), GetBalance()); if (nValue + payTxFee.GetFeePerK() > GetBalance()) return _("Insufficient funds"); LogPrintf("payTxFee.GetFeePerK()=%s\n", payTxFee.GetFeePerK()); CReserveKey reservekey(this); int64_t nFeeRequired; - if (IsLocked()) - { + if (IsLocked()) { string strError = _("Error: Wallet locked, unable to create transaction!"); LogPrintf("MintZerocoin() : %s", strError); return strError; } string strError; - if (!CreateZerocoinMintTransaction(pubCoin, nValue, wtxNew, reservekey, nFeeRequired, strError)) - { + if (!CreateZerocoinMintTransaction(pubCoin, nValue, wtxNew, reservekey, nFeeRequired, strError)) { LogPrintf("nFeeRequired=%s\n", nFeeRequired); if (nValue + nFeeRequired > GetBalance()) - return 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).c_str()); + return 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).c_str()); return strError; } if (fAskFee && !uiInterface.ThreadSafeAskFee(nFeeRequired)) return "ABORTED"; - if (!CommitTransaction(wtxNew, reservekey)){ - return _("Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); - }else{ + if (!CommitTransaction(wtxNew, reservekey)) { + return _( + "Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); + } else { LogPrintf("CommitTransaction success!\n"); // //TODO : // // 1. In this case, we already have pubcoin that just committed to network. @@ -3929,37 +4141,38 @@ string CWallet::MintZerocoin(CScript pubCoin, int64_t nValue, CWalletTx& wtxNew, * @param zcSelectedIsUsed * @return */ -string CWallet::SpendZerocoin(int64_t nValue, libzerocoin::CoinDenomination denomination, CWalletTx& wtxNew, CBigNum& coinSerial, uint256& txHash, CBigNum& zcSelectedValue, bool& zcSelectedIsUsed) -{ +string CWallet::SpendZerocoin(int64_t nValue, libzerocoin::CoinDenomination denomination, CWalletTx &wtxNew, + CBigNum &coinSerial, uint256 &txHash, CBigNum &zcSelectedValue, + bool &zcSelectedIsUsed) { // Check amount if (nValue <= 0) return _("Invalid amount"); CReserveKey reservekey(this); - if (IsLocked()) - { + if (IsLocked()) { string strError = _("Error: Wallet locked, unable to create transaction!"); LogPrintf("SpendZerocoin() : %s", strError); return strError; } string strError; - if(!CreateZerocoinSpendTransaction(nValue, denomination, wtxNew, reservekey, coinSerial, txHash, zcSelectedValue, zcSelectedIsUsed, strError)){ + if (!CreateZerocoinSpendTransaction(nValue, denomination, wtxNew, reservekey, coinSerial, txHash, + zcSelectedValue, zcSelectedIsUsed, strError)) { LogPrintf("SpendZerocoin() : %s\n", strError.c_str()); return strError; } - if (!CommitZerocoinSpendTransaction(wtxNew, reservekey)){ + if (!CommitZerocoinSpendTransaction(wtxNew, reservekey)) { LogPrintf("CommitZerocoinSpendTransaction() -> FAILED!\n"); CZerocoinEntry pubCoinTx; - list listPubCoin; + list listPubCoin; listPubCoin.clear(); CWalletDB walletdb(pwalletMain->strWalletFile); walletdb.ListPubCoin(listPubCoin); - BOOST_FOREACH(const CZerocoinEntry& pubCoinItem, listPubCoin) { - if(zcSelectedValue == pubCoinItem.value){ + BOOST_FOREACH(const CZerocoinEntry &pubCoinItem, listPubCoin) { + if (zcSelectedValue == pubCoinItem.value) { pubCoinTx.id = pubCoinItem.id; pubCoinTx.IsUsed = false; // having error, so set to false, to be able to use again pubCoinTx.value = pubCoinItem.value; @@ -3977,13 +4190,15 @@ string CWallet::SpendZerocoin(int64_t nValue, libzerocoin::CoinDenomination deno entry.coinSerial = coinSerial; entry.hashTx = txHash; entry.pubCoin = zcSelectedValue; - if(!CWalletDB(strWalletFile).EraseCoinSpendSerialEntry(entry)){ + if (!CWalletDB(strWalletFile).EraseCoinSpendSerialEntry(entry)) { return _("Error: It cannot delete coin serial number in wallet"); } - return _("Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); + return _( + "Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); } return ""; } + /** btzc: * @brief CompHeight * @param a @@ -4109,7 +4324,8 @@ bool CWallet::SetAddressBook(const CTxDestination &address, const string &strNam strPurpose, (fUpdated ? CT_UPDATED : CT_NEW)); if (!fFileBacked) return false; - if (!strPurpose.empty() && !CWalletDB(strWalletFile).WritePurpose(CBitcoinAddress(address).ToString(), strPurpose)) + if (!strPurpose.empty() && + !CWalletDB(strWalletFile).WritePurpose(CBitcoinAddress(address).ToString(), strPurpose)) return false; return CWalletDB(strWalletFile).WriteName(CBitcoinAddress(address).ToString(), strName); } @@ -4121,8 +4337,7 @@ bool CWallet::DelAddressBook(const CTxDestination &address) { if (fFileBacked) { // Delete destdata tuples associated with address std::string strAddress = CBitcoinAddress(address).ToString(); - BOOST_FOREACH( - const PAIRTYPE(string, string) &item, mapAddressBook[address].destdata) + BOOST_FOREACH(const PAIRTYPE(string, string) &item, mapAddressBook[address].destdata) { CWalletDB(strWalletFile).EraseDestData(strAddress, item.first); } @@ -4155,8 +4370,7 @@ bool CWallet::NewKeyPool() { { LOCK(cs_wallet); CWalletDB walletdb(strWalletFile); - BOOST_FOREACH(int64_t - nIndex, setKeyPool) + BOOST_FOREACH(int64_t nIndex, setKeyPool) walletdb.ErasePool(nIndex); setKeyPool.clear(); @@ -4286,8 +4500,7 @@ std::map CWallet::GetAddressBalances() { { LOCK(cs_wallet); - BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) - walletEntry, mapWallet) + BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet) { CWalletTx *pcoin = &walletEntry.second; @@ -4325,16 +4538,14 @@ set > CWallet::GetAddressGroupings() { set > groupings; set grouping; - BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) - walletEntry, mapWallet) + BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet) { CWalletTx *pcoin = &walletEntry.second; if (pcoin->vin.size() > 0) { bool any_mine = false; // group all input addresses with each other - BOOST_FOREACH(CTxIn - txin, pcoin->vin) + BOOST_FOREACH(CTxIn txin, pcoin->vin) { CTxDestination address; if (!IsMine(txin)) /* If this input isn't mine, ignore it */ @@ -4347,8 +4558,7 @@ set > CWallet::GetAddressGroupings() { // group change with input addresses if (any_mine) { - BOOST_FOREACH(CTxOut - txout, pcoin->vout) + BOOST_FOREACH(CTxOut txout, pcoin->vout) if (IsChange(txout)) { CTxDestination txoutAddr; if (!ExtractDestination(txout.scriptPubKey, txoutAddr)) @@ -4382,8 +4592,7 @@ set > CWallet::GetAddressGroupings() { set < set < CTxDestination > * > hits; map < CTxDestination, set < CTxDestination > * > ::iterator it; - BOOST_FOREACH(CTxDestination - address, grouping) + BOOST_FOREACH(CTxDestination address, grouping) if ((it = setmap.find(address)) != setmap.end()) hits.insert((*it).second); @@ -4398,8 +4607,7 @@ set > CWallet::GetAddressGroupings() { uniqueGroupings.insert(merged); // update setmap - BOOST_FOREACH(CTxDestination - element, *merged) + BOOST_FOREACH(CTxDestination element, *merged) setmap[element] = merged; } @@ -4445,8 +4653,7 @@ CAmount CWallet::GetAccountBalance(CWalletDB &walletdb, const std::string &strAc std::set CWallet::GetAccountAddresses(const std::string &strAccount) const { LOCK(cs_wallet); set result; - BOOST_FOREACH( - const PAIRTYPE(CTxDestination, CAddressBookData)&item, mapAddressBook) + BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)&item, mapAddressBook) { const CTxDestination &address = item.first; const string &strName = item.second.name; @@ -4491,8 +4698,7 @@ void CWallet::GetAllReserveKeys(set &setAddress) const { CWalletDB walletdb(strWalletFile); LOCK2(cs_main, cs_wallet); - BOOST_FOREACH( - const int64_t &id, setKeyPool) + BOOST_FOREACH(const int64_t &id, setKeyPool) { CKeyPool keypool; if (!walletdb.ReadPool(id, keypool)) @@ -4572,8 +4778,7 @@ class CAffectedKeysVisitor : public boost::static_visitor { std::vector vDest; int nRequired; if (ExtractDestinations(script, type, vDest, nRequired)) { - BOOST_FOREACH( - const CTxDestination &dest, vDest) + BOOST_FOREACH(const CTxDestination &dest, vDest) boost::apply_visitor(*this, dest); } } @@ -4597,17 +4802,18 @@ void CWallet::GetKeyBirthTimes(std::map &mapKeyBirth) const { mapKeyBirth.clear(); // get birth times for keys with metadata - for (std::map::const_iterator it = mapKeyMetadata.begin(); it != mapKeyMetadata.end(); it++) + for (std::map::const_iterator it = mapKeyMetadata.begin(); + it != mapKeyMetadata.end(); it++) if (it->second.nCreateTime) mapKeyBirth[it->first] = it->second.nCreateTime; // map in which we'll infer heights of other keys - CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; // the tip can be reorganized; use a 144-block safety margin + CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - + 144)]; // the tip can be reorganized; use a 144-block safety margin std::map < CKeyID, CBlockIndex * > mapKeyFirstBlock; std::set setKeys; GetKeys(setKeys); - BOOST_FOREACH( - const CKeyID &keyid, setKeys) { + BOOST_FOREACH(const CKeyID &keyid, setKeys) { if (mapKeyBirth.count(keyid) == 0) mapKeyFirstBlock[keyid] = pindexMax; } @@ -4626,12 +4832,10 @@ void CWallet::GetKeyBirthTimes(std::map &mapKeyBirth) const { if (blit != mapBlockIndex.end() && chainActive.Contains(blit->second)) { // ... which are already in a block int nHeight = blit->second->nHeight; - BOOST_FOREACH( - const CTxOut &txout, wtx.vout) { + BOOST_FOREACH(const CTxOut &txout, wtx.vout) { // iterate over all their outputs CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey); - BOOST_FOREACH( - const CKeyID &keyid, vAffected) { + BOOST_FOREACH(const CKeyID &keyid, vAffected) { // ... and all their affected keys std::map::iterator rit = mapKeyFirstBlock.find(keyid); if (rit != mapKeyFirstBlock.end() && nHeight < rit->second->nHeight) @@ -4699,11 +4903,13 @@ std::string CWallet::GetWalletHelpString(bool showDebug) { strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"), CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK()))); strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions on startup")); - strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup")); + strUsage += HelpMessageOpt("-salvagewallet", + _("Attempt to recover private keys from a corrupt wallet on startup")); if (showDebug) strUsage += HelpMessageOpt("-sendfreetransactions", - strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), - DEFAULT_SEND_FREE_TRANSACTIONS)); + strprintf( + _("Send transactions as zero-fee transactions if possible (default: %u)"), + DEFAULT_SEND_FREE_TRANSACTIONS)); strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); @@ -4731,8 +4937,9 @@ std::string CWallet::GetWalletHelpString(bool showDebug) { strUsage += HelpMessageOpt("-dblogsize=", strprintf( "Flush wallet database activity from memory to disk log every megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE)); - strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", - DEFAULT_FLUSHWALLET)); + strUsage += HelpMessageOpt("-flushwallet", + strprintf("Run a thread to flush wallet periodically (default: %u)", + DEFAULT_FLUSHWALLET)); strUsage += HelpMessageOpt("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB)); @@ -4757,7 +4964,8 @@ void ReIndexZerocoin(std::string strWalletFile) { // RECURSIVE, SET NEW ID LogPrintf("lastCalculatedZCBlock: %s\n", lastCalculatedZCBlock); - BOOST_FOREACH(const CZerocoinEntry &pubCoinItem, listPubCoin) { + BOOST_FOREACH( + const CZerocoinEntry &pubCoinItem, listPubCoin) { if (!fReindex && pubCoinItem.nHeight < lastCalculatedZCBlock) { continue; } else { @@ -4781,12 +4989,8 @@ void ReIndexZerocoin(std::string strWalletFile) { list listPubCoin = list(); CWalletDB walletdb(strWalletFile); walletdb.ListPubCoin(listPubCoin); -// BOOST_FOREACH(const CZerocoinEntry &pubCoinItem, listPubCoin) { -// walletdb.EraseZerocoinEntry(pubCoinItem); -// } BOOST_FOREACH(const CZerocoinEntry &pubCoinItem, listPubCoin) { -// walletdb.EraseZerocoinEntry(pubCoinItem); CZerocoinEntry pubCoinTx; pubCoinTx.value = pubCoinItem.value; pubCoinTx.id = -1; @@ -4798,20 +5002,11 @@ void ReIndexZerocoin(std::string strWalletFile) { LogPrintf("- Reindex Pubcoin Id: %d Denomination: %d\n", pubCoinTx.id, pubCoinTx.denomination); walletdb.WriteZerocoinEntry(pubCoinTx); } -// LogPrintf("RecheckZerocoin\n"); -// CWalletDB walletdbNew(strWalletFile); -// listPubCoin.clear(); -// walletdbNew.ListPubCoin(listPubCoin); -// BOOST_FOREACH(const CZerocoinEntry &pubCoinItem, listPubCoin) { -// walletdb.EraseZerocoinEntry(pubCoinItem); -// LogPrintf("- Exists Id: %d Denomination: %d Height: %d\n", pubCoinItem.id, pubCoinItem.denomination, pubCoinItem.nHeight); -// } std::list listCoinSpendSerial; walletdb.ListCoinSpendSerial(listCoinSpendSerial); BOOST_FOREACH(const CZerocoinSpendEntry &item, listCoinSpendSerial) { walletdb.EraseCoinSpendSerialEntry(item); -// LogPrintf("serialNumber=%s\n", item.coinSerial.ToString()); } } else { LogPrintf("chainActive.Genesis() nHeight= %s\n", chainActive.Genesis()->nHeight); @@ -4824,8 +5019,9 @@ void ReIndexZerocoin(std::string strWalletFile) { continue; } else { // LogPrintf("PROCESS BLOCK = %d\n", pindexRecur->nHeight); - std::string blocksProcessed = "Loading wallet... " + std::to_string(pindexRecur->nHeight) + "/" + - std::to_string(mapBlockIndex.size()); + std::string blocksProcessed = + "Loading wallet... " + std::to_string(pindexRecur->nHeight) + "/" + + std::to_string(mapBlockIndex.size()); uiInterface.InitMessage(blocksProcessed); CBlock blockRecur; ReadBlockFromDisk(blockRecur, pindexRecur, Params().GetConsensus()); @@ -4851,23 +5047,23 @@ void ReIndexZerocoin(std::string strWalletFile) { int currentId = 1; unsigned int countExistingItems = 0; listPubCoinInLoop.sort(CompHeight); - BOOST_FOREACH(const CZerocoinEntry& pubCoinIdItem, listPubCoinInLoop) { - if(pubCoinIdItem.id > 0){ - if(pubCoinIdItem.nHeight <= pindexRecur->nHeight){ - if(pubCoinIdItem.denomination == pubCoinItem.denomination){ + BOOST_FOREACH(const CZerocoinEntry &pubCoinIdItem, listPubCoinInLoop) { + if (pubCoinIdItem.id > 0) { + if (pubCoinIdItem.nHeight <= pindexRecur->nHeight) { + if (pubCoinIdItem.denomination == pubCoinItem.denomination) { countExistingItems++; - if(pubCoinIdItem.id > currentId){ + if (pubCoinIdItem.id > currentId) { currentId = pubCoinIdItem.id; countExistingItems = 1; } } - }else{ + } else { break; } } } - if(countExistingItems > 9){ + if (countExistingItems > 9) { currentId++; } pubCoinTx.id = currentId; @@ -4876,7 +5072,8 @@ void ReIndexZerocoin(std::string strWalletFile) { pubCoinTx.serialNumber = pubCoinItem.serialNumber; pubCoinTx.value = pubCoinItem.value; pubCoinTx.nHeight = pindexRecur->nHeight; - LogPrintf("REORG PUBCOIN ID: %d HEIGHT: %d DENOMINATION: %d\n", pubCoinTx.id, + LogPrintf("REORG PUBCOIN ID: %d HEIGHT: %d DENOMINATION: %d\n", + pubCoinTx.id, pubCoinTx.nHeight, pubCoinItem.denomination); walletdbInLoop.WriteZerocoinEntry(pubCoinTx); } @@ -4935,7 +5132,8 @@ bool CWallet::InitLoadWallet() { return InitError(strprintf(_("Error loading %s: Wallet requires newer version of %s"), walletFile, _(PACKAGE_NAME))); else if (nLoadWalletRet == DB_NEED_REWRITE) { - return InitError(strprintf(_("Wallet needed to be rewritten: restart %s to complete"), _(PACKAGE_NAME))); + return InitError( + strprintf(_("Wallet needed to be rewritten: restart %s to complete"), _(PACKAGE_NAME))); } else return InitError(strprintf(_("Error loading %s"), walletFile)); } @@ -4980,10 +5178,12 @@ bool CWallet::InitLoadWallet() { bool useHD = GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET); if (!walletInstance->hdChain.masterKeyID.IsNull() && !useHD) return InitError( - strprintf(_("Error loading %s: You can't disable HD on a already existing HD wallet"), walletFile)); + strprintf(_("Error loading %s: You can't disable HD on a already existing HD wallet"), + walletFile)); if (walletInstance->hdChain.masterKeyID.IsNull() && useHD) - return InitError(strprintf(_("Error loading %s: You can't enable HD on a already existing non-HD wallet"), - walletFile)); + return InitError( + strprintf(_("Error loading %s: You can't enable HD on a already existing non-HD wallet"), + walletFile)); } LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart); @@ -5065,7 +5265,8 @@ bool CWallet::ParameterInteraction() { if (mapArgs.count("-fallbackfee")) { CAmount nFeePerK = 0; if (!ParseMoney(mapArgs["-fallbackfee"], nFeePerK)) - return InitError(strprintf(_("Invalid amount for -fallbackfee=: '%s'"), mapArgs["-fallbackfee"])); + return InitError( + strprintf(_("Invalid amount for -fallbackfee=: '%s'"), mapArgs["-fallbackfee"])); // if (nFeePerK > HIGH_TX_FEE_PER_KB) // InitWarning( // _("-fallbackfee is set very high! This is the transaction fee you may pay when fee estimates are not available.")); @@ -5097,10 +5298,10 @@ bool CWallet::ParameterInteraction() { } } - if (mapArgs.count("-mininput")) - { + if (mapArgs.count("-mininput")) { if (!ParseMoney(mapArgs["-mininput"], nMinimumInputValue)) - return InitError(strprintf(_("Invalid amount for -mininput=: '%s'"), mapArgs["-mininput"].c_str())); + return InitError( + strprintf(_("Invalid amount for -mininput=: '%s'"), mapArgs["-mininput"].c_str())); } nTxConfirmTarget = GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); @@ -5156,7 +5357,8 @@ CKeyPool::CKeyPool(const CPubKey &vchPubKeyIn) { vchPubKey = vchPubKeyIn; } -CWalletKey::CWalletKey(int64_t nExpires) { +CWalletKey::CWalletKey(int64_t + nExpires) { nTimeCreated = (nExpires ? GetTime() : 0); nTimeExpires = nExpires; } @@ -5189,8 +5391,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock &block) { return chainActive.Height() - pindex->nHeight + 1; } -int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet, bool enableIX) const -{ +int CMerkleTx::GetDepthInMainChain(const CBlockIndex *&pindexRet, bool enableIX) const { int nResult; if (hashUnset()) @@ -5203,7 +5404,7 @@ int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet, bool enableIX) if (mi == mapBlockIndex.end()) nResult = 0; else { - CBlockIndex* pindex = (*mi).second; + CBlockIndex *pindex = (*mi).second; if (!pindex || !chainActive.Contains(pindex)) nResult = 0; else { @@ -5216,8 +5417,8 @@ int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet, bool enableIX) } } -// if(enableIX && nResult < 6 && instantsend.IsLockedInstantSendTransaction(GetHash())) -// return nInstantSendDepth + nResult; + if (enableIX && nResult < 6 && instantsend.IsLockedInstantSendTransaction(GetHash())) + return nInstantSendDepth + nResult; return nResult; } @@ -5246,10 +5447,13 @@ int CMerkleTx::GetBlocksToMaturity() const { } -bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, CAmount nAbsurdFee, CValidationState &state, bool fCheckInputs, bool isCheckWalletTransaction) { +bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, CAmount nAbsurdFee, CValidationState &state, bool fCheckInputs, + bool isCheckWalletTransaction) { LogPrintf("CMerkleTx::AcceptToMemoryPool(), fCheckInputs=%s\n", fCheckInputs); - return ::AcceptToMemoryPool(mempool, state, *this, fCheckInputs, fLimitFree, NULL, false, nAbsurdFee, isCheckWalletTransaction); + return ::AcceptToMemoryPool(mempool, state, *this, fCheckInputs, fLimitFree, NULL, false, nAbsurdFee, + isCheckWalletTransaction); } -bool CompHeight(const CZerocoinEntry & a, const CZerocoinEntry & b) { return a.nHeight < b.nHeight; } -bool CompID(const CZerocoinEntry & a, const CZerocoinEntry & b) { return a.id < b.id; } +bool CompHeight(const CZerocoinEntry &a, const CZerocoinEntry &b) { return a.nHeight < b.nHeight; } + +bool CompID(const CZerocoinEntry &a, const CZerocoinEntry &b) { return a.id < b.id; } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 581f3c722d..653efabde1 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -452,6 +452,7 @@ class COutput { tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; } + int Priority() const; std::string ToString() const; }; @@ -579,7 +580,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface * all coins from coinControl are selected; Never select unconfirmed coins * if they are not ours */ - bool SelectCoins(const std::vector& vAvailableCoins, const CAmount& nTargetValue, std::set >& setCoinsRet, CAmount& nValueRet, const CCoinControl *coinControl = NULL) const; + bool SelectCoins(const std::vector& vAvailableCoins, const CAmount& nTargetValue, std::set >& setCoinsRet, CAmount& nValueRet, const CCoinControl *coinControl = NULL, AvailableCoinsType nCoinType = ALL_COINS, bool fUseInstantSend = false) const; CWalletDB *pwalletdbEncryption; @@ -593,6 +594,11 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface int64_t nLastResend; bool fBroadcastTransactions; + mutable bool fAnonymizableTallyCached; + mutable std::vector vecAnonymizableTallyCached; + mutable bool fAnonymizableTallyCachedNonDenom; + mutable std::vector vecAnonymizableTallyCachedNonDenom; + /** * Used to keep track of spent outpoints, and * detect and report conflicts (double-spends or @@ -664,6 +670,10 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface nLastResend = 0; nTimeFirstKey = 0; fBroadcastTransactions = false; + fAnonymizableTallyCached = false; + fAnonymizableTallyCachedNonDenom = false; + vecAnonymizableTallyCached.clear(); + vecAnonymizableTallyCachedNonDenom.clear(); } std::map mapWallet; @@ -692,8 +702,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface /** * populate vCoins with vector of available COutputs. */ - void AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl = NULL, bool fIncludeZeroValue=false) const; - void AvailableCoinsDash(std::vector& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl = NULL, bool fIncludeZeroValue=false, AvailableCoinsType nCoinType=ALL_COINS, bool fUseInstantSend = false) const; + void AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl = NULL, bool fIncludeZeroValue=false, AvailableCoinsType nCoinType=ALL_COINS, bool fUseInstantSend = false) const; /** * Shuffle and select coins until nTargetValue is reached while avoiding @@ -703,6 +712,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface */ bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, uint64_t nMaxAncestors, std::vector vCoins, std::set >& setCoinsRet, CAmount& nValueRet) const; bool SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount nValueMax, std::vector& vecTxInRet, std::vector& vCoinsRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax); + bool GetCollateralTxIn(CTxIn& txinRet, CAmount& nValueRet) const; bool SelectCoinsDark(CAmount nValueMin, CAmount nValueMax, std::vector& vecTxInRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax) const; bool SelectCoinsGrouppedByAddresses(std::vector& vecTallyRet, bool fSkipDenominated = true, bool fAnonymizable = true) const; @@ -780,13 +790,17 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface void ReacceptWalletTransactions(); void ResendWalletTransactions(int64_t nBestBlockTime); std::vector ResendWalletTransactionsBefore(int64_t nTime); + //znode CAmount GetBalance() const; CAmount GetUnconfirmedBalance() const; CAmount GetImmatureBalance() const; CAmount GetWatchOnlyBalance() const; CAmount GetUnconfirmedWatchOnlyBalance() const; CAmount GetImmatureWatchOnlyBalance() const; - // znode + // get the PrivateSend chain depth for a given input + int GetRealInputPrivateSendRounds(CTxIn txin, int nRounds) const; + // respect current settings + int GetInputPrivateSendRounds(CTxIn txin) const; bool IsDenominated(const CTxIn &txin) const; bool IsDenominatedAmount(CAmount nInputAmount) const; bool IsCollateralAmount(CAmount nInputAmount) const; @@ -808,7 +822,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface * @note passing nChangePosInOut as -1 will result in setting a random position */ bool CreateTransaction(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, - std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true); + std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true, AvailableCoinsType nCoinType = ALL_COINS, bool fUseInstantSend = false); /** * btzc: * Add zerocoin Mint and Spend function diff --git a/src/znode-payments.cpp b/src/znode-payments.cpp index 96f7e9e6c8..95700ed236 100644 --- a/src/znode-payments.cpp +++ b/src/znode-payments.cpp @@ -4,12 +4,11 @@ #include "activeznode.h" #include "darksend.h" -//#include "governance-classes.h" #include "znode-payments.h" #include "znode-sync.h" #include "znodeman.h" -//#include "netfulfilledman.h" -//#include "spork.h" +#include "netfulfilledman.h" +#include "spork.h" #include "util.h" #include @@ -49,15 +48,15 @@ bool IsBlockValueValid(const CBlock& block, int nBlockHeight, CAmount blockRewar if(nBlockHeight >= consensusParams.nBudgetPaymentsStartBlock && nOffset < consensusParams.nBudgetPaymentsWindowBlocks) { // NOTE: make sure SPORK_13_OLD_SUPERBLOCK_FLAG is disabled when 12.1 starts to go live -// if(znodeSync.IsSynced() && !sporkManager.IsSporkActive(SPORK_13_OLD_SUPERBLOCK_FLAG)) { -// // no budget blocks should be accepted here, if SPORK_13_OLD_SUPERBLOCK_FLAG is disabled -// LogPrint("gobject", "IsBlockValueValid -- Client synced but budget spork is disabled, checking block value against block reward\n"); -// if(!isBlockRewardValueMet) { -// strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, budgets are disabled", -// nBlockHeight, block.vtx[0].GetValueOut(), blockReward); -// } -// return isBlockRewardValueMet; -// } + if(znodeSync.IsSynced() && !sporkManager.IsSporkActive(SPORK_13_OLD_SUPERBLOCK_FLAG)) { + // no budget blocks should be accepted here, if SPORK_13_OLD_SUPERBLOCK_FLAG is disabled + LogPrint("gobject", "IsBlockValueValid -- Client synced but budget spork is disabled, checking block value against block reward\n"); + if(!isBlockRewardValueMet) { + strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, budgets are disabled", + nBlockHeight, block.vtx[0].GetValueOut(), blockReward); + } + return isBlockRewardValueMet; + } LogPrint("gobject", "IsBlockValueValid -- WARNING: Skipping budget block value checks, accepting block\n"); // TODO: reprocess blocks to make sure they are legit? return true; @@ -98,7 +97,7 @@ bool IsBlockValueValid(const CBlock& block, int nBlockHeight, CAmount blockRewar // we are synced, let's try to check as much data as we can -// if(sporkManager.IsSporkActive(SPORK_9_SUPERBLOCKS_ENABLED)) { + if(sporkManager.IsSporkActive(SPORK_9_SUPERBLOCKS_ENABLED)) { //// if(CSuperblockManager::IsSuperblockTriggered(nBlockHeight)) { //// if(CSuperblockManager::IsValid(block.vtx[0], nBlockHeight, blockReward)) { //// LogPrint("gobject", "IsBlockValueValid -- Valid superblock at height %d: %s", nBlockHeight, block.vtx[0].ToString()); @@ -117,14 +116,14 @@ bool IsBlockValueValid(const CBlock& block, int nBlockHeight, CAmount blockRewar // strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, no triggered superblock detected", // nBlockHeight, block.vtx[0].GetValueOut(), blockReward); // } -// } else { + } else { // // should NOT allow superblocks at all, when superblocks are disabled -// LogPrint("gobject", "IsBlockValueValid -- Superblocks are disabled, no superblocks allowed\n"); -// if(!isBlockRewardValueMet) { -// strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, superblocks are disabled", -// nBlockHeight, block.vtx[0].GetValueOut(), blockReward); -// } -// } + LogPrint("gobject", "IsBlockValueValid -- Superblocks are disabled, no superblocks allowed\n"); + if(!isBlockRewardValueMet) { + strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, superblocks are disabled", + nBlockHeight, block.vtx[0].GetValueOut(), blockReward); + } + } // it MUST be a regular block return isBlockRewardValueMet; @@ -152,21 +151,21 @@ bool IsBlockPayeeValid(const CTransaction& txNew, int nBlockHeight, CAmount bloc int nOffset = nBlockHeight % consensusParams.nBudgetPaymentsCycleBlocks; if(nBlockHeight >= consensusParams.nBudgetPaymentsStartBlock && nOffset < consensusParams.nBudgetPaymentsWindowBlocks) { -// if(!sporkManager.IsSporkActive(SPORK_13_OLD_SUPERBLOCK_FLAG)) { -// // no budget blocks should be accepted here, if SPORK_13_OLD_SUPERBLOCK_FLAG is disabled -// LogPrint("gobject", "IsBlockPayeeValid -- ERROR: Client synced but budget spork is disabled and znode payment is invalid\n"); -// return false; -// } + if(!sporkManager.IsSporkActive(SPORK_13_OLD_SUPERBLOCK_FLAG)) { + // no budget blocks should be accepted here, if SPORK_13_OLD_SUPERBLOCK_FLAG is disabled + LogPrint("gobject", "IsBlockPayeeValid -- ERROR: Client synced but budget spork is disabled and znode payment is invalid\n"); + return false; + } // NOTE: this should never happen in real, SPORK_13_OLD_SUPERBLOCK_FLAG MUST be disabled when 12.1 starts to go live LogPrint("gobject", "IsBlockPayeeValid -- WARNING: Probably valid budget block, have no data, accepting\n"); // TODO: reprocess blocks to make sure they are legit? return true; } -// if(sporkManager.IsSporkActive(SPORK_8_ZNODE_PAYMENT_ENFORCEMENT)) { -// LogPrintf("IsBlockPayeeValid -- ERROR: Invalid znode payment detected at height %d: %s", nBlockHeight, txNew.ToString()); -// return false; -// } + if(sporkManager.IsSporkActive(SPORK_8_ZNODE_PAYMENT_ENFORCEMENT)) { + LogPrintf("IsBlockPayeeValid -- ERROR: Invalid znode payment detected at height %d: %s", nBlockHeight, txNew.ToString()); + return false; + } LogPrintf("IsBlockPayeeValid -- WARNING: Znode payment enforcement is disabled, accepting any payee\n"); return true; @@ -175,7 +174,7 @@ bool IsBlockPayeeValid(const CTransaction& txNew, int nBlockHeight, CAmount bloc // superblocks started // SEE IF THIS IS A VALID SUPERBLOCK -// if(sporkManager.IsSporkActive(SPORK_9_SUPERBLOCKS_ENABLED)) { + if(sporkManager.IsSporkActive(SPORK_9_SUPERBLOCKS_ENABLED)) { //// if(CSuperblockManager::IsSuperblockTriggered(nBlockHeight)) { //// if(CSuperblockManager::IsValid(txNew, nBlockHeight, blockReward)) { //// LogPrint("gobject", "IsBlockPayeeValid -- Valid superblock at height %d: %s", nBlockHeight, txNew.ToString()); @@ -188,10 +187,10 @@ bool IsBlockPayeeValid(const CTransaction& txNew, int nBlockHeight, CAmount bloc //// } // // continue validation, should pay MN // LogPrint("gobject", "IsBlockPayeeValid -- No triggered superblock detected at height %d\n", nBlockHeight); -// } else { -// // should NOT allow superblocks at all, when superblocks are disabled -// LogPrint("gobject", "IsBlockPayeeValid -- Superblocks are disabled, no superblocks allowed\n"); -// } + } else { + // should NOT allow superblocks at all, when superblocks are disabled + LogPrint("gobject", "IsBlockPayeeValid -- Superblocks are disabled, no superblocks allowed\n"); + } // IF THIS ISN'T A SUPERBLOCK OR SUPERBLOCK IS INVALID, IT SHOULD PAY A ZNODE DIRECTLY if(mnpayments.IsTransactionValid(txNew, nBlockHeight)) { @@ -199,10 +198,10 @@ bool IsBlockPayeeValid(const CTransaction& txNew, int nBlockHeight, CAmount bloc return true; } -// if(sporkManager.IsSporkActive(SPORK_8_ZNODE_PAYMENT_ENFORCEMENT)) { -// LogPrintf("IsBlockPayeeValid -- ERROR: Invalid znode payment detected at height %d: %s", nBlockHeight, txNew.ToString()); -// return false; -// } + if(sporkManager.IsSporkActive(SPORK_8_ZNODE_PAYMENT_ENFORCEMENT)) { + LogPrintf("IsBlockPayeeValid -- ERROR: Invalid znode payment detected at height %d: %s", nBlockHeight, txNew.ToString()); + return false; + } LogPrintf("IsBlockPayeeValid -- WARNING: Znode payment enforcement is disabled, accepting any payee\n"); return true; @@ -299,11 +298,9 @@ void CZnodePayments::FillBlockPayee(CMutableTransaction& txNew, int nBlockHeight } int CZnodePayments::GetMinZnodePaymentsProto() { -// return sporkManager.IsSporkActive(SPORK_10_ZNODE_PAY_UPDATED_NODES) -// ? MIN_ZNODE_PAYMENT_PROTO_VERSION_2 -// : MIN_ZNODE_PAYMENT_PROTO_VERSION_1; - - return MIN_ZNODE_PAYMENT_PROTO_VERSION_1; + return sporkManager.IsSporkActive(SPORK_10_ZNODE_PAY_UPDATED_NODES) + ? MIN_ZNODE_PAYMENT_PROTO_VERSION_2 + : MIN_ZNODE_PAYMENT_PROTO_VERSION_1; } void CZnodePayments::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) @@ -323,13 +320,13 @@ void CZnodePayments::ProcessMessage(CNode* pfrom, std::string& strCommand, CData int nCountNeeded; vRecv >> nCountNeeded; -// if(netfulfilledman.HasFulfilledRequest(pfrom->addr, NetMsgType::ZNODEPAYMENTSYNC)) { -// // Asking for the payments list multiple times in a short period of time is no good -// LogPrintf("ZNODEPAYMENTSYNC -- peer already asked me for the list, peer=%d\n", pfrom->id); -// Misbehaving(pfrom->GetId(), 20); -// return; -// } -// netfulfilledman.AddFulfilledRequest(pfrom->addr, NetMsgType::ZNODEPAYMENTSYNC); + if(netfulfilledman.HasFulfilledRequest(pfrom->addr, NetMsgType::ZNODEPAYMENTSYNC)) { + // Asking for the payments list multiple times in a short period of time is no good + LogPrintf("ZNODEPAYMENTSYNC -- peer already asked me for the list, peer=%d\n", pfrom->id); + Misbehaving(pfrom->GetId(), 20); + return; + } + netfulfilledman.AddFulfilledRequest(pfrom->addr, NetMsgType::ZNODEPAYMENTSYNC); Sync(pfrom); LogPrintf("ZNODEPAYMENTSYNC -- Sent Znode payment votes to peer %d\n", pfrom->id); diff --git a/src/znode-sync.cpp b/src/znode-sync.cpp index 7727ceaf70..28a88c725d 100644 --- a/src/znode-sync.cpp +++ b/src/znode-sync.cpp @@ -4,14 +4,13 @@ #include "activeznode.h" #include "checkpoints.h" -//#include "governance.h" #include "main.h" #include "znode.h" #include "znode-payments.h" #include "znode-sync.h" #include "znodeman.h" -//#include "netfulfilledman.h" -//#include "spork.h" +#include "netfulfilledman.h" +#include "spork.h" #include "util.h" class CZnodeSync; @@ -193,7 +192,7 @@ void CZnodeSync::SwitchToNextAsset() if(!lockRecv) return; BOOST_FOREACH(CNode* pnode, vNodes) { -// netfulfilledman.AddFulfilledRequest(pnode->addr, "full-sync"); + netfulfilledman.AddFulfilledRequest(pnode->addr, "full-sync"); } break; @@ -238,11 +237,10 @@ void CZnodeSync::ClearFulfilledRequests() BOOST_FOREACH(CNode* pnode, vNodes) { -// netfulfilledman.RemoveFulfilledRequest(pnode->addr, "spork-sync"); -// netfulfilledman.RemoveFulfilledRequest(pnode->addr, "znode-list-sync"); -// netfulfilledman.RemoveFulfilledRequest(pnode->addr, "znode-payment-sync"); -// netfulfilledman.RemoveFulfilledRequest(pnode->addr, "governance-sync"); -// netfulfilledman.RemoveFulfilledRequest(pnode->addr, "full-sync"); + netfulfilledman.RemoveFulfilledRequest(pnode->addr, "spork-sync"); + netfulfilledman.RemoveFulfilledRequest(pnode->addr, "znode-list-sync"); + netfulfilledman.RemoveFulfilledRequest(pnode->addr, "znode-payment-sync"); + netfulfilledman.RemoveFulfilledRequest(pnode->addr, "full-sync"); } } @@ -336,24 +334,24 @@ void CZnodeSync::ProcessTick() // NORMAL NETWORK MODE - TESTNET/MAINNET { -// if(netfulfilledman.HasFulfilledRequest(pnode->addr, "full-sync")) { -// // We already fully synced from this node recently, -// // disconnect to free this connection slot for another peer. -// pnode->fDisconnect = true; -// LogPrintf("CZnodeSync::ProcessTick -- disconnecting from recently synced peer %d\n", pnode->id); -// continue; -// } -// -// // SPORK : ALWAYS ASK FOR SPORKS AS WE SYNC (we skip this mode now) -// -// if(!netfulfilledman.HasFulfilledRequest(pnode->addr, "spork-sync")) { -// // only request once from each peer -// netfulfilledman.AddFulfilledRequest(pnode->addr, "spork-sync"); -// // get current network sporks -// pnode->PushMessage(NetMsgType::GETSPORKS); -// LogPrintf("CZnodeSync::ProcessTick -- nTick %d nRequestedZnodeAssets %d -- requesting sporks from peer %d\n", nTick, nRequestedZnodeAssets, pnode->id); -// continue; // always get sporks first, switch to the next node without waiting for the next tick -// } + if(netfulfilledman.HasFulfilledRequest(pnode->addr, "full-sync")) { + // We already fully synced from this node recently, + // disconnect to free this connection slot for another peer. + pnode->fDisconnect = true; + LogPrintf("CZnodeSync::ProcessTick -- disconnecting from recently synced peer %d\n", pnode->id); + continue; + } + + // SPORK : ALWAYS ASK FOR SPORKS AS WE SYNC (we skip this mode now) + + if(!netfulfilledman.HasFulfilledRequest(pnode->addr, "spork-sync")) { + // only request once from each peer + netfulfilledman.AddFulfilledRequest(pnode->addr, "spork-sync"); + // get current network sporks + pnode->PushMessage(NetMsgType::GETSPORKS); + LogPrintf("CZnodeSync::ProcessTick -- nTick %d nRequestedZnodeAssets %d -- requesting sporks from peer %d\n", nTick, nRequestedZnodeAssets, pnode->id); + continue; // always get sporks first, switch to the next node without waiting for the next tick + } // MNLIST : SYNC ZNODE LIST FROM OTHER CONNECTED CLIENTS @@ -375,8 +373,8 @@ void CZnodeSync::ProcessTick() } // only request once from each peer -// if(netfulfilledman.HasFulfilledRequest(pnode->addr, "znode-list-sync")) continue; -// netfulfilledman.AddFulfilledRequest(pnode->addr, "znode-list-sync"); + if(netfulfilledman.HasFulfilledRequest(pnode->addr, "znode-list-sync")) continue; + netfulfilledman.AddFulfilledRequest(pnode->addr, "znode-list-sync"); if (pnode->nVersion < mnpayments.GetMinZnodePaymentsProto()) continue; nRequestedZnodeAttempt++; @@ -419,8 +417,8 @@ void CZnodeSync::ProcessTick() } // only request once from each peer -// if(netfulfilledman.HasFulfilledRequest(pnode->addr, "znode-payment-sync")) continue; -// netfulfilledman.AddFulfilledRequest(pnode->addr, "znode-payment-sync"); + if(netfulfilledman.HasFulfilledRequest(pnode->addr, "znode-payment-sync")) continue; + netfulfilledman.AddFulfilledRequest(pnode->addr, "znode-payment-sync"); if(pnode->nVersion < mnpayments.GetMinZnodePaymentsProto()) continue; nRequestedZnodeAttempt++; @@ -490,7 +488,7 @@ void CZnodeSync::ProcessTick() // if (pnode->nVersion < MIN_GOVERNANCE_PEER_PROTO_VERSION) continue; nRequestedZnodeAttempt++; - SendGovernanceSyncRequest(pnode); +// SendGovernanceSyncRequest(pnode); ReleaseNodeVector(vNodesCopy); return; //this will cause each peer to get one request each six seconds for the various assets we need diff --git a/src/znode.cpp b/src/znode.cpp index 17282dd594..34ac8a3ac9 100644 --- a/src/znode.cpp +++ b/src/znode.cpp @@ -638,7 +638,7 @@ bool CZnodeBroadcast::CheckOutpoint(int& nDos) LogPrint("znode", "CZnodeBroadcast::CheckOutpoint -- Failed to find Znode UTXO, znode=%s\n", vin.prevout.ToStringShort()); return false; } - if(coins.vout[vin.prevout.n].nValue != 1000 * COIN) { + if(coins.vout[vin.prevout.n].nValue != ZNODE_COIN_REQUIRED * COIN) { LogPrint("znode", "CZnodeBroadcast::CheckOutpoint -- Znode UTXO should have 1000 DASH, znode=%s\n", vin.prevout.ToStringShort()); return false; } diff --git a/src/znode.h b/src/znode.h index ab4e1781c3..1b10074cfd 100644 --- a/src/znode.h +++ b/src/znode.h @@ -22,6 +22,7 @@ static const int ZNODE_MIN_MNP_SECONDS = 10 * 60; static const int ZNODE_EXPIRATION_SECONDS = 65 * 60; static const int ZNODE_WATCHDOG_MAX_SECONDS = 120 * 60; static const int ZNODE_NEW_START_REQUIRED_SECONDS = 180 * 60; +static const int ZNODE_COIN_REQUIRED = 1000; static const int ZNODE_POSE_BAN_MAX_SCORE = 5; // diff --git a/src/znodeman.cpp b/src/znodeman.cpp index b95a8d82b4..556c31f201 100644 --- a/src/znodeman.cpp +++ b/src/znodeman.cpp @@ -9,12 +9,11 @@ #include "znode-payments.h" #include "znode-sync.h" #include "znodeman.h" -//#include "netfulfilledman.h" +#include "netfulfilledman.h" #include "util.h" /** Znode manager */ CZnodeMan mnodeman; -//CNetFulfilledRequestManager netfulfilledman; const std::string CZnodeMan::SERIALIZATION_VERSION_STRING = "CZnodeMan-Version-4"; @@ -97,8 +96,7 @@ void CZnodeIndex::RebuildIndex() } } -CZnodeMan::CZnodeMan() -: cs(), +CZnodeMan::CZnodeMan() : cs(), vZnodes(), mAskedUsForZnodeList(), mWeAskedForZnodeList(), @@ -630,9 +628,9 @@ CZnode* CZnodeMan::FindRandomNotInVec(const std::vector &vecToExclude, in vpZnodesShuffled.push_back(&mn); } -// InsecureRand insecureRand; + InsecureRand insecureRand; // shuffle pointers -// std::random_shuffle(vpZnodesShuffled.begin(), vpZnodesShuffled.end(), insecureRand); + std::random_shuffle(vpZnodesShuffled.begin(), vpZnodesShuffled.end(), insecureRand); bool fExclude; // loop through @@ -1089,19 +1087,19 @@ void CZnodeMan::CheckSameAddr() bool CZnodeMan::SendVerifyRequest(const CAddress& addr, const std::vector& vSortedByAddr) { -// if(netfulfilledman.HasFulfilledRequest(addr, strprintf("%s", NetMsgType::MNVERIFY)+"-request")) { + if(netfulfilledman.HasFulfilledRequest(addr, strprintf("%s", NetMsgType::MNVERIFY)+"-request")) { // we already asked for verification, not a good idea to do this too often, skip it -// LogPrint("znode", "CZnodeMan::SendVerifyRequest -- too many requests, skipping... addr=%s\n", addr.ToString()); -// return false; -// } + LogPrint("znode", "CZnodeMan::SendVerifyRequest -- too many requests, skipping... addr=%s\n", addr.ToString()); + return false; + } - CNode* pnode = ConnectNodeDash(addr, NULL, true); + CNode* pnode = ConnectNode(addr, NULL, false, true); if(pnode == NULL) { LogPrintf("CZnodeMan::SendVerifyRequest -- can't connect to node to verify it, addr=%s\n", addr.ToString()); return false; } -// netfulfilledman.AddFulfilledRequest(addr, strprintf("%s", NetMsgType::MNVERIFY)+"-request"); + netfulfilledman.AddFulfilledRequest(addr, strprintf("%s", NetMsgType::MNVERIFY)+"-request"); // use random nonce, store it and require node to reply with correct one later CZnodeVerification mnv(addr, GetRandInt(999999), pCurrentBlockIndex->nHeight - 1); mWeAskedForVerification[addr] = mnv; @@ -1120,12 +1118,12 @@ void CZnodeMan::SendVerifyReply(CNode* pnode, CZnodeVerification& mnv) return; } -// if(netfulfilledman.HasFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-reply")) { + if(netfulfilledman.HasFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-reply")) { // // peer should not ask us that often -// LogPrintf("ZnodeMan::SendVerifyReply -- ERROR: peer already asked me recently, peer=%d\n", pnode->id); -// Misbehaving(pnode->id, 20); -// return; -// } + LogPrintf("ZnodeMan::SendVerifyReply -- ERROR: peer already asked me recently, peer=%d\n", pnode->id); + Misbehaving(pnode->id, 20); + return; + } uint256 blockHash; if(!GetBlockHash(blockHash, mnv.nBlockHeight)) { @@ -1148,7 +1146,7 @@ void CZnodeMan::SendVerifyReply(CNode* pnode, CZnodeVerification& mnv) } pnode->PushMessage(NetMsgType::MNVERIFY, mnv); -// netfulfilledman.AddFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-reply"); + netfulfilledman.AddFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-reply"); } void CZnodeMan::ProcessVerifyReply(CNode* pnode, CZnodeVerification& mnv) @@ -1156,11 +1154,11 @@ void CZnodeMan::ProcessVerifyReply(CNode* pnode, CZnodeVerification& mnv) std::string strError; // did we even ask for it? if that's the case we should have matching fulfilled request -// if(!netfulfilledman.HasFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-request")) { -// LogPrintf("CZnodeMan::ProcessVerifyReply -- ERROR: we didn't ask for verification of %s, peer=%d\n", pnode->addr.ToString(), pnode->id); -// Misbehaving(pnode->id, 20); -// return; -// } + if(!netfulfilledman.HasFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-request")) { + LogPrintf("CZnodeMan::ProcessVerifyReply -- ERROR: we didn't ask for verification of %s, peer=%d\n", pnode->addr.ToString(), pnode->id); + Misbehaving(pnode->id, 20); + return; + } // Received nonce for a known address must match the one we sent if(mWeAskedForVerification[pnode->addr].nonce != mnv.nonce) { @@ -1186,11 +1184,11 @@ void CZnodeMan::ProcessVerifyReply(CNode* pnode, CZnodeVerification& mnv) } // // we already verified this address, why node is spamming? -// if(netfulfilledman.HasFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-done")) { -// LogPrintf("CZnodeMan::ProcessVerifyReply -- ERROR: already verified %s recently\n", pnode->addr.ToString()); -// Misbehaving(pnode->id, 20); -// return; -// } + if(netfulfilledman.HasFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-done")) { + LogPrintf("CZnodeMan::ProcessVerifyReply -- ERROR: already verified %s recently\n", pnode->addr.ToString()); + Misbehaving(pnode->id, 20); + return; + } { LOCK(cs); @@ -1207,7 +1205,7 @@ void CZnodeMan::ProcessVerifyReply(CNode* pnode, CZnodeVerification& mnv) if(!it->IsPoSeVerified()) { it->DecreasePoSeBanScore(); } -// netfulfilledman.AddFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-done"); + netfulfilledman.AddFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-done"); // we can only broadcast it if we are an activated znode if(activeZnode.vin == CTxIn()) continue; From 1f53e327b9ee35cca9400cc3c7832edd34a4069e Mon Sep 17 00:00:00 2001 From: "snguyen.ntu@gmail.com" Date: Fri, 17 Nov 2017 14:39:05 +0700 Subject: [PATCH 04/35] Correct znodeConfig. Update check conditions --- src/init.cpp | 40 ++++---- src/znode-sync.cpp | 247 ++++++++++++++++++++++++--------------------- 2 files changed, 148 insertions(+), 139 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 7583853f7d..85c67940c9 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1669,12 +1669,13 @@ bool AppInit2(boost::thread_group &threadGroup, CScheduler &scheduler) { chainparams); // ********************************************************* Step 11a: setup PrivateSend - fZNode = GetBoolArg("-masternode", false); + fZNode = GetBoolArg("-znode", false); + LogPrintf("fZNode = %s\n", fZNode); LogPrintf("znodeConfig.getCount(): %s\n", znodeConfig.getCount()); if ((fZNode || znodeConfig.getCount() > 0) && !fTxIndex) { - return InitError("Enabling Masternode support requires turning on transaction indexing." + return InitError("Enabling Znode support requires turning on transaction indexing." "Please add txindex=1 to your configuration and start with -reindex"); } @@ -1682,25 +1683,25 @@ bool AppInit2(boost::thread_group &threadGroup, CScheduler &scheduler) { LogPrintf("ZNODE:\n"); if (!GetArg("-znodeaddr", "").empty()) { - // Hot masternode (either local or remote) should get its address in - // CActiveMasternode::ManageState() automatically and no longer relies on masternodeaddr. + // Hot Znode (either local or remote) should get its address in + // CActiveZnode::ManageState() automatically and no longer relies on Znodeaddr. return InitError(_("znodeaddr option is deprecated. Please use znode.conf to manage your remote znodes.")); } - std::string strMasterNodePrivKey = GetArg("-masternodeprivkey", ""); - if (!strMasterNodePrivKey.empty()) { - if (!darkSendSigner.GetKeysFromSecret(strMasterNodePrivKey, activeZnode.keyZnode, + std::string strZnodePrivKey = GetArg("-znodeprivkey", ""); + if (!strZnodePrivKey.empty()) { + if (!darkSendSigner.GetKeysFromSecret(strZnodePrivKey, activeZnode.keyZnode, activeZnode.pubKeyZnode)) return InitError(_("Invalid znodeprivkey. Please see documenation.")); LogPrintf(" pubKeyZnode: %s\n", CBitcoinAddress(activeZnode.pubKeyZnode.GetID()).ToString()); } else { return InitError( - _("You must specify a masternodeprivkey in the configuration. Please see documentation for help.")); + _("You must specify a znodeprivkey in the configuration. Please see documentation for help.")); } } - LogPrintf("Using masternode config file %s\n", GetZnodeConfigFile().string()); + LogPrintf("Using Znode config file %s\n", GetZnodeConfigFile().string()); if (GetBoolArg("-mnconflock", true) && pwalletMain && (znodeConfig.getCount() > 0)) { LOCK(pwalletMain->cs_wallet); @@ -1737,10 +1738,10 @@ bool AppInit2(boost::thread_group &threadGroup, CScheduler &scheduler) { nInstantSendDepth = GetArg("-instantsenddepth", DEFAULT_INSTANTSEND_DEPTH); nInstantSendDepth = std::min(std::max(nInstantSendDepth, 0), 60); - //lite mode disables all Masternode and Darksend related functionality + //lite mode disables all Znode and Darksend related functionality fLiteMode = GetBoolArg("-litemode", false); if (fZNode && fLiteMode) { - return InitError("You can not start a masternode in litemode"); + return InitError("You can not start a znode in litemode"); } LogPrintf("fLiteMode %d\n", fLiteMode); @@ -1755,26 +1756,19 @@ bool AppInit2(boost::thread_group &threadGroup, CScheduler &scheduler) { // LOAD SERIALIZED DAT FILES INTO DATA CACHES FOR INTERNAL USE uiInterface.InitMessage(_("Loading znode cache...")); - CFlatDB flatdb1("mncache.dat", "magicMasternodeCache"); + CFlatDB flatdb1("mncache.dat", "magicZnodeCache"); if (!flatdb1.Load(mnodeman)) { return InitError("Failed to load znode cache from mncache.dat"); } if (mnodeman.size()) { - uiInterface.InitMessage(_("Loading masternode payment cache...")); - CFlatDB flatdb2("mnpayments.dat", "magicMasternodePaymentsCache"); + uiInterface.InitMessage(_("Loading Znode payment cache...")); + CFlatDB flatdb2("mnpayments.dat", "magicZnodePaymentsCache"); if (!flatdb2.Load(mnpayments)) { return InitError("Failed to load znode payments cache from mnpayments.dat"); } - -// uiInterface.InitMessage(_("Loading governance cache...")); -// CFlatDB flatdb3("governance.dat", "magicGovernanceCache"); -// if(!flatdb3.Load(governance)) { -// return InitError("Failed to load governance cache from governance.dat"); -// } -// governance.InitOnLoad(); } else { - uiInterface.InitMessage(_("Masternode cache is empty, skipping payments and governance cache...")); + uiInterface.InitMessage(_("Znode cache is empty, skipping payments and governance cache...")); } uiInterface.InitMessage(_("Loading fulfilled requests cache...")); @@ -1787,7 +1781,7 @@ bool AppInit2(boost::thread_group &threadGroup, CScheduler &scheduler) { // force UpdatedBlockTip to initialize pCurrentBlockIndex for DS, MN payments and budgets // but don't call it directly to prevent triggering of other listeners like zmq etc. - // GetMainSignals().UpdatedBlockTip(chainActive.Tip()); +// GetMainSignals().UpdatedBlockTip(chainActive.Tip()); mnodeman.UpdatedBlockTip(chainActive.Tip()); darkSendPool.UpdatedBlockTip(chainActive.Tip()); mnpayments.UpdatedBlockTip(chainActive.Tip()); diff --git a/src/znode-sync.cpp b/src/znode-sync.cpp index 28a88c725d..057a960705 100644 --- a/src/znode-sync.cpp +++ b/src/znode-sync.cpp @@ -14,57 +14,55 @@ #include "util.h" class CZnodeSync; + CZnodeSync znodeSync; -bool CZnodeSync::CheckNodeHeight(CNode* pnode, bool fDisconnectStuckNodes) -{ +bool CZnodeSync::CheckNodeHeight(CNode *pnode, bool fDisconnectStuckNodes) { CNodeStateStats stats; - if(!GetNodeStateStats(pnode->id, stats) || stats.nCommonHeight == -1 || stats.nSyncHeight == -1) return false; // not enough info about this peer + if (!GetNodeStateStats(pnode->id, stats) || stats.nCommonHeight == -1 || stats.nSyncHeight == -1) return false; // not enough info about this peer // Check blocks and headers, allow a small error margin of 1 block - if(pCurrentBlockIndex->nHeight - 1 > stats.nCommonHeight) { + if (pCurrentBlockIndex->nHeight - 1 > stats.nCommonHeight) { // This peer probably stuck, don't sync any additional data from it - if(fDisconnectStuckNodes) { + if (fDisconnectStuckNodes) { // Disconnect to free this connection slot for another peer. pnode->fDisconnect = true; LogPrintf("CZnodeSync::CheckNodeHeight -- disconnecting from stuck peer, nHeight=%d, nCommonHeight=%d, peer=%d\n", - pCurrentBlockIndex->nHeight, stats.nCommonHeight, pnode->id); + pCurrentBlockIndex->nHeight, stats.nCommonHeight, pnode->id); } else { LogPrintf("CZnodeSync::CheckNodeHeight -- skipping stuck peer, nHeight=%d, nCommonHeight=%d, peer=%d\n", - pCurrentBlockIndex->nHeight, stats.nCommonHeight, pnode->id); + pCurrentBlockIndex->nHeight, stats.nCommonHeight, pnode->id); } return false; - } - else if(pCurrentBlockIndex->nHeight < stats.nSyncHeight - 1) { + } else if (pCurrentBlockIndex->nHeight < stats.nSyncHeight - 1) { // This peer announced more headers than we have blocks currently LogPrintf("CZnodeSync::CheckNodeHeight -- skipping peer, who announced more headers than we have blocks currently, nHeight=%d, nSyncHeight=%d, peer=%d\n", - pCurrentBlockIndex->nHeight, stats.nSyncHeight, pnode->id); + pCurrentBlockIndex->nHeight, stats.nSyncHeight, pnode->id); return false; } return true; } -bool CZnodeSync::IsBlockchainSynced(bool fBlockAccepted) -{ +bool CZnodeSync::IsBlockchainSynced(bool fBlockAccepted) { static bool fBlockchainSynced = false; static int64_t nTimeLastProcess = GetTime(); static int nSkipped = 0; static bool fFirstBlockAccepted = false; // if the last call to this function was more than 60 minutes ago (client was in sleep mode) reset the sync process - if(GetTime() - nTimeLastProcess > 60*60) { + if (GetTime() - nTimeLastProcess > 60 * 60) { Reset(); fBlockchainSynced = false; } - if(!pCurrentBlockIndex || !pindexBestHeader || fImporting || fReindex) return false; + if (!pCurrentBlockIndex || !pindexBestHeader || fImporting || fReindex) return false; - if(fBlockAccepted) { + if (fBlockAccepted) { // this should be only triggered while we are still syncing - if(!IsSynced()) { + if (!IsSynced()) { // we are trying to download smth, reset blockchain sync status - if(fDebug) LogPrintf("CZnodeSync::IsBlockchainSynced -- reset\n"); + if (fDebug) LogPrintf("CZnodeSync::IsBlockchainSynced -- reset\n"); fFirstBlockAccepted = true; fBlockchainSynced = false; nTimeLastProcess = GetTime(); @@ -72,35 +70,41 @@ bool CZnodeSync::IsBlockchainSynced(bool fBlockAccepted) } } else { // skip if we already checked less than 1 tick ago - if(GetTime() - nTimeLastProcess < ZNODE_SYNC_TICK_SECONDS) { + if (GetTime() - nTimeLastProcess < ZNODE_SYNC_TICK_SECONDS) { nSkipped++; return fBlockchainSynced; } } - if(fDebug) LogPrintf("CZnodeSync::IsBlockchainSynced -- state before check: %ssynced, skipped %d times\n", fBlockchainSynced ? "" : "not ", nSkipped); + if (fDebug) LogPrintf("CZnodeSync::IsBlockchainSynced -- state before check: %ssynced, skipped %d times\n", fBlockchainSynced ? "" : "not ", nSkipped); nTimeLastProcess = GetTime(); nSkipped = 0; - - if(fBlockchainSynced) return true; - - if(fCheckpointsEnabled && pCurrentBlockIndex->nHeight < Checkpoints::GetTotalBlocksEstimate(Params().Checkpoints())) + LogPrintf("CZnodeSync::IsBlockchainSynced Synced\n"); + if (fBlockchainSynced) return true; + + if (fCheckpointsEnabled && pCurrentBlockIndex->nHeight < Checkpoints::GetTotalBlocksEstimate(Params().Checkpoints())) { + LogPrintf("fCheckpointsEnabled=%s\n", fCheckpointsEnabled); + LogPrintf("pCurrentBlockIndex->nHeight=%s\n", pCurrentBlockIndex->nHeight); + LogPrintf("Checkpoints::GetTotalBlocksEstimate(Params().Checkpoints())=%s\n", Checkpoints::GetTotalBlocksEstimate(Params().Checkpoints())); + LogPrintf("pCurrentBlockIndex->nHeight < Checkpoints::GetTotalBlocksEstimate(Params().Checkpoints())=%s\n", pCurrentBlockIndex->nHeight < Checkpoints::GetTotalBlocksEstimate(Params().Checkpoints())); + LogPrintf("CZnodeSync::IsBlockchainSynced not synced\n"); return false; + } - std::vector vNodesCopy = CopyNodeVector(); + std::vector < CNode * > vNodesCopy = CopyNodeVector(); // We have enough peers and assume most of them are synced - if(vNodesCopy.size() >= ZNODE_SYNC_ENOUGH_PEERS) { + if (vNodesCopy.size() >= ZNODE_SYNC_ENOUGH_PEERS) { // Check to see how many of our peers are (almost) at the same height as we are int nNodesAtSameHeight = 0; - BOOST_FOREACH(CNode* pnode, vNodesCopy) + BOOST_FOREACH(CNode * pnode, vNodesCopy) { // Make sure this peer is presumably at the same height - if(!CheckNodeHeight(pnode)) continue; + if (!CheckNodeHeight(pnode)) continue; nNodesAtSameHeight++; // if we have decent number of such peers, most likely we are synced now - if(nNodesAtSameHeight >= ZNODE_SYNC_ENOUGH_PEERS) { + if (nNodesAtSameHeight >= ZNODE_SYNC_ENOUGH_PEERS) { LogPrintf("CZnodeSync::IsBlockchainSynced -- found enough peers on the same height as we are, done\n"); fBlockchainSynced = true; ReleaseNodeVector(vNodesCopy); @@ -111,24 +115,29 @@ bool CZnodeSync::IsBlockchainSynced(bool fBlockAccepted) ReleaseNodeVector(vNodesCopy); // wait for at least one new block to be accepted - if(!fFirstBlockAccepted) return false; + LogPrintf("fFirstBlockAccepted=%s", fFirstBlockAccepted); + if (!fFirstBlockAccepted) return false; // same as !IsInitialBlockDownload() but no cs_main needed here int64_t nMaxBlockTime = std::max(pCurrentBlockIndex->GetBlockTime(), pindexBestHeader->GetBlockTime()); + LogPrintf("nMaxBlockTime=%s\n", nMaxBlockTime); fBlockchainSynced = pindexBestHeader->nHeight - pCurrentBlockIndex->nHeight < 24 * 6 && GetTime() - nMaxBlockTime < Params().MaxTipAge(); + LogPrintf("pindexBestHeader->nHeight=%s\n", pindexBestHeader->nHeight); + LogPrintf("pCurrentBlockIndex->nHeight=%s\n", pCurrentBlockIndex->nHeight); + LogPrintf("GetTime() - nMaxBlockTime=%s\n", GetTime() - nMaxBlockTime); + LogPrintf("Params().MaxTipAge()=%s\n", Params().MaxTipAge()); + LogPrintf("fBlockchainSynced\n", fBlockchainSynced); return fBlockchainSynced; } -void CZnodeSync::Fail() -{ +void CZnodeSync::Fail() { nTimeLastFailure = GetTime(); nRequestedZnodeAssets = ZNODE_SYNC_FAILED; } -void CZnodeSync::Reset() -{ +void CZnodeSync::Reset() { nRequestedZnodeAssets = ZNODE_SYNC_INITIAL; nRequestedZnodeAttempt = 0; nTimeAssetSyncStarted = GetTime(); @@ -139,49 +148,53 @@ void CZnodeSync::Reset() nCountFailures = 0; } -std::string CZnodeSync::GetAssetName() -{ - switch(nRequestedZnodeAssets) - { - case(ZNODE_SYNC_INITIAL): return "ZNODE_SYNC_INITIAL"; - case(ZNODE_SYNC_SPORKS): return "ZNODE_SYNC_SPORKS"; - case(ZNODE_SYNC_LIST): return "ZNODE_SYNC_LIST"; - case(ZNODE_SYNC_MNW): return "ZNODE_SYNC_MNW"; - case(ZNODE_SYNC_GOVERNANCE): return "ZNODE_SYNC_GOVERNANCE"; - case(ZNODE_SYNC_FAILED): return "ZNODE_SYNC_FAILED"; - case ZNODE_SYNC_FINISHED: return "ZNODE_SYNC_FINISHED"; - default: return "UNKNOWN"; +std::string CZnodeSync::GetAssetName() { + switch (nRequestedZnodeAssets) { + case (ZNODE_SYNC_INITIAL): + return "ZNODE_SYNC_INITIAL"; + case (ZNODE_SYNC_SPORKS): + return "ZNODE_SYNC_SPORKS"; + case (ZNODE_SYNC_LIST): + return "ZNODE_SYNC_LIST"; + case (ZNODE_SYNC_MNW): + return "ZNODE_SYNC_MNW"; + case (ZNODE_SYNC_GOVERNANCE): + return "ZNODE_SYNC_GOVERNANCE"; + case (ZNODE_SYNC_FAILED): + return "ZNODE_SYNC_FAILED"; + case ZNODE_SYNC_FINISHED: + return "ZNODE_SYNC_FINISHED"; + default: + return "UNKNOWN"; } } -void CZnodeSync::SwitchToNextAsset() -{ - switch(nRequestedZnodeAssets) - { - case(ZNODE_SYNC_FAILED): +void CZnodeSync::SwitchToNextAsset() { + switch (nRequestedZnodeAssets) { + case (ZNODE_SYNC_FAILED): throw std::runtime_error("Can't switch to next asset from failed, should use Reset() first!"); break; - case(ZNODE_SYNC_INITIAL): + case (ZNODE_SYNC_INITIAL): ClearFulfilledRequests(); nRequestedZnodeAssets = ZNODE_SYNC_SPORKS; LogPrintf("CZnodeSync::SwitchToNextAsset -- Starting %s\n", GetAssetName()); break; - case(ZNODE_SYNC_SPORKS): + case (ZNODE_SYNC_SPORKS): nTimeLastZnodeList = GetTime(); nRequestedZnodeAssets = ZNODE_SYNC_LIST; LogPrintf("CZnodeSync::SwitchToNextAsset -- Starting %s\n", GetAssetName()); break; - case(ZNODE_SYNC_LIST): + case (ZNODE_SYNC_LIST): nTimeLastPaymentVote = GetTime(); nRequestedZnodeAssets = ZNODE_SYNC_MNW; LogPrintf("CZnodeSync::SwitchToNextAsset -- Starting %s\n", GetAssetName()); break; - case(ZNODE_SYNC_MNW): + case (ZNODE_SYNC_MNW): nTimeLastGovernanceItem = GetTime(); nRequestedZnodeAssets = ZNODE_SYNC_GOVERNANCE; LogPrintf("CZnodeSync::SwitchToNextAsset -- Starting %s\n", GetAssetName()); break; - case(ZNODE_SYNC_GOVERNANCE): + case (ZNODE_SYNC_GOVERNANCE): LogPrintf("CZnodeSync::SwitchToNextAsset -- Sync has finished\n"); nRequestedZnodeAssets = ZNODE_SYNC_FINISHED; // uiInterface.NotifyAdditionalDataSyncProgressChanged(1); @@ -189,9 +202,10 @@ void CZnodeSync::SwitchToNextAsset() activeZnode.ManageState(); TRY_LOCK(cs_vNodes, lockRecv); - if(!lockRecv) return; + if (!lockRecv) return; - BOOST_FOREACH(CNode* pnode, vNodes) { + BOOST_FOREACH(CNode * pnode, vNodes) + { netfulfilledman.AddFulfilledRequest(pnode->addr, "full-sync"); } @@ -201,26 +215,32 @@ void CZnodeSync::SwitchToNextAsset() nTimeAssetSyncStarted = GetTime(); } -std::string CZnodeSync::GetSyncStatus() -{ +std::string CZnodeSync::GetSyncStatus() { switch (znodeSync.nRequestedZnodeAssets) { - case ZNODE_SYNC_INITIAL: return _("Synchronization pending..."); - case ZNODE_SYNC_SPORKS: return _("Synchronizing sporks..."); - case ZNODE_SYNC_LIST: return _("Synchronizing znodes..."); - case ZNODE_SYNC_MNW: return _("Synchronizing znode payments..."); - case ZNODE_SYNC_GOVERNANCE: return _("Synchronizing governance objects..."); - case ZNODE_SYNC_FAILED: return _("Synchronization failed"); - case ZNODE_SYNC_FINISHED: return _("Synchronization finished"); - default: return ""; + case ZNODE_SYNC_INITIAL: + return _("Synchronization pending..."); + case ZNODE_SYNC_SPORKS: + return _("Synchronizing sporks..."); + case ZNODE_SYNC_LIST: + return _("Synchronizing znodes..."); + case ZNODE_SYNC_MNW: + return _("Synchronizing znode payments..."); + case ZNODE_SYNC_GOVERNANCE: + return _("Synchronizing governance objects..."); + case ZNODE_SYNC_FAILED: + return _("Synchronization failed"); + case ZNODE_SYNC_FINISHED: + return _("Synchronization finished"); + default: + return ""; } } -void CZnodeSync::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) -{ +void CZnodeSync::ProcessMessage(CNode *pfrom, std::string &strCommand, CDataStream &vRecv) { if (strCommand == NetMsgType::SYNCSTATUSCOUNT) { //Sync status count //do not care about stats if sync process finished or failed - if(IsSynced() || IsFailed()) return; + if (IsSynced() || IsFailed()) return; int nItemID; int nCount; @@ -230,12 +250,11 @@ void CZnodeSync::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStre } } -void CZnodeSync::ClearFulfilledRequests() -{ +void CZnodeSync::ClearFulfilledRequests() { TRY_LOCK(cs_vNodes, lockRecv); - if(!lockRecv) return; + if (!lockRecv) return; - BOOST_FOREACH(CNode* pnode, vNodes) + BOOST_FOREACH(CNode * pnode, vNodes) { netfulfilledman.RemoveFulfilledRequest(pnode->addr, "spork-sync"); netfulfilledman.RemoveFulfilledRequest(pnode->addr, "znode-list-sync"); @@ -244,28 +263,27 @@ void CZnodeSync::ClearFulfilledRequests() } } -void CZnodeSync::ProcessTick() -{ +void CZnodeSync::ProcessTick() { static int nTick = 0; - if(nTick++ % ZNODE_SYNC_TICK_SECONDS != 0) return; - if(!pCurrentBlockIndex) return; + if (nTick++ % ZNODE_SYNC_TICK_SECONDS != 0) return; + if (!pCurrentBlockIndex) return; //the actual count of znodes we have currently int nMnCount = mnodeman.CountZnodes(); - if(fDebug) LogPrintf("CZnodeSync::ProcessTick -- nTick %d nMnCount %d\n", nTick, nMnCount); + if (fDebug) LogPrintf("CZnodeSync::ProcessTick -- nTick %d nMnCount %d\n", nTick, nMnCount); // RESET SYNCING INCASE OF FAILURE { - if(IsSynced()) { + if (IsSynced()) { /* Resync if we lost all znodes from sleep/wake or failed to sync originally */ - if(nMnCount == 0) { + if (nMnCount == 0) { LogPrintf("CZnodeSync::ProcessTick -- WARNING: not enough data, restarting sync\n"); Reset(); } else { - std::vector vNodesCopy = CopyNodeVector(); + std::vector < CNode * > vNodesCopy = CopyNodeVector(); // governance.RequestGovernanceObjectVotes(vNodesCopy); ReleaseNodeVector(vNodesCopy); return; @@ -273,8 +291,8 @@ void CZnodeSync::ProcessTick() } //try syncing again - if(IsFailed()) { - if(nTimeLastFailure + (1*60) < GetTime()) { // 1 minute cooldown after failed sync + if (IsFailed()) { + if (nTimeLastFailure + (1 * 60) < GetTime()) { // 1 minute cooldown after failed sync Reset(); } return; @@ -282,14 +300,15 @@ void CZnodeSync::ProcessTick() } // INITIAL SYNC SETUP / LOG REPORTING - double nSyncProgress = double(nRequestedZnodeAttempt + (nRequestedZnodeAssets - 1) * 8) / (8*4); + double nSyncProgress = double(nRequestedZnodeAttempt + (nRequestedZnodeAssets - 1) * 8) / (8 * 4); LogPrintf("CZnodeSync::ProcessTick -- nTick %d nRequestedZnodeAssets %d nRequestedZnodeAttempt %d nSyncProgress %f\n", nTick, nRequestedZnodeAssets, nRequestedZnodeAttempt, nSyncProgress); // uiInterface.NotifyAdditionalDataSyncProgressChanged(nSyncProgress); // sporks synced but blockchain is not, wait until we're almost at a recent block to continue - if(Params().NetworkIDString() != CBaseChainParams::REGTEST && - !IsBlockchainSynced() && nRequestedZnodeAssets > ZNODE_SYNC_SPORKS) - { + if (Params().NetworkIDString() != CBaseChainParams::REGTEST && + !IsBlockchainSynced() && nRequestedZnodeAssets > ZNODE_SYNC_SPORKS) { + LogPrintf("IsBlockchainSynced()=%s", IsBlockchainSynced()); + LogPrintf("nRequestedZnodeAssets > ZNODE_SYNC_SPORKS=%s", nRequestedZnodeAssets > ZNODE_SYNC_SPORKS); LogPrintf("CZnodeSync::ProcessTick -- nTick %d nRequestedZnodeAssets %d nRequestedZnodeAttempt %d -- blockchain is not synced yet\n", nTick, nRequestedZnodeAssets, nRequestedZnodeAttempt); nTimeLastZnodeList = GetTime(); nTimeLastPaymentVote = GetTime(); @@ -297,30 +316,28 @@ void CZnodeSync::ProcessTick() return; } - if(nRequestedZnodeAssets == ZNODE_SYNC_INITIAL || - (nRequestedZnodeAssets == ZNODE_SYNC_SPORKS && IsBlockchainSynced())) - { + if (nRequestedZnodeAssets == ZNODE_SYNC_INITIAL || + (nRequestedZnodeAssets == ZNODE_SYNC_SPORKS && IsBlockchainSynced())) { SwitchToNextAsset(); } - std::vector vNodesCopy = CopyNodeVector(); + std::vector < CNode * > vNodesCopy = CopyNodeVector(); - BOOST_FOREACH(CNode* pnode, vNodesCopy) + BOOST_FOREACH(CNode * pnode, vNodesCopy) { // Don't try to sync any data from outbound "znode" connections - // they are temporary and should be considered unreliable for a sync process. // Inbound connection this early is most likely a "znode" connection // initialted from another node, so skip it too. - if(pnode->fZnode || (fZNode && pnode->fInbound)) continue; + if (pnode->fZnode || (fZNode && pnode->fInbound)) continue; // QUICK MODE (REGTEST ONLY!) - if(Params().NetworkIDString() == CBaseChainParams::REGTEST) - { - if(nRequestedZnodeAttempt <= 2) { + if (Params().NetworkIDString() == CBaseChainParams::REGTEST) { + if (nRequestedZnodeAttempt <= 2) { pnode->PushMessage(NetMsgType::GETSPORKS); //get current network sporks - } else if(nRequestedZnodeAttempt < 4) { + } else if (nRequestedZnodeAttempt < 4) { mnodeman.DsegUpdate(pnode); - } else if(nRequestedZnodeAttempt < 6) { + } else if (nRequestedZnodeAttempt < 6) { int nMnCount = mnodeman.CountZnodes(); pnode->PushMessage(NetMsgType::ZNODEPAYMENTSYNC, nMnCount); //sync payment votes SendGovernanceSyncRequest(pnode); @@ -334,7 +351,7 @@ void CZnodeSync::ProcessTick() // NORMAL NETWORK MODE - TESTNET/MAINNET { - if(netfulfilledman.HasFulfilledRequest(pnode->addr, "full-sync")) { + if (netfulfilledman.HasFulfilledRequest(pnode->addr, "full-sync")) { // We already fully synced from this node recently, // disconnect to free this connection slot for another peer. pnode->fDisconnect = true; @@ -344,7 +361,7 @@ void CZnodeSync::ProcessTick() // SPORK : ALWAYS ASK FOR SPORKS AS WE SYNC (we skip this mode now) - if(!netfulfilledman.HasFulfilledRequest(pnode->addr, "spork-sync")) { + if (!netfulfilledman.HasFulfilledRequest(pnode->addr, "spork-sync")) { // only request once from each peer netfulfilledman.AddFulfilledRequest(pnode->addr, "spork-sync"); // get current network sporks @@ -355,10 +372,10 @@ void CZnodeSync::ProcessTick() // MNLIST : SYNC ZNODE LIST FROM OTHER CONNECTED CLIENTS - if(nRequestedZnodeAssets == ZNODE_SYNC_LIST) { + if (nRequestedZnodeAssets == ZNODE_SYNC_LIST) { LogPrint("znode", "CZnodeSync::ProcessTick -- nTick %d nRequestedZnodeAssets %d nTimeLastZnodeList %lld GetTime() %lld diff %lld\n", nTick, nRequestedZnodeAssets, nTimeLastZnodeList, GetTime(), GetTime() - nTimeLastZnodeList); // check for timeout first - if(nTimeLastZnodeList < GetTime() - ZNODE_SYNC_TIMEOUT_SECONDS) { + if (nTimeLastZnodeList < GetTime() - ZNODE_SYNC_TIMEOUT_SECONDS) { LogPrintf("CZnodeSync::ProcessTick -- nTick %d nRequestedZnodeAssets %d -- timeout\n", nTick, nRequestedZnodeAssets); if (nRequestedZnodeAttempt == 0) { LogPrintf("CZnodeSync::ProcessTick -- ERROR: failed to sync %s\n", GetAssetName()); @@ -373,7 +390,7 @@ void CZnodeSync::ProcessTick() } // only request once from each peer - if(netfulfilledman.HasFulfilledRequest(pnode->addr, "znode-list-sync")) continue; + if (netfulfilledman.HasFulfilledRequest(pnode->addr, "znode-list-sync")) continue; netfulfilledman.AddFulfilledRequest(pnode->addr, "znode-list-sync"); if (pnode->nVersion < mnpayments.GetMinZnodePaymentsProto()) continue; @@ -387,12 +404,12 @@ void CZnodeSync::ProcessTick() // MNW : SYNC ZNODE PAYMENT VOTES FROM OTHER CONNECTED CLIENTS - if(nRequestedZnodeAssets == ZNODE_SYNC_MNW) { + if (nRequestedZnodeAssets == ZNODE_SYNC_MNW) { LogPrint("mnpayments", "CZnodeSync::ProcessTick -- nTick %d nRequestedZnodeAssets %d nTimeLastPaymentVote %lld GetTime() %lld diff %lld\n", nTick, nRequestedZnodeAssets, nTimeLastPaymentVote, GetTime(), GetTime() - nTimeLastPaymentVote); // check for timeout first // This might take a lot longer than ZNODE_SYNC_TIMEOUT_SECONDS minutes due to new blocks, // but that should be OK and it should timeout eventually. - if(nTimeLastPaymentVote < GetTime() - ZNODE_SYNC_TIMEOUT_SECONDS) { + if (nTimeLastPaymentVote < GetTime() - ZNODE_SYNC_TIMEOUT_SECONDS) { LogPrintf("CZnodeSync::ProcessTick -- nTick %d nRequestedZnodeAssets %d -- timeout\n", nTick, nRequestedZnodeAssets); if (nRequestedZnodeAttempt == 0) { LogPrintf("CZnodeSync::ProcessTick -- ERROR: failed to sync %s\n", GetAssetName()); @@ -409,7 +426,7 @@ void CZnodeSync::ProcessTick() // check for data // if mnpayments already has enough blocks and votes, switch to the next asset // try to fetch data from at least two peers though - if(nRequestedZnodeAttempt > 1 && mnpayments.IsEnoughData()) { + if (nRequestedZnodeAttempt > 1 && mnpayments.IsEnoughData()) { LogPrintf("CZnodeSync::ProcessTick -- nTick %d nRequestedZnodeAssets %d -- found enough data\n", nTick, nRequestedZnodeAssets); SwitchToNextAsset(); ReleaseNodeVector(vNodesCopy); @@ -417,10 +434,10 @@ void CZnodeSync::ProcessTick() } // only request once from each peer - if(netfulfilledman.HasFulfilledRequest(pnode->addr, "znode-payment-sync")) continue; + if (netfulfilledman.HasFulfilledRequest(pnode->addr, "znode-payment-sync")) continue; netfulfilledman.AddFulfilledRequest(pnode->addr, "znode-payment-sync"); - if(pnode->nVersion < mnpayments.GetMinZnodePaymentsProto()) continue; + if (pnode->nVersion < mnpayments.GetMinZnodePaymentsProto()) continue; nRequestedZnodeAttempt++; // ask node for all payment votes it has (new nodes will only return votes for future payments) @@ -434,13 +451,13 @@ void CZnodeSync::ProcessTick() // GOVOBJ : SYNC GOVERNANCE ITEMS FROM OUR PEERS - if(nRequestedZnodeAssets == ZNODE_SYNC_GOVERNANCE) { + if (nRequestedZnodeAssets == ZNODE_SYNC_GOVERNANCE) { LogPrint("gobject", "CZnodeSync::ProcessTick -- nTick %d nRequestedZnodeAssets %d nTimeLastGovernanceItem %lld GetTime() %lld diff %lld\n", nTick, nRequestedZnodeAssets, nTimeLastGovernanceItem, GetTime(), GetTime() - nTimeLastGovernanceItem); // check for timeout first - if(GetTime() - nTimeLastGovernanceItem > ZNODE_SYNC_TIMEOUT_SECONDS) { + if (GetTime() - nTimeLastGovernanceItem > ZNODE_SYNC_TIMEOUT_SECONDS) { LogPrintf("CZnodeSync::ProcessTick -- nTick %d nRequestedZnodeAssets %d -- timeout\n", nTick, nRequestedZnodeAssets); - if(nRequestedZnodeAttempt == 0) { + if (nRequestedZnodeAttempt == 0) { LogPrintf("CZnodeSync::ProcessTick -- WARNING: failed to sync %s\n", GetAssetName()); // it's kind of ok to skip this for now, hopefully we'll catch up later? } @@ -499,8 +516,7 @@ void CZnodeSync::ProcessTick() ReleaseNodeVector(vNodesCopy); } -void CZnodeSync::SendGovernanceSyncRequest(CNode* pnode) -{ +void CZnodeSync::SendGovernanceSyncRequest(CNode *pnode) { // if(pnode->nVersion >= GOVERNANCE_FILTER_PROTO_VERSION) { // CBloomFilter filter; // filter.clear(); @@ -512,7 +528,6 @@ void CZnodeSync::SendGovernanceSyncRequest(CNode* pnode) // } } -void CZnodeSync::UpdatedBlockTip(const CBlockIndex *pindex) -{ +void CZnodeSync::UpdatedBlockTip(const CBlockIndex *pindex) { pCurrentBlockIndex = pindex; } From 481ab049a261a08b9c8a51029c7ba4de3049fa93 Mon Sep 17 00:00:00 2001 From: "snguyen.ntu@gmail.com" Date: Tue, 21 Nov 2017 09:45:20 +0700 Subject: [PATCH 05/35] Fix exception for unknown znode command. Add mnsync rpc status. Add sync block with masternode in ProcessNewBlock() --- src/bitcoind.cpp | 8 ++ src/main.cpp | 41 ++++++ src/protocol.cpp | 331 ++++++++++++++++++++++++------------------- src/protocol.h | 4 + src/qt/bitcoin.cpp | 9 ++ src/rpc/misc.cpp | 39 +++++ src/rpc/rpcznode.cpp | 4 +- src/rpc/server.cpp | 1 + src/rpc/server.h | 1 + src/util.cpp | 1 + src/znode-sync.cpp | 5 +- src/znodeconfig.cpp | 7 +- 12 files changed, 295 insertions(+), 156 deletions(-) diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 0d6e27fa7c..529d16a699 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -17,6 +17,7 @@ #include "httpserver.h" #include "httprpc.h" #include "utilstrencodings.h" +#include "znodeconfig.h" #include #include @@ -118,6 +119,13 @@ bool AppInit(int argc, char* argv[]) return false; } + // parse masternode.conf + std::string strErr; + if(!znodeConfig.read(strErr)) { + fprintf(stderr,"Error reading znode configuration file: %s\n", strErr.c_str()); + return false; + } + // Command-line RPC bool fCommandLine = false; for (int i = 1; i < argc; i++) diff --git a/src/main.cpp b/src/main.cpp index 47aed1057d..163fb654a6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4837,6 +4837,8 @@ bool ProcessNewBlock(CValidationState &state, const CChainParams &chainparams, C return error("%s: ActivateBestChain failed", __func__); } + znodeSync.IsBlockchainSynced(true); + return true; } @@ -5850,6 +5852,44 @@ bool static AlreadyHave(const CInv &inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { case MSG_BLOCK: case MSG_WITNESS_BLOCK: return mapBlockIndex.count(inv.hash); + /* + Dash Related Inventory Messages + + -- + + We shouldn't update the sync times for each of the messages when we already have it. + We're going to be asking many nodes upfront for the full inventory list, so we'll get duplicates of these. + We want to only update the time on new hits, so that we can time out appropriately if needed. + */ + case MSG_TXLOCK_REQUEST: + return instantsend.AlreadyHave(inv.hash); + + case MSG_TXLOCK_VOTE: + return instantsend.AlreadyHave(inv.hash); + + case MSG_SPORK: + return mapSporks.count(inv.hash); + + case MSG_ZNODE_PAYMENT_VOTE: + return mnpayments.mapZnodePaymentVotes.count(inv.hash); + + case MSG_ZNODE_PAYMENT_BLOCK: + { + BlockMap::iterator mi = mapBlockIndex.find(inv.hash); + return mi != mapBlockIndex.end() && mnpayments.mapZnodeBlocks.find(mi->second->nHeight) != mnpayments.mapZnodeBlocks.end(); + } + + case MSG_ZNODE_ANNOUNCE: + return mnodeman.mapSeenZnodeBroadcast.count(inv.hash) && !mnodeman.IsMnbRecoveryRequested(inv.hash); + + case MSG_ZNODE_PING: + return mnodeman.mapSeenZnodePing.count(inv.hash); + + case MSG_DSTX: + return mapDarksendBroadcastTxes.count(inv.hash); + + case MSG_ZNODE_VERIFY: + return mnodeman.mapSeenZnodeVerification.count(inv.hash); } // Don't know what it is, just say we already got one return true; @@ -7432,6 +7472,7 @@ bool ProcessMessages(CNode *pfrom) { continue; } string strCommand = hdr.GetCommand(); + LogPrintf("ProcessMEssages() strCommand = %s\n", strCommand); // Message size unsigned int nMessageSize = hdr.nMessageSize; diff --git a/src/protocol.cpp b/src/protocol.cpp index 0c9af62b16..ad451c22af 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -9,133 +9,156 @@ #include "utilstrencodings.h" #ifndef WIN32 + # include + #endif namespace NetMsgType { -const char *VERSION="version"; -const char *VERACK="verack"; -const char *ADDR="addr"; -const char *INV="inv"; -const char *GETDATA="getdata"; -const char *MERKLEBLOCK="merkleblock"; -const char *GETBLOCKS="getblocks"; -const char *GETHEADERS="getheaders"; -const char *TX="tx"; -const char *HEADERS="headers"; -const char *BLOCK="block"; -const char *GETADDR="getaddr"; -const char *MEMPOOL="mempool"; -const char *PING="ping"; -const char *PONG="pong"; -const char *NOTFOUND="notfound"; -const char *FILTERLOAD="filterload"; -const char *FILTERADD="filteradd"; -const char *FILTERCLEAR="filterclear"; -const char *REJECT="reject"; -const char *SENDHEADERS="sendheaders"; -const char *FEEFILTER="feefilter"; -const char *SENDCMPCT="sendcmpct"; -const char *CMPCTBLOCK="cmpctblock"; -const char *GETBLOCKTXN="getblocktxn"; -const char *BLOCKTXN="blocktxn"; + const char *VERSION = "version"; + const char *VERACK = "verack"; + const char *ADDR = "addr"; + const char *INV = "inv"; + const char *GETDATA = "getdata"; + const char *MERKLEBLOCK = "merkleblock"; + const char *GETBLOCKS = "getblocks"; + const char *GETHEADERS = "getheaders"; + const char *TX = "tx"; + const char *HEADERS = "headers"; + const char *BLOCK = "block"; + const char *GETADDR = "getaddr"; + const char *MEMPOOL = "mempool"; + const char *PING = "ping"; + const char *PONG = "pong"; + const char *NOTFOUND = "notfound"; + const char *FILTERLOAD = "filterload"; + const char *FILTERADD = "filteradd"; + const char *FILTERCLEAR = "filterclear"; + const char *REJECT = "reject"; + const char *SENDHEADERS = "sendheaders"; + const char *FEEFILTER = "feefilter"; + const char *SENDCMPCT = "sendcmpct"; + const char *CMPCTBLOCK = "cmpctblock"; + const char *GETBLOCKTXN = "getblocktxn"; + const char *BLOCKTXN = "blocktxn"; //znode -const char *SPORK="spork"; -const char *GETSPORKS="getsporks"; -const char *ZNODEPAYMENTVOTE="mnw"; -const char *ZNODEPAYMENTBLOCK="mnwb"; -const char *ZNODEPAYMENTSYNC="mnget"; -const char *MNBUDGETSYNC="mnvs"; // depreciated since 12.1 -const char *MNBUDGETVOTE="mvote"; // depreciated since 12.1 -const char *MNBUDGETPROPOSAL="mprop"; // depreciated since 12.1 -const char *MNBUDGETFINAL="fbs"; // depreciated since 12.1 -const char *MNBUDGETFINALVOTE="fbvote"; // depreciated since 12.1 -const char *MNQUORUM="mn quorum"; // not implemented -const char *MNANNOUNCE="mnb"; -const char *MNPING="mnp"; -const char *DSACCEPT="dsa"; -const char *DSVIN="dsi"; -const char *DSFINALTX="dsf"; -const char *DSSIGNFINALTX="dss"; -const char *DSCOMPLETE="dsc"; -const char *DSSTATUSUPDATE="dssu"; -const char *DSTX="dstx"; -const char *DSQUEUE="dsq"; -const char *DSEG="dseg"; -const char *SYNCSTATUSCOUNT="ssc"; -const char *MNGOVERNANCESYNC="govsync"; -const char *MNGOVERNANCEOBJECT="govobj"; -const char *MNGOVERNANCEOBJECTVOTE="govobjvote"; -const char *MNVERIFY="mnv"; -const char *TXLOCKREQUEST="ix"; + const char *TXLOCKVOTE="txlvote"; + const char *SPORK = "spork"; + const char *GETSPORKS = "getsporks"; + const char *ZNODEPAYMENTVOTE = "mnw"; + const char *ZNODEPAYMENTBLOCK = "mnwb"; + const char *ZNODEPAYMENTSYNC = "mnget"; + const char *MNBUDGETSYNC = "mnvs"; // depreciated since 12.1 + const char *MNBUDGETVOTE = "mvote"; // depreciated since 12.1 + const char *MNBUDGETPROPOSAL = "mprop"; // depreciated since 12.1 + const char *MNBUDGETFINAL = "fbs"; // depreciated since 12.1 + const char *MNBUDGETFINALVOTE = "fbvote"; // depreciated since 12.1 + const char *MNQUORUM = "mn quorum"; // not implemented + const char *MNANNOUNCE = "mnb"; + const char *MNPING = "mnp"; + const char *DSACCEPT = "dsa"; + const char *DSVIN = "dsi"; + const char *DSFINALTX = "dsf"; + const char *DSSIGNFINALTX = "dss"; + const char *DSCOMPLETE = "dsc"; + const char *DSSTATUSUPDATE = "dssu"; + const char *DSTX = "dstx"; + const char *DSQUEUE = "dsq"; + const char *DSEG = "dseg"; + const char *SYNCSTATUSCOUNT = "ssc"; + const char *MNGOVERNANCESYNC = "govsync"; + const char *MNGOVERNANCEOBJECT = "govobj"; + const char *MNGOVERNANCEOBJECTVOTE = "govobjvote"; + const char *MNVERIFY = "mnv"; + const char *TXLOCKREQUEST = "ix"; }; +static const char *ppszTypeName[] = + { + "ERROR", // Should never occur + NetMsgType::TX, + NetMsgType::BLOCK, + "filtered block", // Should never occur + // Dash message types + // NOTE: include non-implmented here, we must keep this list in sync with enum in protocol.h + NetMsgType::TXLOCKREQUEST, + NetMsgType::TXLOCKVOTE, + NetMsgType::SPORK, + NetMsgType::ZNODEPAYMENTVOTE, + NetMsgType::ZNODEPAYMENTBLOCK, // reusing, was MNSCANERROR previousely, was NOT used in 12.0, we need this for inv + NetMsgType::MNQUORUM, // not implemented + NetMsgType::MNANNOUNCE, + NetMsgType::MNPING, + NetMsgType::DSTX, + NetMsgType::MNGOVERNANCEOBJECT, + NetMsgType::MNGOVERNANCEOBJECTVOTE, + NetMsgType::MNVERIFY, + }; /** All known message types. Keep this in the same order as the list of * messages above and in protocol.h. */ const static std::string allNetMessageTypes[] = { - NetMsgType::VERSION, - NetMsgType::VERACK, - NetMsgType::ADDR, - NetMsgType::INV, - NetMsgType::GETDATA, - NetMsgType::MERKLEBLOCK, - NetMsgType::GETBLOCKS, - NetMsgType::GETHEADERS, - NetMsgType::TX, - NetMsgType::HEADERS, - NetMsgType::BLOCK, - NetMsgType::GETADDR, - NetMsgType::MEMPOOL, - NetMsgType::PING, - NetMsgType::PONG, - NetMsgType::NOTFOUND, - NetMsgType::FILTERLOAD, - NetMsgType::FILTERADD, - NetMsgType::FILTERCLEAR, - NetMsgType::REJECT, - NetMsgType::SENDHEADERS, - NetMsgType::FEEFILTER, - NetMsgType::SENDCMPCT, - NetMsgType::CMPCTBLOCK, - NetMsgType::GETBLOCKTXN, - NetMsgType::BLOCKTXN, - //znode - NetMsgType::TXLOCKREQUEST, - NetMsgType::ZNODEPAYMENTVOTE, - NetMsgType::SPORK, - NetMsgType::GETSPORKS, - NetMsgType::ZNODEPAYMENTSYNC, - NetMsgType::MNANNOUNCE, - NetMsgType::MNPING, - NetMsgType::DSACCEPT, - NetMsgType::DSVIN, - NetMsgType::DSFINALTX, - NetMsgType::DSSIGNFINALTX, - NetMsgType::DSCOMPLETE, - NetMsgType::DSSTATUSUPDATE, - NetMsgType::DSTX, - NetMsgType::DSQUEUE, - NetMsgType::DSEG, - NetMsgType::SYNCSTATUSCOUNT, - NetMsgType::MNGOVERNANCESYNC, - NetMsgType::MNGOVERNANCEOBJECT, - NetMsgType::MNGOVERNANCEOBJECTVOTE, - NetMsgType::MNVERIFY, + NetMsgType::VERSION, + NetMsgType::VERACK, + NetMsgType::ADDR, + NetMsgType::INV, + NetMsgType::GETDATA, + NetMsgType::MERKLEBLOCK, + NetMsgType::GETBLOCKS, + NetMsgType::GETHEADERS, + NetMsgType::TX, + NetMsgType::HEADERS, + NetMsgType::BLOCK, + NetMsgType::GETADDR, + NetMsgType::MEMPOOL, + NetMsgType::PING, + NetMsgType::PONG, + NetMsgType::NOTFOUND, + NetMsgType::FILTERLOAD, + NetMsgType::FILTERADD, + NetMsgType::FILTERCLEAR, + NetMsgType::REJECT, + NetMsgType::SENDHEADERS, + NetMsgType::FEEFILTER, + NetMsgType::SENDCMPCT, + NetMsgType::CMPCTBLOCK, + NetMsgType::GETBLOCKTXN, + NetMsgType::BLOCKTXN, + //znode + NetMsgType::TXLOCKREQUEST, + NetMsgType::ZNODEPAYMENTVOTE, + NetMsgType::SPORK, + NetMsgType::GETSPORKS, + NetMsgType::ZNODEPAYMENTSYNC, + NetMsgType::MNANNOUNCE, + NetMsgType::MNPING, + NetMsgType::DSACCEPT, + NetMsgType::DSVIN, + NetMsgType::DSFINALTX, + NetMsgType::DSSIGNFINALTX, + NetMsgType::DSCOMPLETE, + NetMsgType::DSSTATUSUPDATE, + NetMsgType::DSTX, + NetMsgType::DSQUEUE, + NetMsgType::DSEG, + NetMsgType::SYNCSTATUSCOUNT, + NetMsgType::MNGOVERNANCESYNC, + NetMsgType::MNGOVERNANCEOBJECT, + NetMsgType::MNGOVERNANCEOBJECTVOTE, + NetMsgType::MNVERIFY, + }; -const static std::vector allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes)); +const static std::vector allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes + ARRAYLEN(allNetMessageTypes)); -CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn) -{ +CMessageHeader::CMessageHeader(const MessageStartChars &pchMessageStartIn) { memcpy(pchMessageStart, pchMessageStartIn, MESSAGE_START_SIZE); memset(pchCommand, 0, sizeof(pchCommand)); nMessageSize = -1; nChecksum = 0; } -CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn, const char* pszCommand, unsigned int nMessageSizeIn) -{ +CMessageHeader::CMessageHeader(const MessageStartChars &pchMessageStartIn, const char *pszCommand, unsigned int nMessageSizeIn) { memcpy(pchMessageStart, pchMessageStartIn, MESSAGE_START_SIZE); memset(pchCommand, 0, sizeof(pchCommand)); strncpy(pchCommand, pszCommand, COMMAND_SIZE); @@ -143,34 +166,28 @@ CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn, const nChecksum = 0; } -std::string CMessageHeader::GetCommand() const -{ +std::string CMessageHeader::GetCommand() const { return std::string(pchCommand, pchCommand + strnlen(pchCommand, COMMAND_SIZE)); } -bool CMessageHeader::IsValid(const MessageStartChars& pchMessageStartIn) const -{ +bool CMessageHeader::IsValid(const MessageStartChars &pchMessageStartIn) const { // Check start string if (memcmp(pchMessageStart, pchMessageStartIn, MESSAGE_START_SIZE) != 0) return false; // Check the command string for errors - for (const char* p1 = pchCommand; p1 < pchCommand + COMMAND_SIZE; p1++) - { - if (*p1 == 0) - { + for (const char *p1 = pchCommand; p1 < pchCommand + COMMAND_SIZE; p1++) { + if (*p1 == 0) { // Must be all zeros after the first zero for (; p1 < pchCommand + COMMAND_SIZE; p1++) if (*p1 != 0) return false; - } - else if (*p1 < ' ' || *p1 > 0x7E) + } else if (*p1 < ' ' || *p1 > 0x7E) return false; } // Message size - if (nMessageSize > MAX_SIZE) - { + if (nMessageSize > MAX_SIZE) { LogPrintf("CMessageHeader::IsValid(): (%s, %u bytes) nMessageSize > MAX_SIZE\n", GetCommand(), nMessageSize); return false; } @@ -179,64 +196,80 @@ bool CMessageHeader::IsValid(const MessageStartChars& pchMessageStartIn) const } - -CAddress::CAddress() : CService() -{ +CAddress::CAddress() : CService() { Init(); } -CAddress::CAddress(CService ipIn, ServiceFlags nServicesIn) : CService(ipIn) -{ +CAddress::CAddress(CService ipIn, ServiceFlags nServicesIn) : CService(ipIn) { Init(); nServices = nServicesIn; } -void CAddress::Init() -{ +void CAddress::Init() { nServices = NODE_NONE; nTime = 100000000; } -CInv::CInv() -{ +CInv::CInv() { type = 0; hash.SetNull(); } -CInv::CInv(int typeIn, const uint256& hashIn) -{ +CInv::CInv(int typeIn, const uint256 &hashIn) { type = typeIn; hash = hashIn; } -bool operator<(const CInv& a, const CInv& b) -{ +CInv::CInv(const std::string &strType, const uint256 &hashIn) { + unsigned int i; + for (i = 1; i < ARRAYLEN(ppszTypeName); i++) { + if (strType == ppszTypeName[i]) { + type = i; + break; + } + } + if (i == ARRAYLEN(ppszTypeName)) + throw std::out_of_range(strprintf("CInv::CInv(string, uint256): unknown type '%s'", strType)); + hash = hashIn; +} + +bool operator<(const CInv &a, const CInv &b) { return (a.type < b.type || (a.type == b.type && a.hash < b.hash)); } -std::string CInv::GetCommand() const -{ - std::string cmd; - if (type & MSG_WITNESS_FLAG) - cmd.append("witness-"); - int masked = type & MSG_TYPE_MASK; - switch (masked) - { - case MSG_TX: return cmd.append(NetMsgType::TX); - case MSG_BLOCK: return cmd.append(NetMsgType::BLOCK); - case MSG_FILTERED_BLOCK: return cmd.append(NetMsgType::MERKLEBLOCK); - case MSG_CMPCT_BLOCK: return cmd.append(NetMsgType::CMPCTBLOCK); - default: +bool CInv::IsKnownType() const { + return (type >= 1 && type < (int) ARRAYLEN(ppszTypeName)); +} + +//std::string CInv::GetCommand() const +//{ +// std::string cmd; +// if (type & MSG_WITNESS_FLAG) +// cmd.append("witness-"); +// int masked = type & MSG_TYPE_MASK; +// switch (masked) +// { +// case MSG_TX: return cmd.append(NetMsgType::TX); +// case MSG_BLOCK: return cmd.append(NetMsgType::BLOCK); +// case MSG_FILTERED_BLOCK: return cmd.append(NetMsgType::MERKLEBLOCK); +// case MSG_CMPCT_BLOCK: return cmd.append(NetMsgType::CMPCTBLOCK); +// default: +// throw std::out_of_range(strprintf("CInv::GetCommand(): type=%d unknown type", type)); +// } +//} + +std::string CInv::GetCommand() const { + if (!IsKnownType()) { + LogPrintf("CInv::GetCommand(): type=%d unknown type\n", type); throw std::out_of_range(strprintf("CInv::GetCommand(): type=%d unknown type", type)); } + return allNetMessageTypes[type]; } -std::string CInv::ToString() const -{ +std::string CInv::ToString() const { return strprintf("%s %s", GetCommand(), hash.ToString()); } -const std::vector &getAllNetMessageTypes() -{ +const std::vector &getAllNetMessageTypes() { return allNetMessageTypesVec; } diff --git a/src/protocol.h b/src/protocol.h index 9fcbf54165..baf086a45e 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -244,6 +244,7 @@ extern const char *GETBLOCKTXN; */ extern const char *BLOCKTXN; +extern const char *TXLOCKVOTE; extern const char *SPORK; extern const char *GETSPORKS; extern const char *ZNODEPAYMENTVOTE; @@ -357,6 +358,7 @@ enum GetDataMsg MSG_ZNODE_PING, MSG_ZNODE_VERIFY, MSG_TXLOCK_REQUEST, + MSG_TXLOCK_VOTE, MSG_DSTX, DSQUEUE, }; @@ -366,6 +368,7 @@ class CInv { public: CInv(); + CInv(const std::string& strType, const uint256& hashIn); CInv(int typeIn, const uint256& hashIn); ADD_SERIALIZE_METHODS; @@ -379,6 +382,7 @@ class CInv friend bool operator<(const CInv& a, const CInv& b); + bool IsKnownType() const; std::string GetCommand() const; std::string ToString() const; diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 618c7109ae..1f05924ca4 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -19,6 +19,7 @@ #include "splashscreen.h" #include "utilitydialog.h" #include "winshutdownmonitor.h" +#include "znodeconfig.h" #ifdef ENABLE_WALLET #include "paymentserver.h" @@ -627,6 +628,14 @@ int main(int argc, char *argv[]) initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator); #ifdef ENABLE_WALLET + /// 7a. parse znode.conf + std::string strErr; + if(!znodeConfig.read(strErr)) { + QMessageBox::critical(0, QObject::tr("Zcoin Core"), + QObject::tr("Error reading masternode configuration file: %1").arg(strErr.c_str())); + return EXIT_FAILURE; + } + /// 8. URI IPC sending // - Do this early as we don't want to bother initializing if we are just calling IPC // - Do this *after* setting up the data directory, as the data directory hash is used in the name diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 9a902522a9..33cd08c1ec 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -14,6 +14,7 @@ #include "util.h" #include "utilstrencodings.h" #ifdef ENABLE_WALLET +#include "znode-sync.h" #include "wallet/wallet.h" #include "wallet/walletdb.h" #endif @@ -278,6 +279,44 @@ CScript _createmultisig_redeemScript(const UniValue& params) return result; } +UniValue mnsync(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "mnsync [status|next|reset]\n" + "Returns the sync status, updates to the next step or resets it entirely.\n" + ); + + std::string strMode = params[0].get_str(); + + if(strMode == "status") { + UniValue objStatus(UniValue::VOBJ); + objStatus.push_back(Pair("AssetID", znodeSync.GetAssetID())); + objStatus.push_back(Pair("AssetName", znodeSync.GetAssetName())); + objStatus.push_back(Pair("Attempt", znodeSync.GetAttempt())); + objStatus.push_back(Pair("IsBlockchainSynced", znodeSync.IsBlockchainSynced())); + objStatus.push_back(Pair("IsMasternodeListSynced", znodeSync.IsZnodeListSynced())); + objStatus.push_back(Pair("IsWinnersListSynced", znodeSync.IsWinnersListSynced())); + objStatus.push_back(Pair("IsSynced", znodeSync.IsSynced())); + objStatus.push_back(Pair("IsFailed", znodeSync.IsFailed())); + return objStatus; + } + + if(strMode == "next") + { + znodeSync.SwitchToNextAsset(); + return "sync updated to " + znodeSync.GetAssetName(); + } + + if(strMode == "reset") + { + znodeSync.Reset(); + return "success"; + } + return "failure"; +} + + UniValue createmultisig(const UniValue& params, bool fHelp) { if (fHelp || params.size() < 2 || params.size() > 2) diff --git a/src/rpc/rpcznode.cpp b/src/rpc/rpcznode.cpp index 4e932762b0..5e69fefb5a 100644 --- a/src/rpc/rpcznode.cpp +++ b/src/rpc/rpcznode.cpp @@ -256,7 +256,6 @@ UniValue znode(const UniValue ¶ms, bool fHelp) { bool fResult = CZnodeBroadcast::Create(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), strError, mnb); - statusObj.push_back(Pair("result", fResult ? "successful" : "failed")); if (fResult) { mnodeman.UpdateZnodeList(mnb); @@ -344,8 +343,7 @@ UniValue znode(const UniValue ¶ms, bool fHelp) { if (strCommand == "list-conf") { UniValue resultObj(UniValue::VOBJ); - BOOST_FOREACH(CZnodeConfig::CZnodeEntry - mne, znodeConfig.getEntries()) { + BOOST_FOREACH(CZnodeConfig::CZnodeEntry mne, znodeConfig.getEntries()) { CTxIn vin = CTxIn(uint256S(mne.getTxHash()), uint32_t(atoi(mne.getOutputIndex().c_str()))); CZnode *pmn = mnodeman.Find(vin); diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index e910e9d6f9..0a8f00f964 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -273,6 +273,7 @@ static const CRPCCommand vRPCCommands[] = { "control", "stop", &stop, true }, /* Dash features */ { "dash", "znode", &znode, true }, + { "dash", "mnsync", &mnsync, true }, { "dash", "znodelist", &znodelist, true }, { "dash", "znodebroadcast", &znodebroadcast, true }, { "dash", "getpoolinfo", &getpoolinfo, true }, diff --git a/src/rpc/server.h b/src/rpc/server.h index 7ba177b492..0aa0b5870d 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -195,6 +195,7 @@ extern UniValue spork(const UniValue& params, bool fHelp); extern UniValue znode(const UniValue& params, bool fHelp); extern UniValue znodelist(const UniValue& params, bool fHelp); extern UniValue znodebroadcast(const UniValue& params, bool fHelp); +extern UniValue mnsync(const UniValue& params, bool fHelp); extern void EnsureWalletIsUnlocked(); diff --git a/src/util.cpp b/src/util.cpp index 166e17fd87..5ebf16110a 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -567,6 +567,7 @@ boost::filesystem::path GetZnodeConfigFile() { boost::filesystem::path pathConfigFile(GetArg("-mnconf", "znode.conf")); if (!pathConfigFile.is_complete()) pathConfigFile = GetDataDir() / pathConfigFile; + LogPrintf("pathConfigFile=%s\n", pathConfigFile); return pathConfigFile; } diff --git a/src/znode-sync.cpp b/src/znode-sync.cpp index 057a960705..a4a475af2c 100644 --- a/src/znode-sync.cpp +++ b/src/znode-sync.cpp @@ -52,6 +52,7 @@ bool CZnodeSync::IsBlockchainSynced(bool fBlockAccepted) { // if the last call to this function was more than 60 minutes ago (client was in sleep mode) reset the sync process if (GetTime() - nTimeLastProcess > 60 * 60) { + LogPrintf("CZnodeSync::IsBlockchainSynced time-check fBlockchainSynced=%s\n", fBlockchainSynced); Reset(); fBlockchainSynced = false; } @@ -115,7 +116,7 @@ bool CZnodeSync::IsBlockchainSynced(bool fBlockAccepted) { ReleaseNodeVector(vNodesCopy); // wait for at least one new block to be accepted - LogPrintf("fFirstBlockAccepted=%s", fFirstBlockAccepted); + LogPrintf("fFirstBlockAccepted=%s\n", fFirstBlockAccepted); if (!fFirstBlockAccepted) return false; // same as !IsInitialBlockDownload() but no cs_main needed here @@ -127,7 +128,7 @@ bool CZnodeSync::IsBlockchainSynced(bool fBlockAccepted) { LogPrintf("pCurrentBlockIndex->nHeight=%s\n", pCurrentBlockIndex->nHeight); LogPrintf("GetTime() - nMaxBlockTime=%s\n", GetTime() - nMaxBlockTime); LogPrintf("Params().MaxTipAge()=%s\n", Params().MaxTipAge()); - LogPrintf("fBlockchainSynced\n", fBlockchainSynced); + LogPrintf("fBlockchainSynced=%s\n", fBlockchainSynced); return fBlockchainSynced; } diff --git a/src/znodeconfig.cpp b/src/znodeconfig.cpp index d24a5bb42c..f781a039c2 100644 --- a/src/znodeconfig.cpp +++ b/src/znodeconfig.cpp @@ -18,6 +18,7 @@ bool CZnodeConfig::read(std::string& strErr) { int linenumber = 1; boost::filesystem::path pathZnodeConfigFile = GetZnodeConfigFile(); boost::filesystem::ifstream streamConfig(pathZnodeConfigFile); + LogPrintf("pathZnodeConfigFile=%s\n", pathZnodeConfigFile); if (!streamConfig.good()) { FILE* configFile = fopen(pathZnodeConfigFile.string().c_str(), "a"); @@ -34,7 +35,7 @@ bool CZnodeConfig::read(std::string& strErr) { for(std::string line; std::getline(streamConfig, line); linenumber++) { if(line.empty()) continue; - + LogPrintf("Read line=%s\n", line); std::istringstream iss(line); std::string comment, alias, ip, privKey, txHash, outputIndex; @@ -43,7 +44,6 @@ bool CZnodeConfig::read(std::string& strErr) { iss.str(line); iss.clear(); } - if (!(iss >> alias >> ip >> privKey >> txHash >> outputIndex)) { iss.str(line); iss.clear(); @@ -65,6 +65,9 @@ bool CZnodeConfig::read(std::string& strErr) { return false; } int mainnetDefaultPort = Params(CBaseChainParams::MAIN).GetDefaultPort(); + LogPrintf("mainnetDefaultPort=%s\n", mainnetDefaultPort); + LogPrintf("Params().NetworkIDString()=%s\n", Params().NetworkIDString()); + LogPrintf("CBaseChainParams::MAIN=%s\n", CBaseChainParams::MAIN); if(Params().NetworkIDString() == CBaseChainParams::MAIN) { if(port != mainnetDefaultPort) { strErr = _("Invalid port detected in znode.conf") + "\n" + From db8769f1f8166fa2b207b4ba7d32dc9cf3ef1450 Mon Sep 17 00:00:00 2001 From: "snguyen.ntu@gmail.com" Date: Tue, 21 Nov 2017 10:09:15 +0700 Subject: [PATCH 06/35] Added Znode Build Instructions --- ZNODE.md | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 ZNODE.md diff --git a/ZNODE.md b/ZNODE.md new file mode 100644 index 0000000000..61c3a0a56a --- /dev/null +++ b/ZNODE.md @@ -0,0 +1,96 @@ +Znode Build Instructions and Notes +============================= +Version 1.4 +Date: 20th November 2017 + +Prerequisites +------------- + - Ubuntu 16.04+ + - Libraries to build from zcoin source + - Port 18618 is open + +Step 1. Build +---------------------- +**1.1.** Check out from source: + + git clone -b znode https://github.com/zcoinofficial/zcoin + +**1.2.** See (README.md) for instructions on building. + +Step 2. (Optional - only if firewall is running). Open port 18618 +---------------------- +**2.1.** Run: + + sudo ufw allow 18618 + sudo ufw default allow outgoing + sudo ufw enable + +Step 3. First run +---------------------- +**3.1.** Start daemon in testnet mode: + + zcoind -daemon -testnet + +**3.2.** Generate znodeprivkey: + + zcoin-cli znode genkey +(Store this key) + +**3.3.** Get wallet address: + + zcoin-cli getaccountaddress 0 + +**3.4.** Send to received address **exactly 1000 XZC** in **1 transaction**. +Wait for 15 confirmations. + +**3.5.** Stop daemon: + + zcoin-cli stop + +Step 4. Update config files +---------------------- +**4.1.** Create file **zcoin.conf** (in folder **~/.zcoin**) + + rpcuser=username + rpcpassword=password + rpcallowip=127.0.0.1 + testnet=1 + debug=1 + txindex=1 + daemon=1 + server=1 + listen=1 + maxconnections=24 + znode=1 + znodeprivkey=XXXXXXXXXXXXXXXXX ## Replace with your znode private key + externalIP=XXX.XXX.XXX.XXX ## Replace with your node external IP + +**4.2.** Create file **znode.conf** (in 2 folders **~/.zcoin** and **~/.zcoin/testnet3**) contains the following info: + o LABEL: A one word name you make up to call your node (ex. ZN1) + o IP:PORT: Your masternode VPS's IP, and the port is always 18168. + o ZNODEPRIVKEY: This is the result of your "masternode genkey" from earlier. + o TRANSACTION HASH: The collateral tx. hash from the 1000 XZC deposit. + o INDEX: The Index is always 0 or 1. +To get TRANSACTION HASH, run: + + zcoin-cli znode outputs +The output will look like: + + { "d6fd38868bb8f9958e34d5155437d009b72dfd33fc28874c87fd42e51c0f74fdb" : "0", } +Sample of znode.conf: + + ZN1 51.52.53.54:18168 XrxSr3fXpX3dZcU7CoiFuFWqeHYw83r28btCFfIHqf6zkMp1PZ4 d6fd38868bb8f9958e34d5155437d009b72dfd33fc28874c87fd42e51c0f74fdb 0 + +Step 5. Run a znode +---------------------- +**5.1.** Start znode: + + zcoin-cli znode start-alias