X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fmain.cpp;h=9a7ff16841ae915dd434860d126ad267980b292c;hb=23e7583a8c9a0dcee9cbbf3be8bfc453298773f0;hp=76e07837945c73c7ea2b45bd04653571baabf57b;hpb=774e9b6dbb2c967ec979351cc4dba82fc0102ee1;p=novacoin.git diff --git a/src/main.cpp b/src/main.cpp index 76e0783..9a7ff16 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,13 +1,14 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // 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" +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include "checkpoints.h" #include "db.h" #include "net.h" #include "init.h" -#include "cryptopp/sha.h" +#include "ui_interface.h" +#include #include #include @@ -23,15 +24,12 @@ set setpwalletRegistered; CCriticalSection cs_main; -static map mapTransactions; -CCriticalSection cs_mapTransactions; +CTxMemPool mempool; unsigned int nTransactionsUpdated = 0; -map mapNextTx; map mapBlockIndex; uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); -const int nInitialBlockThreshold = 120; // Regard blocks up until N-threshold as "initial download" CBlockIndex* pindexGenesisBlock = NULL; int nBestHeight = -1; CBigNum bnBestChainWork = 0; @@ -40,32 +38,24 @@ uint256 hashBestChain = 0; CBlockIndex* pindexBest = NULL; int64 nTimeBestReceived = 0; +CMedianFilter cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have + map mapOrphanBlocks; multimap mapOrphanBlocksByPrev; map mapOrphanTransactions; -multimap mapOrphanTransactionsByPrev; +map > 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 - - - - @@ -74,22 +64,26 @@ int fUseUPnP = false; // dispatching functions // +// These functions dispatch to one or all registered wallets + + 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); } } +// check whether the passed transaction is from us bool static IsFromMe(CTransaction& tx) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) @@ -98,6 +92,7 @@ bool static IsFromMe(CTransaction& tx) return false; } +// get the wallet transaction with the given hash (if it exists) bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) @@ -106,42 +101,49 @@ bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx) return false; } +// erases transaction with the given hash from all wallets void static EraseFromWallets(uint256 hash) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->EraseFromWallet(hash); } +// make sure all wallets know about the given transaction, in the given block void static SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate); } +// notify wallets about a new best chain void static SetBestChain(const CBlockLocator& loc) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->SetBestChain(loc); } +// notify wallets about an updated transaction void static UpdatedTransaction(const uint256& hashTx) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->UpdatedTransaction(hashTx); } +// dump all wallets void static PrintWallets(const CBlock& block) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->PrintWallet(block); } +// notify wallets about an incoming inventory (for request counts) void static Inventory(const uint256& hash) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->Inventory(hash); } +// ask wallets to resend their transactions void static ResendWalletTransactions() { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) @@ -159,17 +161,37 @@ void static ResendWalletTransactions() // mapOrphanTransactions // -void AddOrphanTx(const CDataStream& vMsg) +bool AddOrphanTx(const CDataStream& vMsg) { CTransaction tx; CDataStream(vMsg) >> tx; uint256 hash = tx.GetHash(); if (mapOrphanTransactions.count(hash)) - return; + return false; + + CDataStream* pvMsg = new CDataStream(vMsg); + + // Ignore big transactions, to avoid a + // send-big-orphans memory exhaustion attack. If a peer has a legitimate + // large transaction with a missing parent then we assume + // it will rebroadcast it later, after the parent transaction(s) + // have been mined or received. + // 10,000 orphans, each of which is at most 5,000 bytes big is + // at most 500 megabytes of orphans: + if (pvMsg->size() > 5000) + { + printf("ignoring large orphan tx (size: %u, hash: %s)\n", pvMsg->size(), hash.ToString().substr(0,10).c_str()); + delete pvMsg; + return false; + } - CDataStream* pvMsg = mapOrphanTransactions[hash] = new CDataStream(vMsg); + mapOrphanTransactions[hash] = pvMsg; BOOST_FOREACH(const CTxIn& txin, tx.vin) - mapOrphanTransactionsByPrev.insert(make_pair(txin.prevout.hash, pvMsg)); + mapOrphanTransactionsByPrev[txin.prevout.hash].insert(make_pair(hash, pvMsg)); + + printf("stored orphan tx %s (mapsz %u)\n", hash.ToString().substr(0,10).c_str(), + mapOrphanTransactions.size()); + return true; } void static EraseOrphanTx(uint256 hash) @@ -181,28 +203,21 @@ void static EraseOrphanTx(uint256 hash) CDataStream(*pvMsg) >> tx; BOOST_FOREACH(const CTxIn& txin, tx.vin) { - for (multimap::iterator mi = mapOrphanTransactionsByPrev.lower_bound(txin.prevout.hash); - mi != mapOrphanTransactionsByPrev.upper_bound(txin.prevout.hash);) - { - if ((*mi).second == pvMsg) - mapOrphanTransactionsByPrev.erase(mi++); - else - mi++; - } + mapOrphanTransactionsByPrev[txin.prevout.hash].erase(hash); + if (mapOrphanTransactionsByPrev[txin.prevout.hash].empty()) + mapOrphanTransactionsByPrev.erase(txin.prevout.hash); } delete pvMsg; mapOrphanTransactions.erase(hash); } -int LimitOrphanTxSize(int nMaxOrphans) +unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) { - int nEvicted = 0; + unsigned int nEvicted = 0; while (mapOrphanTransactions.size() > nMaxOrphans) { // Evict a random orphan: - std::vector randbytes(32); - RAND_bytes(&randbytes[0], 32); - uint256 randomhash(randbytes); + uint256 randomhash = GetRandHash(); map::iterator it = mapOrphanTransactions.lower_bound(randomhash); if (it == mapOrphanTransactions.end()) it = mapOrphanTransactions.begin(); @@ -251,6 +266,103 @@ bool CTransaction::ReadFromDisk(COutPoint prevout) return ReadFromDisk(txdb, prevout, txindex); } +bool CTransaction::IsStandard() const +{ + BOOST_FOREACH(const CTxIn& txin, vin) + { + // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG + // pay-to-script-hash, which is 3 ~80-byte signatures, 3 + // ~65-byte public keys, plus a few script ops. + if (txin.scriptSig.size() > 500) + return false; + if (!txin.scriptSig.IsPushOnly()) + return false; + } + BOOST_FOREACH(const CTxOut& txout, vout) + if (!::IsStandard(txout.scriptPubKey)) + return false; + return true; +} + +// +// Check transaction inputs, and make sure any +// pay-to-script-hash transactions are evaluating IsStandard scripts +// +// Why bother? To avoid denial-of-service attacks; an attacker +// can submit a standard HASH... OP_EQUAL transaction, +// which will get accepted into blocks. The redemption +// script can be anything; an attacker could use a very +// expensive-to-check-upon-redemption script like: +// DUP CHECKSIG DROP ... repeated 100 times... OP_1 +// +bool CTransaction::AreInputsStandard(const MapPrevTx& mapInputs) const +{ + if (IsCoinBase()) + return true; // Coinbases don't use vin normally + + for (unsigned int i = 0; i < vin.size(); i++) + { + const CTxOut& prev = GetOutputFor(vin[i], mapInputs); + + vector > vSolutions; + txnouttype whichType; + // get the scriptPubKey corresponding to this input: + const CScript& prevScript = prev.scriptPubKey; + if (!Solver(prevScript, whichType, vSolutions)) + return false; + int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions); + if (nArgsExpected < 0) + return false; + + // 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) + { + if (stack.empty()) + return false; + CScript subscript(stack.back().begin(), stack.back().end()); + vector > vSolutions2; + txnouttype whichType2; + if (!Solver(subscript, whichType2, vSolutions2)) + return false; + if (whichType2 == TX_SCRIPTHASH) + return false; + + int tmpExpected; + tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2); + if (tmpExpected < 0) + return false; + nArgsExpected += tmpExpected; + } + + if (stack.size() != (unsigned int)nArgsExpected) + return false; + } + + return true; +} + +unsigned int +CTransaction::GetLegacySigOpCount() const +{ + unsigned int nSigOps = 0; + BOOST_FOREACH(const CTxIn& txin, vin) + { + nSigOps += txin.scriptSig.GetSigOpCount(false); + } + BOOST_FOREACH(const CTxOut& txout, vout) + { + nSigOps += txout.scriptPubKey.GetSigOpCount(false); + } + return nSigOps; +} int CMerkleTx::SetMerkleBranch(const CBlock* pblock) @@ -278,10 +390,10 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) hashBlock = pblock->GetHash(); // Locate the transaction - for (nIndex = 0; nIndex < pblock->vtx.size(); nIndex++) + for (nIndex = 0; nIndex < (int)pblock->vtx.size(); nIndex++) if (pblock->vtx[nIndex] == *(CTransaction*)this) break; - if (nIndex == pblock->vtx.size()) + if (nIndex == (int)pblock->vtx.size()) { vMerkleBranch.clear(); nIndex = -1; @@ -314,24 +426,24 @@ bool CTransaction::CheckTransaction() const { // Basic checks that don't depend on any context if (vin.empty()) - return error("CTransaction::CheckTransaction() : vin empty"); + return DoS(10, error("CTransaction::CheckTransaction() : vin empty")); if (vout.empty()) - return error("CTransaction::CheckTransaction() : vout empty"); + return DoS(10, error("CTransaction::CheckTransaction() : vout empty")); // Size limits - if (::GetSerializeSize(*this, SER_NETWORK) > MAX_BLOCK_SIZE) - return error("CTransaction::CheckTransaction() : size limits failed"); + 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 int64 nValueOut = 0; BOOST_FOREACH(const CTxOut& txout, vout) { if (txout.nValue < 0) - return error("CTransaction::CheckTransaction() : txout.nValue negative"); + return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative")); if (txout.nValue > MAX_MONEY) - return error("CTransaction::CheckTransaction() : txout.nValue too high"); + return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high")); nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) - return error("CTransaction::CheckTransaction() : txout total out of range"); + return DoS(100, error("CTransaction::CheckTransaction() : txout total out of range")); } // Check for duplicate inputs @@ -346,52 +458,55 @@ bool CTransaction::CheckTransaction() const if (IsCoinBase()) { if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) - return error("CTransaction::CheckTransaction() : coinbase script size"); + return DoS(100, error("CTransaction::CheckTransaction() : coinbase script size")); } else { BOOST_FOREACH(const CTxIn& txin, vin) if (txin.prevout.IsNull()) - return error("CTransaction::CheckTransaction() : prevout is null"); + return DoS(10, error("CTransaction::CheckTransaction() : prevout is null")); } 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 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 > INT_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 (unsigned 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 @@ -403,11 +518,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 (unsigned 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; } @@ -420,29 +535,29 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi MapPrevTx mapInputs; map mapUnused; bool fInvalid = false; - if (!FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid)) + if (!tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid)) { if (fInvalid) - return error("AcceptToMemoryPool() : FetchInputs found invalid tx %s", hash.ToString().substr(0,10).c_str()); + 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()); } - // Safety limits - unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK); - // Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service - // attacks disallow transactions with more than one SigOp per 34 bytes. - // 34 bytes because a TxOut is: - // 20-byte address + 8 byte bitcoin amount + 5 bytes of ops + 1 byte script length - if (GetSigOpCount() > nSize / 34 || nSize < 100) - return error("AcceptToMemoryPool() : nonstandard transaction"); + // Check for non-standard pay-to-script-hash in inputs + if (!tx.AreInputsStandard(mapInputs) && !fTestNet) + return error("CTxMemPool::accept() : nonstandard transaction input"); - int64 nFees = GetValueIn(mapInputs)-GetValueOut(); + // 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. + + int64 nFees = tx.GetValueIn(mapInputs)-tx.GetValueOut(); + unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); // Don't accept it if it can't get into a block - if (nFees < GetMinFee(1000, true, true)) - return error("AcceptToMemoryPool() : not enough fees"); + 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 @@ -454,15 +569,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; @@ -471,21 +586,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)) { - 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 @@ -493,41 +608,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); } -bool CTransaction::AddToMemoryPoolUnchecked() +bool CTxMemPool::addUnchecked(CTransaction &tx) { + 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 (unsigned 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++; } 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++; + 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; } @@ -537,7 +656,7 @@ bool CTransaction::RemoveFromMemoryPool() -int CMerkleTx::GetDepthInMainChain(int& nHeightRet) const +int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const { if (hashBlock == 0 || nIndex == -1) return 0; @@ -558,7 +677,7 @@ int CMerkleTx::GetDepthInMainChain(int& nHeightRet) const fMerkleVerified = true; } - nHeightRet = pindex->nHeight; + pindexRet = pindex; return pindexBest->nHeight - pindex->nHeight + 1; } @@ -595,15 +714,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); } } @@ -612,7 +732,7 @@ bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs) return false; } -bool CWalletTx::AcceptWalletTransaction() +bool CWalletTx::AcceptWalletTransaction() { CTxDB txdb("r"); return AcceptWalletTransaction(txdb); @@ -788,9 +908,15 @@ bool CheckProofOfWork(uint256 hash, unsigned int nBits) return true; } +// Return maximum amount of blocks that other nodes claim to have +int GetNumBlocksOfPeers() +{ + return std::max(cPeerBlockCounts.median(), Checkpoints::GetTotalBlocksEstimate()); +} + bool IsInitialBlockDownload() { - if (pindexBest == NULL || nBestHeight < (Checkpoints::GetTotalBlocksEstimate()-nInitialBlockThreshold)) + if (pindexBest == NULL || nBestHeight < Checkpoints::GetTotalBlocksEstimate()) return true; static int64 nLastUpdate; static CBlockIndex* pindexLastBest; @@ -911,11 +1037,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()); @@ -940,7 +1066,7 @@ bool CTransaction::FetchInputs(CTxDB& txdb, const map& mapTes // Revisit this if/when transaction replacement is implemented and allows // adding inputs: fInvalid = true; - return 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 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())); } } @@ -974,12 +1100,12 @@ int64 CTransaction::GetValueIn(const MapPrevTx& inputs) const } -int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const +unsigned int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const { if (IsCoinBase()) return 0; - int nSigOps = 0; + unsigned int nSigOps = 0; for (unsigned int i = 0; i < vin.size(); i++) { const CTxOut& prevout = GetOutputFor(vin[i], inputs); @@ -1009,7 +1135,7 @@ bool CTransaction::ConnectInputs(MapPrevTx inputs, CTransaction& txPrev = inputs[prevout.hash].second; if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) - return error("ConnectInputs() : %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 DoS(100, error("ConnectInputs() : %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())); // If prev is coinbase, check that it's matured if (txPrev.IsCoinBase()) @@ -1017,25 +1143,43 @@ bool CTransaction::ConnectInputs(MapPrevTx inputs, if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile) return error("ConnectInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - pindex->nHeight); - // Check for conflicts (double-spend) - if (!txindex.vSpent[prevout.n].IsNull()) - return fMiner ? false : error("ConnectInputs() : %s prev tx already used at %s", GetHash().ToString().substr(0,10).c_str(), txindex.vSpent[prevout.n].ToString().c_str()); - // Check for negative or overflow input values nValueIn += txPrev.vout[prevout.n].nValue; if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return error("ConnectInputs() : txin values out of range"); + return DoS(100, error("ConnectInputs() : txin values out of range")); - // Verify signature - if (!VerifySignature(txPrev, *this, i, fStrictPayToScriptHash, 0)) + } + // The first loop above does all the inexpensive checks. + // Only if ALL inputs pass do we perform expensive ECDSA signature checks. + // Helps prevent CPU exhaustion attacks. + for (unsigned int i = 0; i < vin.size(); i++) + { + COutPoint prevout = vin[i].prevout; + assert(inputs.count(prevout.hash) > 0); + CTxIndex& txindex = inputs[prevout.hash].first; + CTransaction& txPrev = inputs[prevout.hash].second; + + // Check for conflicts (double-spend) + // 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 (!txindex.vSpent[prevout.n].IsNull()) + return fMiner ? false : error("ConnectInputs() : %s prev tx already used at %s", GetHash().ToString().substr(0,10).c_str(), txindex.vSpent[prevout.n].ToString().c_str()); + + // 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. + if (!(fBlock && (nBestHeight < Checkpoints::GetTotalBlocksEstimate()))) { - // only during transition phase for P2SH: do not invoke (external) - // 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()); + // 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 error("ConnectInputs() : %s 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 @@ -1049,15 +1193,15 @@ bool CTransaction::ConnectInputs(MapPrevTx inputs, } if (nValueIn < GetValueOut()) - return error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str()); + return DoS(100, error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str())); // Tally transaction fees int64 nTxFee = nValueIn - GetValueOut(); if (nTxFee < 0) - return error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str()); + return DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str())); nFees += nTxFee; if (!MoneyRange(nFees)) - return error("ConnectInputs() : nFees out of range"); + return DoS(100, error("ConnectInputs() : nFees out of range")); } return true; @@ -1070,16 +1214,16 @@ bool CTransaction::ClientConnectInputs() return false; // Take over previous transactions' spent pointers - CRITICAL_BLOCK(cs_mapTransactions) { + LOCK(mempool.cs); int64 nValueIn = 0; 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; @@ -1088,7 +1232,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()) @@ -1162,29 +1307,29 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) } } - // P2SH didn't become active until Apr 1 2012 (Feb 15 on testnet) - int64 nEvalSwitchTime = fTestNet ? 1329264000 : 1333238400; - bool fStrictPayToScriptHash = (pindex->nTime >= nEvalSwitchTime); + // 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; + unsigned int nSigOps = 0; BOOST_FOREACH(CTransaction& tx, vtx) { - nSigOps += tx.GetSigOpCount(); + nSigOps += tx.GetLegacySigOpCount(); if (nSigOps > MAX_BLOCK_SIGOPS) - return error("ConnectBlock() : too many 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); - bool fInvalid; MapPrevTx mapInputs; if (!tx.IsCoinBase()) { + bool fInvalid; if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs, fInvalid)) return false; @@ -1195,7 +1340,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) // an incredibly-expensive-to-validate block. nSigOps += tx.GetP2SHSigOpCount(mapInputs); if (nSigOps > MAX_BLOCK_SIGOPS) - return error("ConnectBlock() : too many sigops"); + return DoS(100, error("ConnectBlock() : too many sigops")); } nFees += tx.GetValueIn(mapInputs)-tx.GetValueOut(); @@ -1324,7 +1469,7 @@ 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"); @@ -1332,11 +1477,46 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) } +static void +runCommand(std::string strCommand) +{ + int nErr = ::system(strCommand.c_str()); + if (nErr) + 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(); - txdb.TxnBegin(); + if (!txdb.TxnBegin()) + return error("SetBestChain() : TxnBegin failed"); + if (pindexGenesisBlock == NULL && hash == hashGenesisBlock) { txdb.WriteHashBestChain(hash); @@ -1346,36 +1526,58 @@ 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; + } + if (!txdb.TxnBegin()) { + printf("SetBestChain() : TxnBegin 2 failed\n"); + break; + } + // 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) - if (!IsInitialBlockDownload()) + bool fIsInitialDownload = IsInitialBlockDownload(); + if (!fIsInitialDownload) { const CBlockLocator locator(pindexNew); ::SetBestChain(locator); @@ -1390,6 +1592,14 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) nTransactionsUpdated++; printf("SetBestChain: new best=%s height=%d work=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainWork.ToString().c_str()); + std::string strCmd = GetArg("-blocknotify", ""); + + if (!fIsInitialDownload && !strCmd.empty()) + { + boost::replace_all(strCmd, "%s", hashBestChain.GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } + return true; } @@ -1416,7 +1626,8 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) pindexNew->bnChainWork = (pindexNew->pprev ? pindexNew->pprev->bnChainWork : 0) + pindexNew->GetBlockWork(); CTxDB txdb; - txdb.TxnBegin(); + if (!txdb.TxnBegin()) + return false; txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew)); if (!txdb.TxnCommit()) return false; @@ -1449,12 +1660,12 @@ 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) - return error("CheckBlock() : size limits failed"); + 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 if (!CheckProofOfWork(GetHash(), nBits)) - return error("CheckBlock() : proof of work failed"); + return DoS(50, error("CheckBlock() : proof of work failed")); // Check timestamp if (GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) @@ -1462,23 +1673,37 @@ bool CBlock::CheckBlock() const // First transaction must be coinbase, the rest must not be if (vtx.empty() || !vtx[0].IsCoinBase()) - return error("CheckBlock() : first tx is not coinbase"); + return DoS(100, error("CheckBlock() : first tx is not coinbase")); for (unsigned int i = 1; i < vtx.size(); i++) if (vtx[i].IsCoinBase()) - return error("CheckBlock() : more than one coinbase"); + return DoS(100, error("CheckBlock() : more than one coinbase")); // Check transactions BOOST_FOREACH(const CTransaction& tx, vtx) if (!tx.CheckTransaction()) - return error("CheckBlock() : CheckTransaction failed"); + return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed")); - // Check that it's not full of nonstandard transactions - if (GetSigOpCount() > MAX_BLOCK_SIGOPS) - return error("CheckBlock() : out-of-bounds SigOpCount"); + // Check for duplicate txids. This is caught by ConnectInputs(), + // but catching it earlier avoids a potential DoS attack: + set uniqueTx; + BOOST_FOREACH(const CTransaction& tx, vtx) + { + uniqueTx.insert(tx.GetHash()); + } + if (uniqueTx.size() != vtx.size()) + return DoS(100, error("CheckBlock() : duplicate transaction")); + + unsigned int nSigOps = 0; + BOOST_FOREACH(const CTransaction& tx, vtx) + { + nSigOps += tx.GetLegacySigOpCount(); + } + if (nSigOps > MAX_BLOCK_SIGOPS) + return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); // Check merkleroot if (hashMerkleRoot != BuildMerkleTree()) - return error("CheckBlock() : hashMerkleRoot mismatch"); + return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); return true; } @@ -1493,13 +1718,13 @@ bool CBlock::AcceptBlock() // Get prev block index map::iterator mi = mapBlockIndex.find(hashPrevBlock); if (mi == mapBlockIndex.end()) - return error("AcceptBlock() : prev block not found"); + return DoS(10, error("AcceptBlock() : prev block not found")); CBlockIndex* pindexPrev = (*mi).second; int nHeight = pindexPrev->nHeight+1; // Check proof of work if (nBits != GetNextWorkRequired(pindexPrev, this)) - return error("AcceptBlock() : incorrect proof of work"); + return DoS(100, error("AcceptBlock() : incorrect proof of work")); // Check timestamp against prev if (GetBlockTime() <= pindexPrev->GetMedianTimePast()) @@ -1508,14 +1733,14 @@ bool CBlock::AcceptBlock() // Check that all transactions are finalized BOOST_FOREACH(const CTransaction& tx, vtx) if (!tx.IsFinal(nHeight, GetBlockTime())) - return error("AcceptBlock() : contains a non-final transaction"); + return DoS(10, error("AcceptBlock() : contains a non-final transaction")); // Check that the block chain matches the known block chain up to a checkpoint if (!Checkpoints::CheckBlock(nHeight, hash)) - return error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight); + 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; @@ -1527,15 +1752,17 @@ bool CBlock::AcceptBlock() // 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 : nBlockEstimate)) - 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; } -bool static ProcessBlock(CNode* pfrom, CBlock* pblock) +bool ProcessBlock(CNode* pfrom, CBlock* pblock) { // Check for duplicate uint256 hash = pblock->GetHash(); @@ -1555,6 +1782,8 @@ bool static ProcessBlock(CNode* pfrom, CBlock* pblock) int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime; if (deltaTime < 0) { + if (pfrom) + pfrom->Misbehaving(100); return error("ProcessBlock() : block with timestamp before last checkpoint"); } CBigNum bnNewBlock; @@ -1563,6 +1792,8 @@ bool static ProcessBlock(CNode* pfrom, CBlock* pblock) bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime)); if (bnNewBlock > bnRequired) { + if (pfrom) + pfrom->Misbehaving(100); return error("ProcessBlock() : block with too little proof-of-work"); } } @@ -1624,11 +1855,11 @@ bool CheckDiskSpace(uint64 nAdditionalBytes) if (nFreeBytesAvailable < (uint64)15000000 + nAdditionalBytes) { fShutdown = true; - string strMessage = _("Warning: Disk space is low "); + 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); + StartShutdown(); return false; } return true; @@ -1638,7 +1869,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')) @@ -1868,8 +2099,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; @@ -1896,8 +2127,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();) { @@ -1956,16 +2187,18 @@ bool static AlreadyHave(CTxDB& txdb, const CInv& inv) case MSG_TX: { bool txInMap = false; - CRITICAL_BLOCK(cs_mapTransactions) - { - txInMap = (mapTransactions.count(inv.hash) != 0); - } + { + 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); + 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; @@ -1982,7 +2215,7 @@ unsigned char pchMessageStart[4] = { 0xf9, 0xbe, 0xb4, 0xd9 }; bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { - static map > mapReuseKey; + static map > mapReuseKey; RandAddSeedPerfmon(); if (fDebug) { printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); @@ -2002,25 +2235,34 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { // Each connection can only send one version message if (pfrom->nVersion != 0) + { + pfrom->Misbehaving(1); return false; + } int64 nTime; CAddress addrMe; 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) { @@ -2035,19 +2277,17 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); - AddTimeData(pfrom->addr.ip, nTime); + AddTimeData(pfrom->addr, nTime); // Change version - if (pfrom->nVersion >= 209) - pfrom->PushMessage("verack"); - pfrom->vSend.SetVersion(min(pfrom->nVersion, VERSION)); - if (pfrom->nVersion < 209) - pfrom->vRecv.SetVersion(min(pfrom->nVersion, VERSION)); + pfrom->PushMessage("verack"); + pfrom->vSend.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(); @@ -2055,17 +2295,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++; @@ -2073,26 +2321,31 @@ 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; printf("version message: version %d, blocks=%d\n", pfrom->nVersion, pfrom->nStartingHeight); + + cPeerBlockCounts.input(pfrom->nStartingHeight); } else if (pfrom->nVersion == 0) { // Must have a version message before anything else + pfrom->Misbehaving(1); return false; } else if (strCommand == "verack") { - pfrom->vRecv.SetVersion(min(pfrom->nVersion, VERSION)); + pfrom->vRecv.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); } @@ -2102,16 +2355,15 @@ 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) + { + pfrom->Misbehaving(20); return error("message addr size() = %d", vAddr.size()); + } // Store the new addresses - CAddrDB addrDB; - addrDB.TxnBegin(); int64 nNow = GetAdjustedTime(); int64 nSince = nNow - 10 * 60; BOOST_FOREACH(CAddress& addr, vAddr) @@ -2123,24 +2375,24 @@ 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; if (hashSalt == 0) - RAND_bytes((unsigned char*)&hashSalt, sizeof(hashSalt)); - uint256 hashRand = hashSalt ^ (((int64)addr.ip)<<32) ^ ((GetTime()+addr.ip)/(24*60*60)); + hashSalt = GetRandHash(); + int64 hashAddr = addr.GetHash(); + uint256 hashRand = hashSalt ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60)); hashRand = Hash(BEGIN(hashRand), END(hashRand)); 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)); @@ -2154,7 +2406,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; } @@ -2165,11 +2417,24 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vector vInv; vRecv >> vInv; if (vInv.size() > 50000) + { + pfrom->Misbehaving(20); return error("message inv size() = %d", vInv.size()); + } + // find last block in inv vector + unsigned int nLastBlock = (unsigned int)(-1); + for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { + if (vInv[vInv.size() - 1 - nInv].type == MSG_BLOCK) { + nLastBlock = vInv.size() - 1 - nInv; + break; + } + } 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); @@ -2180,8 +2445,17 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (!fAlreadyHave) pfrom->AskFor(inv); - else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) + else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) { pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash])); + } else if (nInv == nLastBlock) { + // In case we are on a very long side-chain, it is possible that we already have + // the last block in an inv bundle sent in response to getblocks. Try to detect + // this situation and push another getblocks to continue. + std::vector vGetData(1,inv); + pfrom->PushGetBlocks(mapBlockIndex[inv.hash], uint256(0)); + if (fDebug) + printf("force request: %s\n", inv.ToString().c_str()); + } // Track requests for our stuff Inventory(inv.hash); @@ -2194,7 +2468,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vector vInv; vRecv >> vInv; if (vInv.size() > 50000) + { + pfrom->Misbehaving(20); return error("message getdata size() = %d", vInv.size()); + } BOOST_FOREACH(const CInv& inv, vInv) { @@ -2228,8 +2505,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); @@ -2267,7 +2544,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 @@ -2304,8 +2581,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()); @@ -2319,7 +2596,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) else if (strCommand == "tx") { vector vWorkQueue; + vector vEraseQueue; CDataStream vMsg(vRecv); + CTxDB txdb("r"); CTransaction tx; vRecv >> tx; @@ -2327,50 +2606,59 @@ 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); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); + vEraseQueue.push_back(inv.hash); // Recursively process any orphan transactions that depended on this one for (unsigned int i = 0; i < vWorkQueue.size(); i++) { uint256 hashPrev = vWorkQueue[i]; - for (multimap::iterator mi = mapOrphanTransactionsByPrev.lower_bound(hashPrev); - mi != mapOrphanTransactionsByPrev.upper_bound(hashPrev); + for (map::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin(); + mi != mapOrphanTransactionsByPrev[hashPrev].end(); ++mi) { const CDataStream& vMsg = *((*mi).second); CTransaction tx; CDataStream(vMsg) >> tx; CInv inv(MSG_TX, tx.GetHash()); + bool fMissingInputs2 = false; - if (tx.AcceptToMemoryPool(true)) + if (tx.AcceptToMemoryPool(txdb, true, &fMissingInputs2)) { printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); SyncWithWallets(tx, NULL, true); RelayMessage(inv, vMsg); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); + vEraseQueue.push_back(inv.hash); + } + else if (!fMissingInputs2) + { + // invalid orphan + vEraseQueue.push_back(inv.hash); + printf(" removed invalid orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); } } } - BOOST_FOREACH(uint256 hash, vWorkQueue) + BOOST_FOREACH(uint256 hash, vEraseQueue) EraseOrphanTx(hash); } else if (fMissingInputs) { - 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); + unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS); if (nEvicted > 0) - printf("mapOrphan overflow, removed %d tx\n", nEvicted); + printf("mapOrphan overflow, removed %u tx\n", nEvicted); } + if (tx.nDoS) pfrom->Misbehaving(tx.nDoS); } @@ -2387,30 +2675,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (ProcessBlock(pfrom, &block)) mapAlreadyAskedFor.erase(inv); + if (block.nDoS) pfrom->Misbehaving(block.nDoS); } 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); } @@ -2431,12 +2705,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) /// we have a chance to check the order here // Keep giving the same key to the same ip until they use it - if (!mapReuseKey.count(pfrom->addr.ip)) - pwalletMain->GetKeyFromPool(mapReuseKey[pfrom->addr.ip], true); + if (!mapReuseKey.count(pfrom->addr)) + pwalletMain->GetKeyFromPool(mapReuseKey[pfrom->addr], true); // Send back approval of order and pubkey to use CScript scriptPubKey; - scriptPubKey << mapReuseKey[pfrom->addr.ip] << OP_CHECKSIG; + scriptPubKey << mapReuseKey[pfrom->addr] << OP_CHECKSIG; pfrom->PushMessage("reply", hashReply, (int)0, scriptPubKey); } @@ -2447,8 +2721,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()) { @@ -2463,6 +2737,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); + } } @@ -2475,9 +2766,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); + } } } @@ -2521,7 +2814,7 @@ bool ProcessMessages(CNode* pfrom) int nHeaderSize = vRecv.GetSerializeSize(CMessageHeader()); if (vRecv.end() - pstart < nHeaderSize) { - if (vRecv.size() > nHeaderSize) + if ((int)vRecv.size() > nHeaderSize) { printf("\n\nPROCESSMESSAGE MESSAGESTART NOT FOUND\n\n"); vRecv.erase(vRecv.begin(), vRecv.end() - nHeaderSize); @@ -2547,7 +2840,7 @@ bool ProcessMessages(CNode* pfrom) unsigned int nMessageSize = hdr.nMessageSize; if (nMessageSize > MAX_SIZE) { - printf("ProcessMessage(%s, %u bytes) : nMessageSize > MAX_SIZE\n", strCommand.c_str(), nMessageSize); + printf("ProcessMessages(%s, %u bytes) : nMessageSize > MAX_SIZE\n", strCommand.c_str(), nMessageSize); continue; } if (nMessageSize > vRecv.size()) @@ -2558,17 +2851,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("ProcessMessages(%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 @@ -2579,8 +2869,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; } @@ -2589,22 +2881,22 @@ bool ProcessMessages(CNode* pfrom) if (strstr(e.what(), "end of data")) { // Allow exceptions from underlength message on vRecv - printf("ProcessMessage(%s, %u bytes) : Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand.c_str(), nMessageSize, e.what()); + printf("ProcessMessages(%s, %u bytes) : Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand.c_str(), nMessageSize, e.what()); } else if (strstr(e.what(), "size too large")) { // Allow exceptions from overlong size - printf("ProcessMessage(%s, %u bytes) : Exception '%s' caught\n", strCommand.c_str(), nMessageSize, e.what()); + printf("ProcessMessages(%s, %u bytes) : Exception '%s' caught\n", strCommand.c_str(), nMessageSize, e.what()); } else { - PrintExceptionContinue(&e, "ProcessMessage()"); + PrintExceptionContinue(&e, "ProcessMessages()"); } } catch (std::exception& e) { - PrintExceptionContinue(&e, "ProcessMessage()"); + PrintExceptionContinue(&e, "ProcessMessages()"); } catch (...) { - PrintExceptionContinue(NULL, "ProcessMessage()"); + PrintExceptionContinue(NULL, "ProcessMessages()"); } if (!fRet) @@ -2618,33 +2910,39 @@ 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()) { + uint64 nonce = 0; + if (pto->nVersion > BIP0031_VERSION) + pto->PushMessage("ping", nonce); + 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(); @@ -2652,37 +2950,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 // @@ -2715,8 +2985,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) @@ -2730,7 +3000,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // 1/4 of tx invs blast to all immediately static uint256 hashSalt; if (hashSalt == 0) - RAND_bytes((unsigned char*)&hashSalt, sizeof(hashSalt)); + hashSalt = GetRandHash(); uint256 hashRand = inv.hash ^ hashSalt; hashRand = Hash(BEGIN(hashRand), END(hashRand)); bool fTrickleWait = ((hashRand & 3) != 0); @@ -2830,15 +3100,25 @@ int static FormatHashBlocks(void* pbuffer, unsigned int len) return blocks; } -using CryptoPP::ByteReverse; - static const unsigned int pSHA256InitState[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; -inline void SHA256Transform(void* pstate, void* pinput, const void* pinit) +void SHA256Transform(void* pstate, void* pinput, const void* pinit) { - memcpy(pstate, pinit, 32); - CryptoPP::SHA256::Transform((CryptoPP::word32*)pstate, (CryptoPP::word32*)pinput); + SHA256_CTX ctx; + unsigned char data[64]; + + SHA256_Init(&ctx); + + for (int i = 0; i < 16; i++) + ((uint32_t*)data)[i] = ByteReverse(((uint32_t*)pinput)[i]); + + for (int i = 0; i < 8; i++) + ctx.h[i] = ((uint32_t*)pinit)[i]; + + SHA256_Update(&ctx, data, sizeof(data)); + for (int i = 0; i < 8; i++) + ((uint32_t*)pstate)[i] = ctx.h[i]; } // @@ -2869,7 +3149,7 @@ unsigned int static ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1 if ((nNonce & 0xffff) == 0) { nHashesDone = 0xffff+1; - return -1; + return (unsigned int) -1; } } } @@ -2897,6 +3177,9 @@ public: }; +uint64 nLastBlockTx = 0; +uint64 nLastBlockSize = 0; + CBlock* CreateNewBlock(CReserveKey& reservekey) { CBlockIndex* pindexPrev = pindexBest; @@ -2918,16 +3201,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()) @@ -2961,11 +3243,11 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) dPriority += (double)nValueIn * nConf; if (fDebug && GetBoolArg("-printpriority")) - printf("priority nValueIn=%-12I64d nConf=%-5d dPriority=%-20.1f\n", nValueIn, nConf, dPriority); + printf("priority nValueIn=%-12"PRI64d" nConf=%-5d dPriority=%-20.1f\n", nValueIn, nConf, dPriority); } // Priority is sum(valuein * age) / txsize - dPriority /= ::GetSerializeSize(tx, SER_NETWORK); + dPriority /= ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); if (porphan) porphan->dPriority = dPriority; @@ -2984,6 +3266,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) // Collect transactions into block map mapTestPool; uint64 nBlockSize = 1000; + uint64 nBlockTx = 0; int nBlockSigOps = 100; while (!mapPriority.empty()) { @@ -2993,24 +3276,24 @@ 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 nTxSigOps = tx.GetSigOpCount(); + unsigned int nTxSigOps = tx.GetLegacySigOpCount(); if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) continue; // Transaction fee required depends on block size bool fAllowFree = (nBlockSize + nTxSize < 4000 || CTransaction::AllowFree(dPriority)); - int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree); + int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree, GMF_BLOCK); // Connecting shouldn't fail due to dependency on other memory pool transactions // because we're already processing them in order of dependency map mapTestPoolTmp(mapTestPool); - bool fInvalid; MapPrevTx mapInputs; + bool fInvalid; if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs, fInvalid)) continue; @@ -3030,6 +3313,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) // Added pblock->vtx.push_back(tx); nBlockSize += nTxSize; + ++nBlockTx; nBlockSigOps += nTxSigOps; nFees += nTxFees; @@ -3048,6 +3332,11 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) } } } + + nLastBlockTx = nBlockTx; + nLastBlockSize = nBlockSize; + printf("CreateNewBlock(): total size %lu\n", nBlockSize); + } pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees); @@ -3072,7 +3361,9 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& hashPrevBlock = pblock->hashPrevBlock; } ++nExtraNonce; - pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nTime << CBigNum(nExtraNonce); + pblock->vtx[0].vin[0].scriptSig = (CScript() << pblock->nTime << CBigNum(nExtraNonce)) + COINBASE_FLAGS; + assert(pblock->vtx[0].vin[0].scriptSig.size() <= 100); + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); } @@ -3139,8 +3430,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"); @@ -3148,20 +3439,25 @@ 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)) return error("BitcoinMiner : ProcessBlock, block not accepted"); } - Sleep(2000); return true; } 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"); @@ -3173,8 +3469,6 @@ void static BitcoinMiner(CWallet *pwallet) while (fGenerateBitcoins) { - if (AffinityBugWorkaround(ThreadBitcoinMiner)) - return; if (fShutdown) return; while (vNodes.empty() || IsInitialBlockDownload()) @@ -3232,7 +3526,7 @@ void static BitcoinMiner(CWallet *pwallet) (char*)&hash, nHashesDone); // Check if something found - if (nNonceFound != -1) + if (nNonceFound != (unsigned int) -1) { for (unsigned int i = 0; i < sizeof(hash)/4; i++) ((unsigned int*)&hash)[i] = ByteReverse(((unsigned int*)&hash)[i]); @@ -3262,21 +3556,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); } } } @@ -3287,7 +3579,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; @@ -3316,34 +3608,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); @@ -3351,7 +3642,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++) {