X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fmain.cpp;h=78d84d906484df6e2ec60c6bf33f52094092cfa6;hb=c23617fef3086e0d11ccfa288afa1c1d177bcdc8;hp=a19cf0e33e454459197fb419243ae7917227cfc7;hpb=575bdcde931c100288843a8f3540426490a78a60;p=novacoin.git diff --git a/src/main.cpp b/src/main.cpp index a19cf0e..78d84d9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,12 +1,12 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2011 The Bitcoin developers +// Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file license.txt or http://www.opensource.org/licenses/mit-license.php. -#include "headers.h" #include "checkpoints.h" #include "db.h" #include "net.h" #include "init.h" +#include "ui_interface.h" #include #include #include @@ -18,20 +18,13 @@ using namespace boost; // Global state // -// Name of client reported in the 'version' message. Report the same name -// for both bitcoind and bitcoin-qt, to make it harder for attackers to -// target servers or GUI users specifically. -const std::string CLIENT_NAME("bitcoin-qt"); - CCriticalSection cs_setpwalletRegistered; set setpwalletRegistered; CCriticalSection cs_main; -static map mapTransactions; -CCriticalSection cs_mapTransactions; +CTxMemPool mempool; unsigned int nTransactionsUpdated = 0; -map mapNextTx; map mapBlockIndex; uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); @@ -52,22 +45,17 @@ multimap mapOrphanBlocksByPrev; map mapOrphanTransactions; multimap mapOrphanTransactionsByPrev; +// Constant stuff for coinbase transactions we create: +CScript COINBASE_FLAGS; + +const string strMessageMagic = "Bitcoin Signed Message:\n"; double dHashesPerSec; int64 nHPSTimerStart; // Settings -int fGenerateBitcoins = false; int64 nTransactionFee = 0; -int fLimitProcessors = false; -int nLimitProcessors = 1; -int fMinimizeToTray = true; -int fMinimizeOnClose = true; -#if USE_UPNP -int fUseUPnP = true; -#else -int fUseUPnP = false; -#endif + ////////////////////////////////////////////////////////////////////////////// @@ -80,16 +68,16 @@ int fUseUPnP = false; void RegisterWallet(CWallet* pwalletIn) { - CRITICAL_BLOCK(cs_setpwalletRegistered) { + LOCK(cs_setpwalletRegistered); setpwalletRegistered.insert(pwalletIn); } } void UnregisterWallet(CWallet* pwalletIn) { - CRITICAL_BLOCK(cs_setpwalletRegistered) { + LOCK(cs_setpwalletRegistered); setpwalletRegistered.erase(pwalletIn); } } @@ -172,13 +160,14 @@ void static ResendWalletTransactions() // mapOrphanTransactions // -void static AddOrphanTx(const CDataStream& vMsg) +void AddOrphanTx(const CDataStream& vMsg) { CTransaction tx; CDataStream(vMsg) >> tx; uint256 hash = tx.GetHash(); if (mapOrphanTransactions.count(hash)) return; + CDataStream* pvMsg = mapOrphanTransactions[hash] = new CDataStream(vMsg); BOOST_FOREACH(const CTxIn& txin, tx.vin) mapOrphanTransactionsByPrev.insert(make_pair(txin.prevout.hash, pvMsg)); @@ -206,6 +195,23 @@ void static EraseOrphanTx(uint256 hash) mapOrphanTransactions.erase(hash); } +int LimitOrphanTxSize(int nMaxOrphans) +{ + int nEvicted = 0; + while (mapOrphanTransactions.size() > nMaxOrphans) + { + // Evict a random orphan: + std::vector randbytes(32); + RAND_bytes(&randbytes[0], 32); + uint256 randomhash(randbytes); + map::iterator it = mapOrphanTransactions.lower_bound(randomhash); + if (it == mapOrphanTransactions.end()) + it = mapOrphanTransactions.begin(); + EraseOrphanTx(it->first); + ++nEvicted; + } + return nEvicted; +} @@ -280,7 +286,7 @@ bool CTransaction::AreInputsStandard(const MapPrevTx& mapInputs) const if (IsCoinBase()) return true; // Coinbases don't use vin normally - for (int i = 0; i < vin.size(); i++) + for (unsigned int i = 0; i < vin.size(); i++) { const CTxOut& prev = GetOutputFor(vin[i], mapInputs); @@ -290,18 +296,33 @@ bool CTransaction::AreInputsStandard(const MapPrevTx& mapInputs) const const CScript& prevScript = prev.scriptPubKey; if (!Solver(prevScript, whichType, vSolutions)) return false; + int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions); + + // Transactions with extra stuff in their scriptSigs are + // non-standard. Note that this EvalScript() call will + // be quick, because if there are any operations + // beside "push data" in the scriptSig the + // IsStandard() call returns false + vector > stack; + if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0)) + return false; + if (whichType == TX_SCRIPTHASH) { - vector > stack; - - if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0)) - return false; if (stack.empty()) return false; CScript subscript(stack.back().begin(), stack.back().end()); - if (!::IsStandard(subscript)) + vector > vSolutions2; + txnouttype whichType2; + if (!Solver(subscript, whichType2, vSolutions2)) + return false; + if (whichType2 == TX_SCRIPTHASH) return false; + nArgsExpected += ScriptSigArgsExpected(whichType2, vSolutions2); } + + if (stack.size() != nArgsExpected) + return false; } return true; @@ -388,7 +409,7 @@ bool CTransaction::CheckTransaction() const if (vout.empty()) return DoS(10, error("CTransaction::CheckTransaction() : vout empty")); // Size limits - if (::GetSerializeSize(*this, SER_NETWORK) > MAX_BLOCK_SIZE) + if (::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) return DoS(100, error("CTransaction::CheckTransaction() : size limits failed")); // Check for negative or overflow output values @@ -428,40 +449,43 @@ bool CTransaction::CheckTransaction() const return true; } -bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMissingInputs) +bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs, + bool* pfMissingInputs) { if (pfMissingInputs) *pfMissingInputs = false; - if (!CheckTransaction()) - return error("AcceptToMemoryPool() : CheckTransaction failed"); + if (!tx.CheckTransaction()) + return error("CTxMemPool::accept() : CheckTransaction failed"); // Coinbase is only valid in a block, not as a loose transaction - if (IsCoinBase()) - return DoS(100, error("AcceptToMemoryPool() : coinbase as individual tx")); + if (tx.IsCoinBase()) + return tx.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx")); // To help v0.1.5 clients who would see it as a negative number - if ((int64)nLockTime > std::numeric_limits::max()) - return error("AcceptToMemoryPool() : not accepting nLockTime beyond 2038 yet"); + if ((int64)tx.nLockTime > std::numeric_limits::max()) + return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet"); // Rather not work on nonstandard transactions (unless -testnet) - if (!fTestNet && !IsStandard()) - return error("AcceptToMemoryPool() : nonstandard transaction type"); + if (!fTestNet && !tx.IsStandard()) + return error("CTxMemPool::accept() : nonstandard transaction type"); // Do we already have it? - uint256 hash = GetHash(); - CRITICAL_BLOCK(cs_mapTransactions) - if (mapTransactions.count(hash)) + uint256 hash = tx.GetHash(); + { + LOCK(cs); + if (mapTx.count(hash)) return false; + } if (fCheckInputs) if (txdb.ContainsTx(hash)) return false; // Check for conflicts with in-memory transactions CTransaction* ptxOld = NULL; - for (int i = 0; i < vin.size(); i++) + for (unsigned int i = 0; i < tx.vin.size(); i++) { - COutPoint outpoint = vin[i].prevout; + COutPoint outpoint = tx.vin[i].prevout; if (mapNextTx.count(outpoint)) { // Disable replacement feature for now @@ -473,11 +497,11 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi ptxOld = mapNextTx[outpoint].ptx; if (ptxOld->IsFinal()) return false; - if (!IsNewerThan(*ptxOld)) + if (!tx.IsNewerThan(*ptxOld)) return false; - for (int i = 0; i < vin.size(); i++) + for (unsigned int i = 0; i < tx.vin.size(); i++) { - COutPoint outpoint = vin[i].prevout; + COutPoint outpoint = tx.vin[i].prevout; if (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld) return false; } @@ -489,30 +513,30 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi { MapPrevTx mapInputs; map mapUnused; - if (!FetchInputs(txdb, mapUnused, false, false, mapInputs)) + bool fInvalid = false; + if (!tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid)) { + if (fInvalid) + return error("CTxMemPool::accept() : FetchInputs found invalid tx %s", hash.ToString().substr(0,10).c_str()); if (pfMissingInputs) *pfMissingInputs = true; - return error("AcceptToMemoryPool() : FetchInputs failed %s", hash.ToString().substr(0,10).c_str()); + return error("CTxMemPool::accept() : FetchInputs failed %s", hash.ToString().substr(0,10).c_str()); } // Check for non-standard pay-to-script-hash in inputs - if (!AreInputsStandard(mapInputs) && !fTestNet) - return error("AcceptToMemoryPool() : nonstandard transaction input"); + if (!tx.AreInputsStandard(mapInputs) && !fTestNet) + return error("CTxMemPool::accept() : nonstandard transaction input"); - int64 nFees = GetValueIn(mapInputs)-GetValueOut(); - int nSigOps = GetSigOpCount(mapInputs); - unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK); + // Note: if you modify this code to accept non-standard transactions, then + // you should add code here to check that the transaction does a + // reasonable number of ECDSA signature verifications. - // Don't accept it if it can't get into a block - if (nFees < GetMinFee(1000, true, GMF_RELAY)) - return error("AcceptToMemoryPool() : not enough fees"); + int64 nFees = tx.GetValueIn(mapInputs)-tx.GetValueOut(); + unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); - // Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service - // attacks disallow transactions with more than one SigOp per 65 bytes. - // 65 bytes because that is the minimum size of an ECDSA signature - if (nSigOps > nSize / 65 || nSize < 100) - return error("AcceptToMemoryPool() : transaction with out-of-bounds SigOpCount"); + // Don't accept it if it can't get into a block + if (nFees < tx.GetMinFee(1000, true, GMF_RELAY)) + return error("CTxMemPool::accept() : not enough fees"); // Continuously rate-limit free transactions // This mitigates 'penny-flooding' -- sending thousands of free transactions just to @@ -524,15 +548,15 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi static int64 nLastTime; int64 nNow = GetTime(); - CRITICAL_BLOCK(cs) { + LOCK(cs); // Use an exponentially decaying ~10-minute window: dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); nLastTime = nNow; // -limitfreerelay unit is thousand-bytes-per-minute // At default rate it would take over a month to fill 1GB - if (dFreeCount > GetArg("-limitfreerelay", 15)*10*1000 && !IsFromMe(*this)) - return error("AcceptToMemoryPool() : free transaction rejected by rate limiter"); + if (dFreeCount > GetArg("-limitfreerelay", 15)*10*1000 && !IsFromMe(tx)) + return error("CTxMemPool::accept() : free transaction rejected by rate limiter"); if (fDebug) printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); dFreeCount += nSize; @@ -541,23 +565,21 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!ConnectInputs(mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false)) + if (!tx.ConnectInputs(mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false)) { - if (pfMissingInputs) - *pfMissingInputs = true; - return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); + return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); } } // Store transaction in memory - CRITICAL_BLOCK(cs_mapTransactions) { + LOCK(cs); if (ptxOld) { - printf("AcceptToMemoryPool() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); - ptxOld->RemoveFromMemoryPool(); + printf("CTxMemPool::accept() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); + remove(*ptxOld); } - AddToMemoryPoolUnchecked(); + addUnchecked(tx); } ///// are we sure this is ok when loading transactions or restoring block txes @@ -565,46 +587,45 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi if (ptxOld) EraseFromWallets(ptxOld->GetHash()); - printf("AcceptToMemoryPool(): accepted %s\n", hash.ToString().substr(0,10).c_str()); + printf("CTxMemPool::accept() : accepted %s\n", hash.ToString().substr(0,10).c_str()); return true; } -bool CTransaction::AcceptToMemoryPool(bool fCheckInputs, bool* pfMissingInputs) +bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMissingInputs) { - CTxDB txdb("r"); - return AcceptToMemoryPool(txdb, fCheckInputs, pfMissingInputs); + return mempool.accept(txdb, *this, fCheckInputs, pfMissingInputs); } -uint64 nPooledTx = 0; - -bool CTransaction::AddToMemoryPoolUnchecked() +bool CTxMemPool::addUnchecked(CTransaction &tx) { - printf("AcceptToMemoryPoolUnchecked(): size %lu\n", mapTransactions.size()); + printf("addUnchecked(): size %lu\n", mapTx.size()); // Add to memory pool without checking anything. Don't call this directly, - // call AcceptToMemoryPool to properly check the transaction first. - CRITICAL_BLOCK(cs_mapTransactions) + // call CTxMemPool::accept to properly check the transaction first. { - uint256 hash = GetHash(); - mapTransactions[hash] = *this; - for (int i = 0; i < vin.size(); i++) - mapNextTx[vin[i].prevout] = CInPoint(&mapTransactions[hash], i); + LOCK(cs); + uint256 hash = tx.GetHash(); + mapTx[hash] = tx; + for (unsigned int i = 0; i < tx.vin.size(); i++) + mapNextTx[tx.vin[i].prevout] = CInPoint(&mapTx[hash], i); nTransactionsUpdated++; - ++nPooledTx; } return true; } -bool CTransaction::RemoveFromMemoryPool() +bool CTxMemPool::remove(CTransaction &tx) { // Remove transaction from memory pool - CRITICAL_BLOCK(cs_mapTransactions) { - BOOST_FOREACH(const CTxIn& txin, vin) - mapNextTx.erase(txin.prevout); - mapTransactions.erase(GetHash()); - nTransactionsUpdated++; - --nPooledTx; + LOCK(cs); + uint256 hash = tx.GetHash(); + if (mapTx.count(hash)) + { + BOOST_FOREACH(const CTxIn& txin, tx.vin) + mapNextTx.erase(txin.prevout); + mapTx.erase(hash); + nTransactionsUpdated++; + } } return true; } @@ -672,15 +693,16 @@ bool CMerkleTx::AcceptToMemoryPool() bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs) { - CRITICAL_BLOCK(cs_mapTransactions) + { + LOCK(mempool.cs); // Add previous supporting transactions first BOOST_FOREACH(CMerkleTx& tx, vtxPrev) { if (!tx.IsCoinBase()) { uint256 hash = tx.GetHash(); - if (!mapTransactions.count(hash) && !txdb.ContainsTx(hash)) + if (!mempool.exists(hash) && !txdb.ContainsTx(hash)) tx.AcceptToMemoryPool(txdb, fCheckInputs); } } @@ -767,6 +789,11 @@ static const int64 nInterval = nTargetTimespan / nTargetSpacing; // unsigned int ComputeMinWork(unsigned int nBase, int64 nTime) { + // Testnet has min-difficulty blocks + // after nTargetSpacing*2 time between blocks: + if (fTestNet && nTime > nTargetSpacing*2) + return bnProofOfWorkLimit.GetCompact(); + CBigNum bnResult; bnResult.SetCompact(nBase); while (nTime > 0 && bnResult < bnProofOfWorkLimit) @@ -781,16 +808,36 @@ unsigned int ComputeMinWork(unsigned int nBase, int64 nTime) return bnResult.GetCompact(); } -unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast) +unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlock *pblock) { + unsigned int nProofOfWorkLimit = bnProofOfWorkLimit.GetCompact(); // Genesis block if (pindexLast == NULL) - return bnProofOfWorkLimit.GetCompact(); + return nProofOfWorkLimit; // Only change once per interval if ((pindexLast->nHeight+1) % nInterval != 0) + { + // Special rules for testnet after 15 Feb 2012: + if (fTestNet && pblock->nTime > 1329264000) + { + // If the new block's timestamp is more than 2* 10 minutes + // then allow mining of a min-difficulty block. + if (pblock->nTime - pindexLast->nTime > nTargetSpacing*2) + return nProofOfWorkLimit; + else + { + // Return the last non-special-min-difficulty-rules-block + const CBlockIndex* pindex = pindexLast; + while (pindex->pprev && pindex->nHeight % nInterval != 0 && pindex->nBits == nProofOfWorkLimit) + pindex = pindex->pprev; + return pindex->nBits; + } + } + return pindexLast->nBits; + } // Go back by what we want to be 14 days worth of blocks const CBlockIndex* pindexFirst = pindexLast; @@ -875,6 +922,15 @@ void static InvalidChainFound(CBlockIndex* pindexNew) printf("InvalidChainFound: WARNING: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.\n"); } +void CBlock::UpdateTime(const CBlockIndex* pindexPrev) +{ + nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + + // Updating time can change work required on testnet: + if (fTestNet) + nBits = GetNextWorkRequired(pindexPrev, this); +} + @@ -912,20 +968,28 @@ bool CTransaction::DisconnectInputs(CTxDB& txdb) } // Remove transaction from index - if (!txdb.EraseTxIndex(*this)) - return error("DisconnectInputs() : EraseTxPos failed"); + // This can fail if a duplicate of this transaction was in a chain that got + // reorganized away. This is only possible if this transaction was completely + // spent, so erasing it would be a no-op anway. + txdb.EraseTxIndex(*this); return true; } bool CTransaction::FetchInputs(CTxDB& txdb, const map& mapTestPool, - bool fBlock, bool fMiner, MapPrevTx& inputsRet) + bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid) { + // FetchInputs can return false either because we just haven't seen some inputs + // (in which case the transaction should be stored as an orphan) + // or because the transaction is malformed (in which case the transaction should + // be dropped). If tx is definitely invalid, fInvalid will be set to true. + fInvalid = false; + if (IsCoinBase()) return true; // Coinbase transactions have no inputs to fetch. - for (int i = 0; i < vin.size(); i++) + for (unsigned int i = 0; i < vin.size(); i++) { COutPoint prevout = vin[i].prevout; if (inputsRet.count(prevout.hash)) @@ -952,11 +1016,11 @@ bool CTransaction::FetchInputs(CTxDB& txdb, const map& mapTes if (!fFound || txindex.pos == CDiskTxPos(1,1,1)) { // Get prev tx from single transactions in memory - CRITICAL_BLOCK(cs_mapTransactions) { - if (!mapTransactions.count(prevout.hash)) - return error("FetchInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); - txPrev = mapTransactions[prevout.hash]; + LOCK(mempool.cs); + if (!mempool.exists(prevout.hash)) + return error("FetchInputs() : %s mempool Tx prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); + txPrev = mempool.lookup(prevout.hash); } if (!fFound) txindex.vSpent.resize(txPrev.vout.size()); @@ -970,14 +1034,19 @@ bool CTransaction::FetchInputs(CTxDB& txdb, const map& mapTes } // Make sure all prevout.n's are valid: - for (int i = 0; i < vin.size(); i++) + for (unsigned int i = 0; i < vin.size(); i++) { const COutPoint prevout = vin[i].prevout; assert(inputsRet.count(prevout.hash) != 0); const CTxIndex& txindex = inputsRet[prevout.hash].first; const CTransaction& txPrev = inputsRet[prevout.hash].second; if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) + { + // Revisit this if/when transaction replacement is implemented and allows + // adding inputs: + fInvalid = true; return DoS(100, error("FetchInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str())); + } } return true; @@ -1002,7 +1071,7 @@ int64 CTransaction::GetValueIn(const MapPrevTx& inputs) const return 0; int64 nResult = 0; - for (int i = 0; i < vin.size(); i++) + for (unsigned int i = 0; i < vin.size(); i++) { nResult += GetOutputFor(vin[i], inputs).nValue; } @@ -1010,22 +1079,24 @@ int64 CTransaction::GetValueIn(const MapPrevTx& inputs) const } -int CTransaction::GetSigOpCount(const MapPrevTx& inputs) const +int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const { if (IsCoinBase()) return 0; int nSigOps = 0; - for (int i = 0; i < vin.size(); i++) + for (unsigned int i = 0; i < vin.size(); i++) { - nSigOps += GetOutputFor(vin[i], inputs).scriptPubKey.GetSigOpCount(vin[i].scriptSig); + const CTxOut& prevout = GetOutputFor(vin[i], inputs); + if (prevout.scriptPubKey.IsPayToScriptHash()) + nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig); } return nSigOps; } bool CTransaction::ConnectInputs(MapPrevTx inputs, map& mapTestPool, const CDiskTxPos& posThisTx, - const CBlockIndex* pindexBlock, bool fBlock, bool fMiner) + const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash) { // Take over previous transactions' spent pointers // fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain @@ -1035,7 +1106,7 @@ bool CTransaction::ConnectInputs(MapPrevTx inputs, { int64 nValueIn = 0; int64 nFees = 0; - for (int i = 0; i < vin.size(); i++) + for (unsigned int i = 0; i < vin.size(); i++) { COutPoint prevout = vin[i].prevout; assert(inputs.count(prevout.hash) > 0); @@ -1062,20 +1133,6 @@ bool CTransaction::ConnectInputs(MapPrevTx inputs, if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) return DoS(100, error("ConnectInputs() : txin values out of range")); - bool fStrictPayToScriptHash = true; - if (fBlock) - { - // To avoid being on the short end of a block-chain split, - // don't do secondary validation of pay-to-script-hash transactions - // until blocks with timestamps after paytoscripthashtime: - int64 nEvalSwitchTime = GetArg("paytoscripthashtime", 1329264000); // Feb 15, 2012 - fStrictPayToScriptHash = (pindexBlock->nTime >= nEvalSwitchTime); - } - // if !fBlock, then always be strict-- don't accept - // invalid-under-new-rules pay-to-script-hash transactions into - // our memory pool (don't relay them, don't include them - // in blocks we mine). - // Skip ECDSA signature verification when connecting blocks (fBlock=true) // before the last blockchain checkpoint. This is safe because block merkle hashes are // still computed and checked, and any change will be caught at the next checkpoint. @@ -1083,7 +1140,14 @@ bool CTransaction::ConnectInputs(MapPrevTx inputs, { // Verify signature if (!VerifySignature(txPrev, *this, i, fStrictPayToScriptHash, 0)) + { + // only during transition phase for P2SH: do not invoke anti-DoS code for + // potentially old clients relaying bad P2SH transactions + if (fStrictPayToScriptHash && VerifySignature(txPrev, *this, i, false, 0)) + return error("ConnectInputs() : %s P2SH VerifySignature failed", GetHash().ToString().substr(0,10).c_str()); + return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str())); + } } // Mark outpoints as spent @@ -1118,16 +1182,16 @@ bool CTransaction::ClientConnectInputs() return false; // Take over previous transactions' spent pointers - CRITICAL_BLOCK(cs_mapTransactions) { + LOCK(mempool.cs); int64 nValueIn = 0; - for (int i = 0; i < vin.size(); i++) + for (unsigned int i = 0; i < vin.size(); i++) { // Get prev tx from single transactions in memory COutPoint prevout = vin[i].prevout; - if (!mapTransactions.count(prevout.hash)) + if (!mempool.exists(prevout.hash)) return false; - CTransaction& txPrev = mapTransactions[prevout.hash]; + CTransaction& txPrev = mempool.lookup(prevout.hash); if (prevout.n >= txPrev.vout.size()) return false; @@ -1136,7 +1200,8 @@ bool CTransaction::ClientConnectInputs() if (!VerifySignature(txPrev, *this, i, true, 0)) return error("ConnectInputs() : VerifySignature failed"); - ///// this is redundant with the mapNextTx stuff, not sure which I want to get rid of + ///// this is redundant with the mempool.mapNextTx stuff, + ///// not sure which I want to get rid of ///// this has to go away now that posNext is gone // // Check for conflicts // if (!txPrev.vout[prevout.n].posNext.IsNull()) @@ -1186,34 +1251,69 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) if (!CheckBlock()) return false; + // Do not allow blocks that contain transactions which 'overwrite' older transactions, + // unless those are already completely spent. + // If such overwrites are allowed, coinbases and transactions depending upon those + // can be duplicated to remove the ability to spend the first instance -- even after + // being sent to another address. + // See BIP30 and http://r6.ca/blog/20120206T005236Z.html for more information. + // This logic is not necessary for memory pool transactions, as AcceptToMemoryPool + // already refuses previously-known transaction id's entirely. + // This rule applies to all blocks whose timestamp is after March 15, 2012, 0:00 UTC. + // On testnet it is enabled as of februari 20, 2012, 0:00 UTC. + if (pindex->nTime > 1331769600 || (fTestNet && pindex->nTime > 1329696000)) + { + BOOST_FOREACH(CTransaction& tx, vtx) + { + CTxIndex txindexOld; + if (txdb.ReadTxIndex(tx.GetHash(), txindexOld)) + { + BOOST_FOREACH(CDiskTxPos &pos, txindexOld.vSpent) + if (pos.IsNull()) + return false; + } + } + } + + // BIP16 didn't become active until Apr 1 2012 (Feb 15 on testnet) + int64 nBIP16SwitchTime = fTestNet ? 1329264000 : 1333238400; + bool fStrictPayToScriptHash = (pindex->nTime >= nBIP16SwitchTime); + //// issue here: it doesn't know the version - unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK) - 1 + GetSizeOfCompactSize(vtx.size()); + unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - 1 + GetSizeOfCompactSize(vtx.size()); map mapQueuedChanges; int64 nFees = 0; int nSigOps = 0; BOOST_FOREACH(CTransaction& tx, vtx) { + nSigOps += tx.GetLegacySigOpCount(); + if (nSigOps > MAX_BLOCK_SIGOPS) + return DoS(100, error("ConnectBlock() : too many sigops")); + CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos); - nTxPos += ::GetSerializeSize(tx, SER_DISK); + nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); MapPrevTx mapInputs; if (!tx.IsCoinBase()) { - if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs)) + bool fInvalid; + if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs, fInvalid)) return false; - int nTxOps = tx.GetSigOpCount(mapInputs); - nSigOps += nTxOps; - if (nSigOps > MAX_BLOCK_SIGOPS) - return DoS(100, error("ConnectBlock() : too many sigops")); - // There is a different MAX_BLOCK_SIGOPS check in AcceptBlock(); - // a block must satisfy both to make it into the best-chain - // (AcceptBlock() is always called before ConnectBlock()) + if (fStrictPayToScriptHash) + { + // Add in sigops done by pay-to-script-hash inputs; + // this is to prevent a "rogue miner" from creating + // an incredibly-expensive-to-validate block. + nSigOps += tx.GetP2SHSigOpCount(mapInputs); + if (nSigOps > MAX_BLOCK_SIGOPS) + return DoS(100, error("ConnectBlock() : too many sigops")); + } nFees += tx.GetValueIn(mapInputs)-tx.GetValueOut(); - if (!tx.ConnectInputs(mapInputs, mapQueuedChanges, posThisTx, pindex, true, false)) + if (!tx.ConnectInputs(mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fStrictPayToScriptHash)) return false; } @@ -1276,6 +1376,9 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) vConnect.push_back(pindex); reverse(vConnect.begin(), vConnect.end()); + printf("REORGANIZE: Disconnect %i blocks; %s..%s\n", vDisconnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexBest->GetBlockHash().ToString().substr(0,20).c_str()); + printf("REORGANIZE: Connect %i blocks; %s..%s\n", vConnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->GetBlockHash().ToString().substr(0,20).c_str()); + // Disconnect shorter branch vector vResurrect; BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) @@ -1284,7 +1387,7 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) if (!block.ReadFromDisk(pindex)) return error("Reorganize() : ReadFromDisk for disconnect failed"); if (!block.DisconnectBlock(txdb, pindex)) - return error("Reorganize() : DisconnectBlock failed"); + return error("Reorganize() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str()); // Queue memory transactions to resurrect BOOST_FOREACH(const CTransaction& tx, block.vtx) @@ -1294,7 +1397,7 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) // Connect longer branch vector vDelete; - for (int i = 0; i < vConnect.size(); i++) + for (unsigned int i = 0; i < vConnect.size(); i++) { CBlockIndex* pindex = vConnect[i]; CBlock block; @@ -1304,7 +1407,7 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) { // Invalid block txdb.TxnAbort(); - return error("Reorganize() : ConnectBlock failed"); + return error("Reorganize() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str()); } // Queue memory transactions to delete @@ -1334,7 +1437,9 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) // Delete redundant memory transactions that are in the connected branch BOOST_FOREACH(CTransaction& tx, vDelete) - tx.RemoveFromMemoryPool(); + mempool.remove(tx); + + printf("REORGANIZE: done\n"); return true; } @@ -1348,6 +1453,31 @@ runCommand(std::string strCommand) printf("runCommand error: system(%s) returned %d\n", strCommand.c_str(), nErr); } +// Called from inside SetBestChain: attaches a block to the new best chain being built +bool CBlock::SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew) +{ + uint256 hash = GetHash(); + + // Adding to current best branch + if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash)) + { + txdb.TxnAbort(); + InvalidChainFound(pindexNew); + return false; + } + if (!txdb.TxnCommit()) + return error("SetBestChain() : TxnCommit failed"); + + // Add to current best branch + pindexNew->pprev->pnext = pindexNew; + + // Delete redundant memory transactions + BOOST_FOREACH(CTransaction& tx, vtx) + mempool.remove(tx); + + return true; +} + bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) { uint256 hash = GetHash(); @@ -1362,32 +1492,50 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) } else if (hashPrevBlock == hashBestChain) { - // Adding to current best branch - if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash)) + if (!SetBestChainInner(txdb, pindexNew)) + return error("SetBestChain() : SetBestChainInner failed"); + } + else + { + // the first block in the new chain that will cause it to become the new best chain + CBlockIndex *pindexIntermediate = pindexNew; + + // list of blocks that need to be connected afterwards + std::vector vpindexSecondary; + + // Reorganize is costly in terms of db load, as it works in a single db transaction. + // Try to limit how much needs to be done inside + while (pindexIntermediate->pprev && pindexIntermediate->pprev->bnChainWork > pindexBest->bnChainWork) { - txdb.TxnAbort(); - InvalidChainFound(pindexNew); - return error("SetBestChain() : ConnectBlock failed"); + vpindexSecondary.push_back(pindexIntermediate); + pindexIntermediate = pindexIntermediate->pprev; } - if (!txdb.TxnCommit()) - return error("SetBestChain() : TxnCommit failed"); - // Add to current best branch - pindexNew->pprev->pnext = pindexNew; + if (!vpindexSecondary.empty()) + printf("Postponing %i reconnects\n", vpindexSecondary.size()); - // Delete redundant memory transactions - BOOST_FOREACH(CTransaction& tx, vtx) - tx.RemoveFromMemoryPool(); - } - else - { - // New best branch - if (!Reorganize(txdb, pindexNew)) + // Switch to new best branch + if (!Reorganize(txdb, pindexIntermediate)) { txdb.TxnAbort(); InvalidChainFound(pindexNew); return error("SetBestChain() : Reorganize failed"); } + + // Connect futher blocks + BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vpindexSecondary) + { + CBlock block; + if (!block.ReadFromDisk(pindex)) + { + printf("SetBestChain() : ReadFromDisk failed\n"); + break; + } + txdb.TxnBegin(); + // errors now are not fatal, we still did a reorganisation to a new chain in a valid way + if (!block.SetBestChainInner(txdb, pindex)) + break; + } } // Update best block in wallet (so we can detect restored wallets) @@ -1474,7 +1622,7 @@ bool CBlock::CheckBlock() const // that can be verified before saving an orphan block. // Size limits - if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK) > MAX_BLOCK_SIZE) + if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) return DoS(100, error("CheckBlock() : size limits failed")); // Check proof of work matches claimed amount @@ -1488,7 +1636,7 @@ bool CBlock::CheckBlock() const // First transaction must be coinbase, the rest must not be if (vtx.empty() || !vtx[0].IsCoinBase()) return DoS(100, error("CheckBlock() : first tx is not coinbase")); - for (int i = 1; i < vtx.size(); i++) + for (unsigned int i = 1; i < vtx.size(); i++) if (vtx[i].IsCoinBase()) return DoS(100, error("CheckBlock() : more than one coinbase")); @@ -1497,9 +1645,6 @@ bool CBlock::CheckBlock() const if (!tx.CheckTransaction()) return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed")); - // Pre-pay-to-script-hash (before version 0.6), this is how sigops - // were counted; there is another check in ConnectBlock when - // transaction inputs are fetched to count pay-to-script-hash sigops: int nSigOps = 0; BOOST_FOREACH(const CTransaction& tx, vtx) { @@ -1530,7 +1675,7 @@ bool CBlock::AcceptBlock() int nHeight = pindexPrev->nHeight+1; // Check proof of work - if (nBits != GetNextWorkRequired(pindexPrev)) + if (nBits != GetNextWorkRequired(pindexPrev, this)) return DoS(100, error("AcceptBlock() : incorrect proof of work")); // Check timestamp against prev @@ -1547,7 +1692,7 @@ bool CBlock::AcceptBlock() return DoS(100, error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight)); // Write block to history file - if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK))) + if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION))) return error("AcceptBlock() : out of disk space"); unsigned int nFile = -1; unsigned int nBlockPos = 0; @@ -1557,11 +1702,14 @@ bool CBlock::AcceptBlock() return error("AcceptBlock() : AddToBlockIndex failed"); // Relay inventory, but don't relay old inventory during initial block download + int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); if (hashBestChain == hash) - CRITICAL_BLOCK(cs_vNodes) - BOOST_FOREACH(CNode* pnode, vNodes) - if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 140700)) - pnode->PushInventory(CInv(MSG_BLOCK, hash)); + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) + pnode->PushInventory(CInv(MSG_BLOCK, hash)); + } return true; } @@ -1586,7 +1734,8 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime; if (deltaTime < 0) { - pfrom->Misbehaving(100); + if (pfrom) + pfrom->Misbehaving(100); return error("ProcessBlock() : block with timestamp before last checkpoint"); } CBigNum bnNewBlock; @@ -1595,7 +1744,8 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime)); if (bnNewBlock > bnRequired) { - pfrom->Misbehaving(100); + if (pfrom) + pfrom->Misbehaving(100); return error("ProcessBlock() : block with too little proof-of-work"); } } @@ -1622,7 +1772,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) // Recursively process any orphan blocks that depended on this one vector vWorkQueue; vWorkQueue.push_back(hash); - for (int i = 0; i < vWorkQueue.size(); i++) + for (unsigned int i = 0; i < vWorkQueue.size(); i++) { uint256 hashPrev = vWorkQueue[i]; for (multimap::iterator mi = mapOrphanBlocksByPrev.lower_bound(hashPrev); @@ -1660,8 +1810,8 @@ bool CheckDiskSpace(uint64 nAdditionalBytes) string strMessage = _("Warning: Disk space is low "); strMiscWarning = strMessage; printf("*** %s\n", strMessage.c_str()); - ThreadSafeMessageBox(strMessage, "Bitcoin", wxOK | wxICON_EXCLAMATION); - CreateThread(Shutdown, NULL); + ThreadSafeMessageBox(strMessage, "Bitcoin", wxOK | wxICON_EXCLAMATION | wxMODAL); + QueueShutdown(); return false; } return true; @@ -1671,7 +1821,7 @@ FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszM { if (nFile == -1) return NULL; - FILE* file = fopen(strprintf("%s/blk%04d.dat", GetDataDir().c_str(), nFile).c_str(), pszMode); + FILE* file = fopen((GetDataDir() / strprintf("blk%04d.dat", nFile)).string().c_str(), pszMode); if (!file) return NULL; if (nBlockPos != 0 && !strchr(pszMode, 'a') && !strchr(pszMode, 'w')) @@ -1846,7 +1996,7 @@ void PrintBlockTree() // put the main timechain first vector& vNext = mapNext[pindex]; - for (int i = 0; i < vNext.size(); i++) + for (unsigned int i = 0; i < vNext.size(); i++) { if (vNext[i]->pnext) { @@ -1856,7 +2006,7 @@ void PrintBlockTree() } // iterate children - for (int i = 0; i < vNext.size(); i++) + for (unsigned int i = 0; i < vNext.size(); i++) vStack.push_back(make_pair(nCol+i, vNext[i])); } } @@ -1901,8 +2051,8 @@ string GetWarnings(string strFor) } // Alerts - CRITICAL_BLOCK(cs_mapAlerts) { + LOCK(cs_mapAlerts); BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) { const CAlert& alert = item.second; @@ -1929,8 +2079,8 @@ bool CAlert::ProcessAlert() if (!IsInEffect()) return false; - CRITICAL_BLOCK(cs_mapAlerts) { + LOCK(cs_mapAlerts); // Cancel previous alerts for (map::iterator mi = mapAlerts.begin(); mi != mapAlerts.end();) { @@ -1986,8 +2136,21 @@ bool static AlreadyHave(CTxDB& txdb, const CInv& inv) { switch (inv.type) { - case MSG_TX: return mapTransactions.count(inv.hash) || mapOrphanTransactions.count(inv.hash) || txdb.ContainsTx(inv.hash); - case MSG_BLOCK: return mapBlockIndex.count(inv.hash) || mapOrphanBlocks.count(inv.hash); + case MSG_TX: + { + bool txInMap = false; + { + LOCK(mempool.cs); + txInMap = (mempool.exists(inv.hash)); + } + return txInMap || + mapOrphanTransactions.count(inv.hash) || + txdb.ContainsTx(inv.hash); + } + + case MSG_BLOCK: + return mapBlockIndex.count(inv.hash) || + mapOrphanBlocks.count(inv.hash); } // Don't know what it is, just say we already got one return true; @@ -2034,18 +2197,24 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CAddress addrFrom; uint64 nNonce = 1; vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe; + if (pfrom->nVersion < MIN_PROTO_VERSION) + { + // Since February 20, 2012, the protocol is initiated at version 209, + // and earlier versions are no longer supported + printf("partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString().c_str(), pfrom->nVersion); + pfrom->fDisconnect = true; + return false; + } + if (pfrom->nVersion == 10300) pfrom->nVersion = 300; - if (pfrom->nVersion >= 106 && !vRecv.empty()) + if (!vRecv.empty()) vRecv >> addrFrom >> nNonce; - if (pfrom->nVersion >= 106 && !vRecv.empty()) + if (!vRecv.empty()) vRecv >> pfrom->strSubVer; - if (pfrom->nVersion >= 209 && !vRecv.empty()) + if (!vRecv.empty()) vRecv >> pfrom->nStartingHeight; - if (pfrom->nVersion == 0) - return false; - // Disconnect if we connected to ourself if (nNonce == nLocalHostNonce && nNonce > 1) { @@ -2063,16 +2232,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) AddTimeData(pfrom->addr, nTime); // Change version - if (pfrom->nVersion >= 209) - pfrom->PushMessage("verack"); + pfrom->PushMessage("verack"); pfrom->vSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); - if (pfrom->nVersion < 209) - pfrom->vRecv.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); if (!pfrom->fInbound) { // Advertise our address - if (addrLocalHost.IsRoutable() && !fUseProxy) + if (!fNoListen && !fUseProxy && addrLocalHost.IsRoutable() && + !IsInitialBlockDownload()) { CAddress addr(addrLocalHost); addr.nTime = GetAdjustedTime(); @@ -2080,17 +2247,25 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } // Get recent addresses - if (pfrom->nVersion >= 31402 || mapAddresses.size() < 1000) + if (pfrom->nVersion >= CADDR_TIME_VERSION || addrman.size() < 1000) { pfrom->PushMessage("getaddr"); pfrom->fGetAddr = true; } + addrman.Good(pfrom->addr); + } else { + if (((CNetAddr)pfrom->addr) == (CNetAddr)addrFrom) + { + addrman.Add(addrFrom, addrFrom); + addrman.Good(addrFrom); + } } // Ask the first connected node for block updates static int nAskedForBlocks = 0; if (!pfrom->fClient && - (pfrom->nVersion < 32000 || pfrom->nVersion >= 32400) && + (pfrom->nVersion < NOBLKS_VERSION_START || + pfrom->nVersion >= NOBLKS_VERSION_END) && (nAskedForBlocks < 1 || vNodes.size() <= 1)) { nAskedForBlocks++; @@ -2098,9 +2273,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } // Relay alerts - CRITICAL_BLOCK(cs_mapAlerts) + { + LOCK(cs_mapAlerts); BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) item.second.RelayTo(pfrom); + } pfrom->fSuccessfullyConnected = true; @@ -2130,9 +2307,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vRecv >> vAddr; // Don't want addr from older versions unless seeding - if (pfrom->nVersion < 209) - return true; - if (pfrom->nVersion < 31402 && mapAddresses.size() > 1000) + if (pfrom->nVersion < CADDR_TIME_VERSION && addrman.size() > 1000) return true; if (vAddr.size() > 1000) { @@ -2141,8 +2316,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } // Store the new addresses - CAddrDB addrDB; - addrDB.TxnBegin(); int64 nNow = GetAdjustedTime(); int64 nSince = nNow - 10 * 60; BOOST_FOREACH(CAddress& addr, vAddr) @@ -2154,13 +2327,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) continue; if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) addr.nTime = nNow - 5 * 24 * 60 * 60; - AddAddress(addr, 2 * 60 * 60, &addrDB); pfrom->AddAddressKnown(addr); if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable()) { // Relay to a limited number of other nodes - CRITICAL_BLOCK(cs_vNodes) { + LOCK(cs_vNodes); // Use deterministic randomness to send to the same nodes for 24 hours // at a time so the setAddrKnowns of the chosen nodes prevent repeats static uint256 hashSalt; @@ -2172,7 +2344,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) multimap mapMix; BOOST_FOREACH(CNode* pnode, vNodes) { - if (pnode->nVersion < 31402) + if (pnode->nVersion < CADDR_TIME_VERSION) continue; unsigned int nPointer; memcpy(&nPointer, &pnode, sizeof(nPointer)); @@ -2186,7 +2358,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } } } - addrDB.TxnCommit(); // Save addresses (it's ok if this fails) + addrman.Add(vAddr, pfrom->addr, 2 * 60 * 60); if (vAddr.size() < 1000) pfrom->fGetAddr = false; } @@ -2203,8 +2375,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } CTxDB txdb("r"); - BOOST_FOREACH(const CInv& inv, vInv) + for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { + const CInv &inv = vInv[nInv]; + if (fShutdown) return true; pfrom->AddInventoryKnown(inv); @@ -2213,9 +2387,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (fDebug) printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new"); - if (!fAlreadyHave) + // Always request the last block in an inv bundle (even if we already have it), as it is the + // trigger for the other side to send further invs. If we are stuck on a (very long) side chain, + // this is necessary to connect earlier received orphan blocks to the chain again. + if (!fAlreadyHave || (inv.type == MSG_BLOCK && nInv==vInv.size()-1)) pfrom->AskFor(inv); - else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) + if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash])); // Track requests for our stuff @@ -2266,8 +2443,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) else if (inv.IsKnownType()) { // Send stream from relay memory - CRITICAL_BLOCK(cs_mapRelay) { + LOCK(cs_mapRelay); map::iterator mi = mapRelay.find(inv); if (mi != mapRelay.end()) pfrom->PushMessage(inv.GetCommand(), (*mi).second); @@ -2305,7 +2482,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); CBlock block; block.ReadFromDisk(pindex, true); - nBytes += block.GetSerializeSize(SER_NETWORK); + nBytes += block.GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION); if (--nLimit <= 0 || nBytes >= SendBufferSize()/2) { // When this block is requested, we'll send an inv that'll make them @@ -2342,8 +2519,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } vector vHeaders; - int nLimit = 2000 + locator.GetDistanceBack(); - printf("getheaders %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20).c_str(), nLimit); + int nLimit = 2000; + printf("getheaders %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20).c_str()); for (; pindex; pindex = pindex->pnext) { vHeaders.push_back(pindex->GetBlockHeader()); @@ -2358,6 +2535,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { vector vWorkQueue; CDataStream vMsg(vRecv); + CTxDB txdb("r"); CTransaction tx; vRecv >> tx; @@ -2365,7 +2543,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->AddInventoryKnown(inv); bool fMissingInputs = false; - if (tx.AcceptToMemoryPool(true, &fMissingInputs)) + if (tx.AcceptToMemoryPool(txdb, true, &fMissingInputs)) { SyncWithWallets(tx, NULL, true); RelayMessage(inv, vMsg); @@ -2373,7 +2551,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vWorkQueue.push_back(inv.hash); // Recursively process any orphan transactions that depended on this one - for (int i = 0; i < vWorkQueue.size(); i++) + for (unsigned int i = 0; i < vWorkQueue.size(); i++) { uint256 hashPrev = vWorkQueue[i]; for (multimap::iterator mi = mapOrphanTransactionsByPrev.lower_bound(hashPrev); @@ -2385,7 +2563,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CDataStream(vMsg) >> tx; CInv inv(MSG_TX, tx.GetHash()); - if (tx.AcceptToMemoryPool(true)) + if (tx.AcceptToMemoryPool(txdb, true)) { printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); SyncWithWallets(tx, NULL, true); @@ -2403,6 +2581,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { printf("storing orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); AddOrphanTx(vMsg); + + // DoS prevention: do not allow mapOrphanTransactions to grow unbounded + int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS); + if (nEvicted > 0) + printf("mapOrphan overflow, removed %d tx\n", nEvicted); } if (tx.nDoS) pfrom->Misbehaving(tx.nDoS); } @@ -2427,25 +2610,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) else if (strCommand == "getaddr") { - // Nodes rebroadcast an addr every 24 hours pfrom->vAddrToSend.clear(); - int64 nSince = GetAdjustedTime() - 3 * 60 * 60; // in the last 3 hours - CRITICAL_BLOCK(cs_mapAddresses) - { - unsigned int nCount = 0; - BOOST_FOREACH(const PAIRTYPE(vector, CAddress)& item, mapAddresses) - { - const CAddress& addr = item.second; - if (addr.nTime > nSince) - nCount++; - } - BOOST_FOREACH(const PAIRTYPE(vector, CAddress)& item, mapAddresses) - { - const CAddress& addr = item.second; - if (addr.nTime > nSince && GetRand(nCount) < 2500) - pfrom->PushAddress(addr); - } - } + vector vAddr = addrman.GetAddr(); + BOOST_FOREACH(const CAddress &addr, vAddr) + pfrom->PushAddress(addr); } @@ -2482,8 +2650,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vRecv >> hashReply; CRequestTracker tracker; - CRITICAL_BLOCK(pfrom->cs_mapRequests) { + LOCK(pfrom->cs_mapRequests); map::iterator mi = pfrom->mapRequests.find(hashReply); if (mi != pfrom->mapRequests.end()) { @@ -2498,6 +2666,23 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) else if (strCommand == "ping") { + if (pfrom->nVersion > BIP0031_VERSION) + { + uint64 nonce = 0; + vRecv >> nonce; + // Echo the message back with the nonce. This allows for two useful features: + // + // 1) A remote node can quickly check if the connection is operational + // 2) Remote nodes can measure the latency of the network thread. If this node + // is overloaded it won't respond to pings quickly and the remote node can + // avoid sending us more work, like chain download requests. + // + // The nonce stops the remote getting confused between different pings: without + // it, if the remote node sends a ping once per second and this node takes 5 + // seconds to respond to each, the 5th ping the remote sends would appear to + // return very quickly. + pfrom->PushMessage("pong", nonce); + } } @@ -2510,9 +2695,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { // Relay pfrom->setKnown.insert(alert.GetHash()); - CRITICAL_BLOCK(cs_vNodes) + { + LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) alert.RelayTo(pnode); + } } } @@ -2593,17 +2780,14 @@ bool ProcessMessages(CNode* pfrom) } // Checksum - if (vRecv.GetVersion() >= 209) + uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); + unsigned int nChecksum = 0; + memcpy(&nChecksum, &hash, sizeof(nChecksum)); + if (nChecksum != hdr.nChecksum) { - uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); - unsigned int nChecksum = 0; - memcpy(&nChecksum, &hash, sizeof(nChecksum)); - if (nChecksum != hdr.nChecksum) - { - printf("ProcessMessage(%s, %u bytes) : CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", - strCommand.c_str(), nMessageSize, nChecksum, hdr.nChecksum); - continue; - } + printf("ProcessMessage(%s, %u bytes) : CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", + strCommand.c_str(), nMessageSize, nChecksum, hdr.nChecksum); + continue; } // Copy message to its own buffer @@ -2614,8 +2798,10 @@ bool ProcessMessages(CNode* pfrom) bool fRet = false; try { - CRITICAL_BLOCK(cs_main) + { + LOCK(cs_main); fRet = ProcessMessage(pfrom, strCommand, vMsg); + } if (fShutdown) return true; } @@ -2653,33 +2839,38 @@ bool ProcessMessages(CNode* pfrom) bool SendMessages(CNode* pto, bool fSendTrickle) { - CRITICAL_BLOCK(cs_main) - { + TRY_LOCK(cs_main, lockMain); + if (lockMain) { // Don't send anything until we get their version message if (pto->nVersion == 0) return true; - // Keep-alive ping - if (pto->nLastSend && GetTime() - pto->nLastSend > 30 * 60 && pto->vSend.empty()) - pto->PushMessage("ping"); + // Keep-alive ping. We send a nonce of zero because we don't use it anywhere + // right now. + if (pto->nLastSend && GetTime() - pto->nLastSend > 30 * 60 && pto->vSend.empty()) { + if (pto->nVersion > BIP0031_VERSION) + pto->PushMessage("ping", 0); + else + pto->PushMessage("ping"); + } // Resend wallet transactions that haven't gotten in a block yet ResendWalletTransactions(); // Address refresh broadcast static int64 nLastRebroadcast; - if (GetTime() - nLastRebroadcast > 24 * 60 * 60) + if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60)) { - nLastRebroadcast = GetTime(); - CRITICAL_BLOCK(cs_vNodes) { + LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) { // Periodically clear setAddrKnown to allow refresh broadcasts - pnode->setAddrKnown.clear(); + if (nLastRebroadcast) + pnode->setAddrKnown.clear(); // Rebroadcast our address - if (addrLocalHost.IsRoutable() && !fUseProxy) + if (!fNoListen && !fUseProxy && addrLocalHost.IsRoutable()) { CAddress addr(addrLocalHost); addr.nTime = GetAdjustedTime(); @@ -2687,37 +2878,9 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } } } + nLastRebroadcast = GetTime(); } - // Clear out old addresses periodically so it's not too much work at once - static int64 nLastClear; - if (nLastClear == 0) - nLastClear = GetTime(); - if (GetTime() - nLastClear > 10 * 60 && vNodes.size() >= 3) - { - nLastClear = GetTime(); - CRITICAL_BLOCK(cs_mapAddresses) - { - CAddrDB addrdb; - int64 nSince = GetAdjustedTime() - 14 * 24 * 60 * 60; - for (map, CAddress>::iterator mi = mapAddresses.begin(); - mi != mapAddresses.end();) - { - const CAddress& addr = (*mi).second; - if (addr.nTime < nSince) - { - if (mapAddresses.size() < 1000 || GetTime() > nLastClear + 20) - break; - addrdb.EraseAddress(addr); - mapAddresses.erase(mi++); - } - else - mi++; - } - } - } - - // // Message: addr // @@ -2750,8 +2913,8 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // vector vInv; vector vInvWait; - CRITICAL_BLOCK(pto->cs_inventory) { + LOCK(pto->cs_inventory); vInv.reserve(pto->vInventoryToSend.size()); vInvWait.reserve(pto->vInventoryToSend.size()); BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend) @@ -2966,16 +3129,15 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) // Collect memory pool transactions into the block int64 nFees = 0; - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(cs_mapTransactions) { + LOCK2(cs_main, mempool.cs); CTxDB txdb("r"); // Priority order to process transactions list vOrphan; // list memory doesn't move map > mapDependers; multimap mapPriority; - for (map::iterator mi = mapTransactions.begin(); mi != mapTransactions.end(); ++mi) + for (map::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi) { CTransaction& tx = (*mi).second; if (tx.IsCoinBase() || !tx.IsFinal()) @@ -3013,7 +3175,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) } // Priority is sum(valuein * age) / txsize - dPriority /= ::GetSerializeSize(tx, SER_NETWORK); + dPriority /= ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); if (porphan) porphan->dPriority = dPriority; @@ -3033,8 +3195,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) map mapTestPool; uint64 nBlockSize = 1000; uint64 nBlockTx = 0; - int nBlockSigOps1 = 100; // pre-0.6 count of sigOps - int nBlockSigOps2 = 100; // post-0.6 count of sigOps + int nBlockSigOps = 100; while (!mapPriority.empty()) { // Take highest priority transaction off priority queue @@ -3043,13 +3204,13 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) mapPriority.erase(mapPriority.begin()); // Size limits - unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK); + unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN) continue; // Legacy limits on sigOps: - int nTxSigOps1 = tx.GetLegacySigOpCount(); - if (nBlockSigOps1 + nTxSigOps1 >= MAX_BLOCK_SIGOPS) + int nTxSigOps = tx.GetLegacySigOpCount(); + if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) continue; // Transaction fee required depends on block size @@ -3060,15 +3221,16 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) // because we're already processing them in order of dependency map mapTestPoolTmp(mapTestPool); MapPrevTx mapInputs; - if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs)) + bool fInvalid; + if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs, fInvalid)) continue; - int64 nFees = tx.GetValueIn(mapInputs)-tx.GetValueOut(); - if (nFees < nMinFee) + int64 nTxFees = tx.GetValueIn(mapInputs)-tx.GetValueOut(); + if (nTxFees < nMinFee) continue; - int nTxSigOps2 = tx.GetSigOpCount(mapInputs); - if (nBlockSigOps2 + nTxSigOps2 >= MAX_BLOCK_SIGOPS) + nTxSigOps += tx.GetP2SHSigOpCount(mapInputs); + if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) continue; if (!tx.ConnectInputs(mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, false, true)) @@ -3080,8 +3242,8 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) pblock->vtx.push_back(tx); nBlockSize += nTxSize; ++nBlockTx; - nBlockSigOps1 += nTxSigOps1; - nBlockSigOps2 += nTxSigOps2; + nBlockSigOps += nTxSigOps; + nFees += nTxFees; // Add transactions that depend on this one to the priority queue uint256 hash = tx.GetHash(); @@ -3109,8 +3271,8 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); pblock->hashMerkleRoot = pblock->BuildMerkleTree(); - pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); - pblock->nBits = GetNextWorkRequired(pindexPrev); + pblock->UpdateTime(pindexPrev); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock.get()); pblock->nNonce = 0; return pblock.release(); @@ -3169,7 +3331,7 @@ void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash FormatHashBlocks(&tmp.hash1, sizeof(tmp.hash1)); // Byte swap all the input buffer - for (int i = 0; i < sizeof(tmp)/4; i++) + for (unsigned int i = 0; i < sizeof(tmp)/4; i++) ((unsigned int*)&tmp)[i] = ByteReverse(((unsigned int*)&tmp)[i]); // Precalc the first half of the first hash, which stays constant @@ -3196,8 +3358,8 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str()); // Found a solution - CRITICAL_BLOCK(cs_main) { + LOCK(cs_main); if (pblock->hashPrevBlock != hashBestChain) return error("BitcoinMiner : generated block is stale"); @@ -3205,8 +3367,10 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) reservekey.KeepKey(); // Track how many getdata requests this block gets - CRITICAL_BLOCK(wallet.cs_wallet) + { + LOCK(wallet.cs_wallet); wallet.mapRequestCount[pblock->GetHash()] = 0; + } // Process this block the same as if we had received it from another node if (!ProcessBlock(NULL, pblock)) @@ -3218,6 +3382,10 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) void static ThreadBitcoinMiner(void* parg); +static bool fGenerateBitcoins = false; +static bool fLimitProcessors = false; +static int nLimitProcessors = -1; + void static BitcoinMiner(CWallet *pwallet) { printf("BitcoinMiner started\n"); @@ -3229,8 +3397,6 @@ void static BitcoinMiner(CWallet *pwallet) while (fGenerateBitcoins) { - if (AffinityBugWorkaround(ThreadBitcoinMiner)) - return; if (fShutdown) return; while (vNodes.empty() || IsInitialBlockDownload()) @@ -3267,6 +3433,7 @@ void static BitcoinMiner(CWallet *pwallet) FormatHashBuffers(pblock.get(), pmidstate, pdata, phash1); unsigned int& nBlockTime = *(unsigned int*)(pdata + 64 + 4); + unsigned int& nBlockBits = *(unsigned int*)(pdata + 64 + 8); unsigned int& nBlockNonce = *(unsigned int*)(pdata + 64 + 12); @@ -3289,7 +3456,7 @@ void static BitcoinMiner(CWallet *pwallet) // Check if something found if (nNonceFound != -1) { - for (int i = 0; i < sizeof(hash)/4; i++) + for (unsigned int i = 0; i < sizeof(hash)/4; i++) ((unsigned int*)&hash)[i] = ByteReverse(((unsigned int*)&hash)[i]); if (hash <= hashTarget) @@ -3317,21 +3484,19 @@ void static BitcoinMiner(CWallet *pwallet) if (GetTimeMillis() - nHPSTimerStart > 4000) { static CCriticalSection cs; - CRITICAL_BLOCK(cs) { + LOCK(cs); if (GetTimeMillis() - nHPSTimerStart > 4000) { dHashesPerSec = 1000.0 * nHashCounter / (GetTimeMillis() - nHPSTimerStart); nHPSTimerStart = GetTimeMillis(); nHashCounter = 0; - string strStatus = strprintf(" %.0f khash/s", dHashesPerSec/1000.0); - UIThreadCall(boost::bind(CalledSetStatusBar, strStatus, 0)); static int64 nLogTime; if (GetTime() - nLogTime > 30 * 60) { nLogTime = GetTime(); printf("%s ", DateTimeStrFormat("%x %H:%M", GetTime()).c_str()); - printf("hashmeter %3d CPUs %6.0f khash/s\n", vnThreadsRunning[3], dHashesPerSec/1000.0); + printf("hashmeter %3d CPUs %6.0f khash/s\n", vnThreadsRunning[THREAD_MINER], dHashesPerSec/1000.0); } } } @@ -3342,7 +3507,7 @@ void static BitcoinMiner(CWallet *pwallet) return; if (!fGenerateBitcoins) return; - if (fLimitProcessors && vnThreadsRunning[3] > nLimitProcessors) + if (fLimitProcessors && vnThreadsRunning[THREAD_MINER] > nLimitProcessors) return; if (vNodes.empty()) break; @@ -3354,8 +3519,14 @@ void static BitcoinMiner(CWallet *pwallet) break; // Update nTime every few seconds - pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); + pblock->UpdateTime(pindexPrev); nBlockTime = ByteReverse(pblock->nTime); + if (fTestNet) + { + // Changing pblock->nTime can change work required on testnet: + nBlockBits = ByteReverse(pblock->nBits); + hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + } } } } @@ -3365,34 +3536,33 @@ void static ThreadBitcoinMiner(void* parg) CWallet* pwallet = (CWallet*)parg; try { - vnThreadsRunning[3]++; + vnThreadsRunning[THREAD_MINER]++; BitcoinMiner(pwallet); - vnThreadsRunning[3]--; + vnThreadsRunning[THREAD_MINER]--; } catch (std::exception& e) { - vnThreadsRunning[3]--; + vnThreadsRunning[THREAD_MINER]--; PrintException(&e, "ThreadBitcoinMiner()"); } catch (...) { - vnThreadsRunning[3]--; + vnThreadsRunning[THREAD_MINER]--; PrintException(NULL, "ThreadBitcoinMiner()"); } - UIThreadCall(boost::bind(CalledSetStatusBar, "", 0)); nHPSTimerStart = 0; - if (vnThreadsRunning[3] == 0) + if (vnThreadsRunning[THREAD_MINER] == 0) dHashesPerSec = 0; - printf("ThreadBitcoinMiner exiting, %d threads remaining\n", vnThreadsRunning[3]); + printf("ThreadBitcoinMiner exiting, %d threads remaining\n", vnThreadsRunning[THREAD_MINER]); } void GenerateBitcoins(bool fGenerate, CWallet* pwallet) { - if (fGenerateBitcoins != fGenerate) - { - fGenerateBitcoins = fGenerate; - WriteSetting("fGenerateBitcoins", fGenerateBitcoins); - MainFrameRepaint(); - } - if (fGenerateBitcoins) + fGenerateBitcoins = fGenerate; + nLimitProcessors = GetArg("-genproclimit", -1); + if (nLimitProcessors == 0) + fGenerateBitcoins = false; + fLimitProcessors = (nLimitProcessors != -1); + + if (fGenerate) { int nProcessors = boost::thread::hardware_concurrency(); printf("%d processors\n", nProcessors); @@ -3400,7 +3570,7 @@ void GenerateBitcoins(bool fGenerate, CWallet* pwallet) nProcessors = 1; if (fLimitProcessors && nProcessors > nLimitProcessors) nProcessors = nLimitProcessors; - int nAddThreads = nProcessors - vnThreadsRunning[3]; + int nAddThreads = nProcessors - vnThreadsRunning[THREAD_MINER]; printf("Starting %d BitcoinMiner threads\n", nAddThreads); for (int i = 0; i < nAddThreads; i++) {