diff options
Diffstat (limited to 'src/main.cpp')
| -rw-r--r-- | src/main.cpp | 1079 |
1 files changed, 733 insertions, 346 deletions
diff --git a/src/main.cpp b/src/main.cpp index 28fbc8b45..3151a806d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,6 +10,7 @@ #include "net.h" #include "init.h" #include "ui_interface.h" +#include "checkqueue.h" #include <boost/algorithm/string/replace.hpp> #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> @@ -40,9 +41,11 @@ uint256 hashBestChain = 0; CBlockIndex* pindexBest = NULL; set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; // may contain all CBlockIndex*'s that have validness >=BLOCK_VALID_TRANSACTIONS, and must contain those who aren't failed int64 nTimeBestReceived = 0; +int nScriptCheckThreads = 0; bool fImporting = false; bool fReindex = false; bool fBenchmark = false; +bool fTxIndex = false; unsigned int nCoinCacheSize = 5000; CMedianFilter<int> cPeerBlockCounts(8, 0); // Amount of blocks that other nodes claim to have @@ -90,15 +93,6 @@ void UnregisterWallet(CWallet* pwalletIn) } } -// check whether the passed transaction is from us -bool static IsFromMe(CTransaction& tx) -{ - BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - if (pwallet->IsFromMe(tx)) - return true; - return false; -} - // get the wallet transaction with the given hash (if it exists) bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx) { @@ -365,6 +359,17 @@ bool CTransaction::IsStandard() const if (nVersion > CTransaction::CURRENT_VERSION) return false; + if (!IsFinal()) + return false; + + // Extremely large transactions with lots of inputs can cost the network + // almost as much to process as they cost the sender in fees, because + // computing signature hashes is O(ninputs*txsize). Limiting transactions + // to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks. + unsigned int sz = this->GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); + if (sz >= MAX_STANDARD_TX_SIZE) + return false; + BOOST_FOREACH(const CTxIn& txin, vin) { // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG @@ -467,28 +472,21 @@ CTransaction::GetLegacySigOpCount() const int CMerkleTx::SetMerkleBranch(const CBlock* pblock) { - if (fClient) - { - if (hashBlock == 0) - return 0; - } - else - { - CBlock blockTmp; - - if (pblock == NULL) { - CCoins coins; - if (pcoinsTip->GetCoins(GetHash(), coins)) { - CBlockIndex *pindex = FindBlockByHeight(coins.nHeight); - if (pindex) { - if (!blockTmp.ReadFromDisk(pindex)) - return 0; - pblock = &blockTmp; - } + CBlock blockTmp; + + if (pblock == NULL) { + CCoins coins; + if (pcoinsTip->GetCoins(GetHash(), coins)) { + CBlockIndex *pindex = FindBlockByHeight(coins.nHeight); + if (pindex) { + if (!blockTmp.ReadFromDisk(pindex)) + return 0; + pblock = &blockTmp; } } + } - if (pblock) { + if (pblock) { // Update the tx's hashBlock hashBlock = pblock->GetHash(); @@ -506,7 +504,6 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) // Fill in merkle branch vMerkleBranch = pblock->GetMerkleBranch(nIndex); - } } // Is the tx in a block that's in the main chain @@ -526,28 +523,28 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) -bool CTransaction::CheckTransaction() const +bool CTransaction::CheckTransaction(CValidationState &state) const { // Basic checks that don't depend on any context if (vin.empty()) - return DoS(10, error("CTransaction::CheckTransaction() : vin empty")); + return state.DoS(10, error("CTransaction::CheckTransaction() : vin empty")); if (vout.empty()) - return DoS(10, error("CTransaction::CheckTransaction() : vout empty")); + return state.DoS(10, error("CTransaction::CheckTransaction() : vout empty")); // Size limits if (::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) - return DoS(100, error("CTransaction::CheckTransaction() : size limits failed")); + return state.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 DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative")); + return state.DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative")); if (txout.nValue > MAX_MONEY) - return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high")); + return state.DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high")); nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) - return DoS(100, error("CTransaction::CheckTransaction() : txout total out of range")); + return state.DoS(100, error("CTransaction::CheckTransaction() : txout total out of range")); } // Check for duplicate inputs @@ -555,20 +552,20 @@ bool CTransaction::CheckTransaction() const BOOST_FOREACH(const CTxIn& txin, vin) { if (vInOutPoints.count(txin.prevout)) - return false; + return state.DoS(100, error("CTransaction::CheckTransaction() : duplicate inputs")); vInOutPoints.insert(txin.prevout); } if (IsCoinBase()) { if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) - return DoS(100, error("CTransaction::CheckTransaction() : coinbase script size")); + return state.DoS(100, error("CTransaction::CheckTransaction() : coinbase script size")); } else { BOOST_FOREACH(const CTxIn& txin, vin) if (txin.prevout.IsNull()) - return DoS(10, error("CTransaction::CheckTransaction() : prevout is null")); + return state.DoS(10, error("CTransaction::CheckTransaction() : prevout is null")); } return true; @@ -635,18 +632,18 @@ void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins) } } -bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, +bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs) { if (pfMissingInputs) *pfMissingInputs = false; - if (!tx.CheckTransaction()) + if (!tx.CheckTransaction(state)) return error("CTxMemPool::accept() : CheckTransaction failed"); // Coinbase is only valid in a block, not as a loose transaction if (tx.IsCoinBase()) - return tx.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx")); + return state.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)tx.nLockTime > std::numeric_limits<int>::max()) @@ -719,7 +716,7 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, // are the actual inputs available? if (!tx.HaveInputs(view)) - return error("CTxMemPool::accept() : inputs already spent"); + return state.Invalid(error("CTxMemPool::accept() : inputs already spent")); // Bring the best block into scope view.GetBestBlock(); @@ -741,7 +738,7 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, // Don't accept it if it can't get into a block int64 txMinFee = tx.GetMinFee(1000, true, GMF_RELAY); - if (nFees < txMinFee) + if (fLimitFree && nFees < txMinFee) return error("CTxMemPool::accept() : not enough fees %s, %"PRI64d" < %"PRI64d, hash.ToString().c_str(), nFees, txMinFee); @@ -749,30 +746,29 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, // Continuously rate-limit free transactions // This mitigates 'penny-flooding' -- sending thousands of free transactions just to // be annoying or make others' transactions take longer to confirm. - if (nFees < MIN_RELAY_TX_FEE) + if (fLimitFree && nFees < MIN_RELAY_TX_FEE) { - static CCriticalSection cs; static double dFreeCount; static int64 nLastTime; int64 nNow = GetTime(); - { - // 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(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; - } + 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) + return error("CTxMemPool::accept() : free transaction rejected by rate limiter"); + if (fDebug) + printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); + dFreeCount += nSize; } // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!tx.CheckInputs(view, CS_ALWAYS, SCRIPT_VERIFY_P2SH)) + if (!tx.CheckInputs(state, view, true, SCRIPT_VERIFY_P2SH)) { return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); } @@ -793,6 +789,7 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, // If updated, erase old tx from wallet if (ptxOld) EraseFromWallets(ptxOld->GetHash()); + SyncWithWallets(hash, tx, NULL, true); printf("CTxMemPool::accept() : accepted %s (poolsz %"PRIszu")\n", hash.ToString().substr(0,10).c_str(), @@ -800,9 +797,13 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, return true; } -bool CTransaction::AcceptToMemoryPool(bool fCheckInputs, bool* pfMissingInputs) +bool CTransaction::AcceptToMemoryPool(CValidationState &state, bool fCheckInputs, bool fLimitFree, bool* pfMissingInputs) { - return mempool.accept(*this, fCheckInputs, pfMissingInputs); + try { + return mempool.accept(state, *this, fCheckInputs, fLimitFree, pfMissingInputs); + } catch(std::runtime_error &e) { + return state.Abort(_("System error: ") + e.what()); + } } bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx) @@ -913,18 +914,10 @@ int CMerkleTx::GetBlocksToMaturity() const } -bool CMerkleTx::AcceptToMemoryPool(bool fCheckInputs) +bool CMerkleTx::AcceptToMemoryPool(bool fCheckInputs, bool fLimitFree) { - if (fClient) - { - if (!IsInMainChain() && !ClientCheckInputs()) - return false; - return CTransaction::AcceptToMemoryPool(false); - } - else - { - return CTransaction::AcceptToMemoryPool(fCheckInputs); - } + CValidationState state; + return CTransaction::AcceptToMemoryPool(state, fCheckInputs, fLimitFree); } @@ -940,10 +933,10 @@ bool CWalletTx::AcceptWalletTransaction(bool fCheckInputs) { uint256 hash = tx.GetHash(); if (!mempool.exists(hash) && pcoinsTip->HaveCoins(hash)) - tx.AcceptToMemoryPool(fCheckInputs); + tx.AcceptToMemoryPool(fCheckInputs, false); } } - return AcceptToMemoryPool(fCheckInputs); + return AcceptToMemoryPool(fCheckInputs, false); } return false; } @@ -964,6 +957,25 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock } } + if (fTxIndex) { + CDiskTxPos postx; + if (pblocktree->ReadTxIndex(hash, postx)) { + CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION); + CBlockHeader header; + try { + file >> header; + fseek(file, postx.nTxOffset, SEEK_CUR); + file >> txOut; + } catch (std::exception &e) { + return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); + } + hashBlock = header.GetHash(); + if (txOut.GetHash() != hash) + return error("%s() : txid mismatch", __PRETTY_FUNCTION__); + return true; + } + } + if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it int nHeight = -1; { @@ -1201,11 +1213,13 @@ void static InvalidBlockFound(CBlockIndex *pindex) { pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex)); setBlockIndexValid.erase(pindex); InvalidChainFound(pindex); - if (pindex->pnext) - ConnectBestBlock(); // reorganise away from the failed block + if (pindex->pnext) { + CValidationState stateDummy; + ConnectBestBlock(stateDummy); // reorganise away from the failed block + } } -bool ConnectBestBlock() { +bool ConnectBestBlock(CValidationState &state) { do { CBlockIndex *pindexNewBest; @@ -1244,8 +1258,12 @@ bool ConnectBestBlock() { BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) { if (fRequestShutdown) break; - if (!SetBestChain(pindexSwitch)) - return false; + try { + if (!SetBestChain(state, pindexSwitch)) + return false; + } catch(std::runtime_error &e) { + return state.Abort(_("System error: ") + e.what()); + } } return true; } @@ -1307,22 +1325,20 @@ unsigned int CTransaction::GetP2SHSigOpCount(CCoinsViewCache& inputs) const return nSigOps; } -bool CTransaction::UpdateCoins(CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) const +bool CTransaction::UpdateCoins(CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) const { // mark inputs spent if (!IsCoinBase()) { BOOST_FOREACH(const CTxIn &txin, vin) { CCoins &coins = inputs.GetCoins(txin.prevout.hash); CTxInUndo undo; - if (!coins.Spend(txin.prevout, undo)) - return error("UpdateCoins() : cannot spend input"); + assert(coins.Spend(txin.prevout, undo)); txundo.vprevout.push_back(undo); } } // add outputs - if (!inputs.SetCoins(txhash, CCoins(*this, nHeight))) - return error("UpdateCoins() : cannot update output"); + assert(inputs.SetCoins(txhash, CCoins(*this, nHeight))); return true; } @@ -1348,14 +1364,29 @@ bool CTransaction::HaveInputs(CCoinsViewCache &inputs) const return true; } -bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmode, unsigned int flags) const +bool CScriptCheck::operator()() const { + const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; + if (!VerifyScript(scriptSig, scriptPubKey, *ptxTo, nIn, nFlags, nHashType)) + return error("CScriptCheck() : %s VerifySignature failed", ptxTo->GetHash().ToString().substr(0,10).c_str()); + return true; +} + +bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType) +{ + return CScriptCheck(txFrom, txTo, nIn, flags, nHashType)(); +} + +bool CTransaction::CheckInputs(CValidationState &state, CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, std::vector<CScriptCheck> *pvChecks) const { if (!IsCoinBase()) { + if (pvChecks) + pvChecks->reserve(vin.size()); + // 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 (!HaveInputs(inputs)) - return error("CheckInputs() : %s inputs unavailable", GetHash().ToString().substr(0,10).c_str()); + return state.Invalid(error("CheckInputs() : %s inputs unavailable", GetHash().ToString().substr(0,10).c_str())); // While checking, GetBestBlock() refers to the parent block. // This is also true for mempool checks. @@ -1370,26 +1401,26 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmod // If prev is coinbase, check that it's matured if (coins.IsCoinBase()) { if (nSpendHeight - coins.nHeight < COINBASE_MATURITY) - return error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight); + return state.Invalid(error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight)); } // Check for negative or overflow input values nValueIn += coins.vout[prevout.n].nValue; if (!MoneyRange(coins.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return DoS(100, error("CheckInputs() : txin values out of range")); + return state.DoS(100, error("CheckInputs() : txin values out of range")); } if (nValueIn < GetValueOut()) - return DoS(100, error("ChecktInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str())); + return state.DoS(100, error("CheckInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str())); // Tally transaction fees int64 nTxFee = nValueIn - GetValueOut(); if (nTxFee < 0) - return DoS(100, error("CheckInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str())); + return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str())); nFees += nTxFee; if (!MoneyRange(nFees)) - return DoS(100, error("CheckInputs() : nFees out of range")); + return state.DoS(100, error("CheckInputs() : nFees out of range")); // The first loop above does all the inexpensive checks. // Only if ALL inputs pass do we perform expensive ECDSA signature checks. @@ -1398,15 +1429,18 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmod // Skip ECDSA signature verification when connecting blocks // before the last block chain checkpoint. This is safe because block merkle hashes are // still computed and checked, and any change will be caught at the next checkpoint. - if (csmode == CS_ALWAYS || - (csmode == CS_AFTER_CHECKPOINT && inputs.GetBestBlock()->nHeight >= Checkpoints::GetTotalBlocksEstimate())) { + if (fScriptChecks) { for (unsigned int i = 0; i < vin.size(); i++) { const COutPoint &prevout = vin[i].prevout; const CCoins &coins = inputs.GetCoins(prevout.hash); // Verify signature - if (!VerifySignature(coins, *this, i, flags, 0)) - return DoS(100,error("CheckInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str())); + CScriptCheck check(coins, *this, i, flags, 0); + if (pvChecks) { + pvChecks->push_back(CScriptCheck()); + check.swap(pvChecks->back()); + } else if (!check()) + return state.DoS(100,false); } } } @@ -1415,72 +1449,26 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmod } -bool CTransaction::ClientCheckInputs() const -{ - if (IsCoinBase()) - return false; - // Take over previous transactions' spent pointers - { - 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 (!mempool.exists(prevout.hash)) - return false; - CTransaction& txPrev = mempool.lookup(prevout.hash); - if (prevout.n >= txPrev.vout.size()) - return false; - - // Verify signature - if (!VerifySignature(CCoins(txPrev, -1), *this, i, SCRIPT_VERIFY_P2SH, 0)) - return error("ConnectInputs() : VerifySignature failed"); - - ///// 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()) - // return error("ConnectInputs() : prev tx already used"); - // - // // Flag outpoints as used - // txPrev.vout[prevout.n].posNext = posThisTx; - - nValueIn += txPrev.vout[prevout.n].nValue; - - if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return error("ClientConnectInputs() : txin values out of range"); - } - if (GetValueOut() > nValueIn) - return false; - } - - return true; -} - - - - -bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view) +bool CBlock::DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &view, bool *pfClean) { assert(pindex == view.GetBestBlock()); + if (pfClean) + *pfClean = false; + + bool fClean = true; + CBlockUndo blockUndo; - { - CDiskBlockPos pos = pindex->GetUndoPos(); - if (pos.IsNull()) - return error("DisconnectBlock() : no undo data available"); - FILE *file = OpenUndoFile(pos, true); - if (file == NULL) - return error("DisconnectBlock() : undo file not available"); - CAutoFile fileUndo(file, SER_DISK, CLIENT_VERSION); - fileUndo >> blockUndo; - } + CDiskBlockPos pos = pindex->GetUndoPos(); + if (pos.IsNull()) + return error("DisconnectBlock() : no undo data available"); + if (!blockUndo.ReadFromDisk(pos, pindex->pprev->GetBlockHash())) + return error("DisconnectBlock() : failure reading undo data"); - assert(blockUndo.vtxundo.size() + 1 == vtx.size()); + if (blockUndo.vtxundo.size() + 1 != vtx.size()) + return error("DisconnectBlock() : block and undo data inconsistent"); // undo transactions in reverse order for (int i = vtx.size() - 1; i >= 0; i--) { @@ -1488,13 +1476,15 @@ bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view) uint256 hash = tx.GetHash(); // check that all outputs are available - if (!view.HaveCoins(hash)) - return error("DisconnectBlock() : outputs still spent? database corrupted"); + if (!view.HaveCoins(hash)) { + fClean = fClean && error("DisconnectBlock() : outputs still spent? database corrupted"); + view.SetCoins(hash, CCoins()); + } CCoins &outs = view.GetCoins(hash); CCoins outsBlock = CCoins(tx, pindex->nHeight); if (outs != outsBlock) - return error("DisconnectBlock() : added transaction mismatch? database corrupted"); + fClean = fClean && error("DisconnectBlock() : added transaction mismatch? database corrupted"); // remove outputs outs = CCoins(); @@ -1502,24 +1492,27 @@ bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view) // restore inputs if (i > 0) { // not coinbases const CTxUndo &txundo = blockUndo.vtxundo[i-1]; - assert(txundo.vprevout.size() == tx.vin.size()); + if (txundo.vprevout.size() != tx.vin.size()) + return error("DisconnectBlock() : transaction and undo data inconsistent"); for (unsigned int j = tx.vin.size(); j-- > 0;) { const COutPoint &out = tx.vin[j].prevout; const CTxInUndo &undo = txundo.vprevout[j]; CCoins coins; view.GetCoins(out.hash, coins); // this can fail if the prevout was already entirely spent - if (coins.IsPruned()) { - if (undo.nHeight == 0) - return error("DisconnectBlock() : undo data doesn't contain tx metadata? database corrupted"); + if (undo.nHeight != 0) { + // undo data contains height: this is the last output of the prevout tx being spent + if (!coins.IsPruned()) + fClean = fClean && error("DisconnectBlock() : undo data overwriting existing transaction"); + coins = CCoins(); coins.fCoinBase = undo.fCoinBase; coins.nHeight = undo.nHeight; coins.nVersion = undo.nVersion; } else { - if (undo.nHeight != 0) - return error("DisconnectBlock() : undo data contains unneeded tx metadata? database corrupted"); + if (coins.IsPruned()) + fClean = fClean && error("DisconnectBlock() : undo data adding output to missing transaction"); } if (coins.IsAvailable(out.n)) - return error("DisconnectBlock() : prevout output not spent? database corrupted"); + fClean = fClean && error("DisconnectBlock() : undo data overwriting existing output"); if (coins.vout.size() < out.n+1) coins.vout.resize(out.n+1); coins.vout[out.n] = undo.txout; @@ -1532,7 +1525,12 @@ bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view) // move best block pointer to prevout block view.SetBestBlock(pindex->pprev); - return true; + if (pfClean) { + *pfClean = fClean; + return true; + } else { + return fClean; + } } void static FlushBlockFile() @@ -1554,17 +1552,40 @@ void static FlushBlockFile() } } -bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize); +bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize); + +static CCheckQueue<CScriptCheck> scriptcheckqueue(128); + +void ThreadScriptCheck(void*) { + vnThreadsRunning[THREAD_SCRIPTCHECK]++; + RenameThread("bitcoin-scriptch"); + scriptcheckqueue.Thread(); + vnThreadsRunning[THREAD_SCRIPTCHECK]--; +} + +void ThreadScriptCheckQuit() { + scriptcheckqueue.Quit(); +} -bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck) +bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck) { // Check it again in case a previous version let a bad block in - if (!CheckBlock(!fJustCheck, !fJustCheck)) + if (!CheckBlock(state, !fJustCheck, !fJustCheck)) return false; // verify that the view's current state corresponds to the previous block assert(pindex->pprev == view.GetBestBlock()); + // Special case for the genesis block, skipping connection of its transactions + // (its coinbase is unspendable) + if (GetHash() == hashGenesisBlock) { + view.SetBestBlock(pindex); + pindexGenesisBlock = pindex; + return true; + } + + bool fScriptChecks = pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(); + // 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 @@ -1584,7 +1605,7 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust for (unsigned int i=0; i<vtx.size(); i++) { uint256 hash = GetTxHash(i); if (view.HaveCoins(hash) && !view.GetCoins(hash).IsPruned()) - return error("ConnectBlock() : tried to overwrite transaction"); + return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction")); } } @@ -1592,12 +1613,20 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust int64 nBIP16SwitchTime = 1333238400; bool fStrictPayToScriptHash = (pindex->nTime >= nBIP16SwitchTime); + unsigned int flags = SCRIPT_VERIFY_NOCACHE | + (fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE); + CBlockUndo blockundo; + CCheckQueueControl<CScriptCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); + int64 nStart = GetTimeMicros(); int64 nFees = 0; int nInputs = 0; unsigned int nSigOps = 0; + CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(vtx.size())); + std::vector<std::pair<uint256, CDiskTxPos> > vPos; + vPos.reserve(vtx.size()); for (unsigned int i=0; i<vtx.size(); i++) { @@ -1606,12 +1635,12 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust nInputs += tx.vin.size(); nSigOps += tx.GetLegacySigOpCount(); if (nSigOps > MAX_BLOCK_SIGOPS) - return DoS(100, error("ConnectBlock() : too many sigops")); + return state.DoS(100, error("ConnectBlock() : too many sigops")); if (!tx.IsCoinBase()) { if (!tx.HaveInputs(view)) - return DoS(100, error("ConnectBlock() : inputs missing/spent")); + return state.DoS(100, error("ConnectBlock() : inputs missing/spent")); if (fStrictPayToScriptHash) { @@ -1620,28 +1649,38 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust // an incredibly-expensive-to-validate block. nSigOps += tx.GetP2SHSigOpCount(view); if (nSigOps > MAX_BLOCK_SIGOPS) - return DoS(100, error("ConnectBlock() : too many sigops")); + return state.DoS(100, error("ConnectBlock() : too many sigops")); } nFees += tx.GetValueIn(view)-tx.GetValueOut(); - if (!tx.CheckInputs(view, CS_AFTER_CHECKPOINT, fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE)) + std::vector<CScriptCheck> vChecks; + if (!tx.CheckInputs(state, view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL)) return false; + control.Add(vChecks); } CTxUndo txundo; - if (!tx.UpdateCoins(view, txundo, pindex->nHeight, GetTxHash(i))) + if (!tx.UpdateCoins(state, view, txundo, pindex->nHeight, GetTxHash(i))) return error("ConnectBlock() : UpdateInputs failed"); if (!tx.IsCoinBase()) blockundo.vtxundo.push_back(txundo); + vPos.push_back(std::make_pair(GetTxHash(i), pos)); + pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } int64 nTime = GetTimeMicros() - nStart; if (fBenchmark) printf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)vtx.size(), 0.001 * nTime, 0.001 * nTime / vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1)); if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees)) - return error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees)); + return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees))); + + if (!control.Wait()) + return state.DoS(100, false); + int64 nTime2 = GetTimeMicros() - nStart; + if (fBenchmark) + printf("- Verify %u txins: %.2fms (%.3fms/txin)\n", nInputs - 1, 0.001 * nTime2, nInputs <= 1 ? 0 : 0.001 * nTime2 / (nInputs-1)); if (fJustCheck) return true; @@ -1651,10 +1690,10 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust { if (pindex->GetUndoPos().IsNull()) { CDiskBlockPos pos; - if (!FindUndoPos(pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 8)) + if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) return error("ConnectBlock() : FindUndoPos failed"); - if (!blockundo.WriteToDisk(pos)) - return error("ConnectBlock() : CBlockUndo::WriteToDisk failed"); + if (!blockundo.WriteToDisk(pos, pindex->pprev->GetBlockHash())) + return state.Abort(_("Failed to write undo data")); // update nUndoPos in block index pindex->nUndoPos = pos.nPos; @@ -1665,12 +1704,15 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust CDiskBlockIndex blockindex(pindex); if (!pblocktree->WriteBlockIndex(blockindex)) - return error("ConnectBlock() : WriteBlockIndex failed"); + return state.Abort(_("Failed to write block index")); } + if (fTxIndex) + if (!pblocktree->WriteTxIndex(vPos)) + return state.Abort(_("Failed to write transaction index")); + // add this block to the view's block chain - if (!view.SetBestBlock(pindex)) - return false; + assert(view.SetBestBlock(pindex)); // Watch for transactions paying to me for (unsigned int i=0; i<vtx.size(); i++) @@ -1679,39 +1721,25 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust return true; } -bool SetBestChain(CBlockIndex* pindexNew) +bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) { // All modifications to the coin state will be done in this cache. // Only when all have succeeded, we push it to pcoinsTip. CCoinsViewCache view(*pcoinsTip, true); - // special case for attaching the genesis block - // note that no ConnectBlock is called, so its coinbase output is non-spendable - if (pindexGenesisBlock == NULL && pindexNew->GetBlockHash() == hashGenesisBlock) - { - view.SetBestBlock(pindexNew); - if (!view.Flush()) - return false; - pindexGenesisBlock = pindexNew; - pindexBest = pindexNew; - hashBestChain = pindexNew->GetBlockHash(); - nBestHeight = pindexBest->nHeight; - bnBestChainWork = pindexNew->bnChainWork; - return true; - } - // Find the fork (typically, there is none) CBlockIndex* pfork = view.GetBestBlock(); CBlockIndex* plonger = pindexNew; while (pfork && pfork != plonger) { - while (plonger->nHeight > pfork->nHeight) - if (!(plonger = plonger->pprev)) - return error("SetBestChain() : plonger->pprev is null"); + while (plonger->nHeight > pfork->nHeight) { + plonger = plonger->pprev; + assert(plonger != NULL); + } if (pfork == plonger) break; - if (!(pfork = pfork->pprev)) - return error("SetBestChain() : pfork->pprev is null"); + pfork = pfork->pprev; + assert(pfork != NULL); } // List of what to disconnect (typically nothing) @@ -1735,9 +1763,9 @@ bool SetBestChain(CBlockIndex* pindexNew) BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) { CBlock block; if (!block.ReadFromDisk(pindex)) - return error("SetBestBlock() : ReadFromDisk for disconnect failed"); + return state.Abort(_("Failed to read block")); int64 nStart = GetTimeMicros(); - if (!block.DisconnectBlock(pindex, view)) + if (!block.DisconnectBlock(state, pindex, view)) return error("SetBestBlock() : DisconnectBlock %s failed", BlockHashStr(pindex->GetBlockHash()).c_str()); if (fBenchmark) printf("- Disconnect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); @@ -1755,11 +1783,13 @@ bool SetBestChain(CBlockIndex* pindexNew) BOOST_FOREACH(CBlockIndex *pindex, vConnect) { CBlock block; if (!block.ReadFromDisk(pindex)) - return error("SetBestBlock() : ReadFromDisk for connect failed"); + return state.Abort(_("Failed to read block")); int64 nStart = GetTimeMicros(); - if (!block.ConnectBlock(pindex, view)) { - InvalidChainFound(pindexNew); - InvalidBlockFound(pindex); + if (!block.ConnectBlock(state, pindex, view)) { + if (state.IsInvalid()) { + InvalidChainFound(pindexNew); + InvalidBlockFound(pindex); + } return error("SetBestBlock() : ConnectBlock %s failed", BlockHashStr(pindex->GetBlockHash()).c_str()); } if (fBenchmark) @@ -1773,8 +1803,7 @@ bool SetBestChain(CBlockIndex* pindexNew) // Flush changes to global coin state int64 nStart = GetTimeMicros(); int nModified = view.GetCacheSize(); - if (!view.Flush()) - return error("SetBestBlock() : unable to modify coin state"); + assert(view.Flush()); int64 nTime = GetTimeMicros() - nStart; if (fBenchmark) printf("- Flush %i transactions: %.2fms (%.4fms/tx)\n", nModified, 0.001 * nTime, 0.001 * nTime / nModified); @@ -1782,10 +1811,17 @@ bool SetBestChain(CBlockIndex* pindexNew) // Make sure it's successfully written to disk before changing memory structure bool fIsInitialDownload = IsInitialBlockDownload(); if (!fIsInitialDownload || pcoinsTip->GetCacheSize() > nCoinCacheSize) { + // Typical CCoins structures on disk are around 100 bytes in size. + // Pushing a new one to the database can cause it to be written + // twice (once in the log, and once in the tables). This is already + // an overestimation, as most will delete an existing entry or + // overwrite one. Still, use a conservative safety factor of 2. + if (!CheckDiskSpace(100 * 2 * 2 * pcoinsTip->GetCacheSize())) + return state.Error(); FlushBlockFile(); pblocktree->Sync(); if (!pcoinsTip->Flush()) - return false; + return state.Abort(_("Failed to write to coin database")); } // At this point, all changes have been done to the database. @@ -1802,8 +1838,11 @@ bool SetBestChain(CBlockIndex* pindexNew) pindex->pprev->pnext = pindex; // Resurrect memory transactions that were in the disconnected branch - BOOST_FOREACH(CTransaction& tx, vResurrect) - tx.AcceptToMemoryPool(); + BOOST_FOREACH(CTransaction& tx, vResurrect) { + // ignore validation errors in resurrected transactions + CValidationState stateDummy; + tx.AcceptToMemoryPool(stateDummy, true, false); + } // Delete redundant memory transactions that are in the connected branch BOOST_FOREACH(CTransaction& tx, vDelete) { @@ -1860,17 +1899,16 @@ bool SetBestChain(CBlockIndex* pindexNew) } -bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos) +bool CBlock::AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos) { // Check for duplicate uint256 hash = GetHash(); if (mapBlockIndex.count(hash)) - return error("AddToBlockIndex() : %s already exists", BlockHashStr(hash).c_str()); + return state.Invalid(error("AddToBlockIndex() : %s already exists", BlockHashStr(hash).c_str())); // Construct new block index object CBlockIndex* pindexNew = new CBlockIndex(*this); - if (!pindexNew) - return error("AddToBlockIndex() : new CBlockIndex failed"); + assert(pindexNew); map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(hashPrevBlock); @@ -1888,10 +1926,11 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos) pindexNew->nStatus = BLOCK_VALID_TRANSACTIONS | BLOCK_HAVE_DATA; setBlockIndexValid.insert(pindexNew); - pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew)); + if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew))) + return state.Abort(_("Failed to write block index")); // New best? - if (!ConnectBestBlock()) + if (!ConnectBestBlock(state)) return false; if (pindexNew == pindexBest) @@ -1902,14 +1941,15 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos) hashPrevBestCoinBase = GetTxHash(0); } - pblocktree->Flush(); + if (!pblocktree->Flush()) + return state.Abort(_("Failed to sync block index")); uiInterface.NotifyBlocksChanged(); return true; } -bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime, bool fKnown = false) +bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime, bool fKnown = false) { bool fUpdatedLast = false; @@ -1951,19 +1991,19 @@ bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeigh } } else - return error("FindBlockPos() : out of disk space"); + return state.Error(); } } if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile)) - return error("FindBlockPos() : cannot write updated block info"); + return state.Abort(_("Failed to write file info")); if (fUpdatedLast) pblocktree->WriteLastBlockFile(nLastBlockFile); return true; } -bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize) +bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize) { pos.nFile = nFile; @@ -1974,15 +2014,15 @@ bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize) pos.nPos = infoLastBlockFile.nUndoSize; nNewSize = (infoLastBlockFile.nUndoSize += nAddSize); if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile)) - return error("FindUndoPos() : cannot write updated block info"); + return state.Abort(_("Failed to write block info")); } else { CBlockFileInfo info; if (!pblocktree->ReadBlockFileInfo(nFile, info)) - return error("FindUndoPos() : cannot read block info"); + return state.Abort(_("Failed to read block info")); pos.nPos = info.nUndoSize; nNewSize = (info.nUndoSize += nAddSize); if (!pblocktree->WriteBlockFileInfo(nFile, info)) - return error("FindUndoPos() : cannot write updated block info"); + return state.Abort(_("Failed to write block info")); } unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; @@ -1997,41 +2037,41 @@ bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize) } } else - return error("FindUndoPos() : out of disk space"); + return state.Error(); } return true; } -bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const +bool CBlock::CheckBlock(CValidationState &state, bool fCheckPOW, bool fCheckMerkleRoot) const { // These are checks that are independent of context // that can be verified before saving an orphan block. // Size limits 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")); + return state.DoS(100, error("CheckBlock() : size limits failed")); // Check proof of work matches claimed amount if (fCheckPOW && !CheckProofOfWork(GetHash(), nBits)) - return DoS(50, error("CheckBlock() : proof of work failed")); + return state.DoS(50, error("CheckBlock() : proof of work failed")); // Check timestamp if (GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) - return error("CheckBlock() : block timestamp too far in the future"); + return state.Invalid(error("CheckBlock() : block timestamp too far in the future")); // 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")); + return state.DoS(100, error("CheckBlock() : first tx is not coinbase")); for (unsigned int i = 1; i < vtx.size(); i++) if (vtx[i].IsCoinBase()) - return DoS(100, error("CheckBlock() : more than one coinbase")); + return state.DoS(100, error("CheckBlock() : more than one coinbase")); // Check transactions BOOST_FOREACH(const CTransaction& tx, vtx) - if (!tx.CheckTransaction()) - return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed")); + if (!tx.CheckTransaction(state)) + return error("CheckBlock() : CheckTransaction failed"); // Build the merkle tree already. We need it anyway later, and it makes the // block cache the transaction hashes, which means they don't need to be @@ -2045,7 +2085,7 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const uniqueTx.insert(GetTxHash(i)); } if (uniqueTx.size() != vtx.size()) - return DoS(100, error("CheckBlock() : duplicate transaction")); + return state.DoS(100, error("CheckBlock() : duplicate transaction")); unsigned int nSigOps = 0; BOOST_FOREACH(const CTransaction& tx, vtx) @@ -2053,21 +2093,21 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const nSigOps += tx.GetLegacySigOpCount(); } if (nSigOps > MAX_BLOCK_SIGOPS) - return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); + return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); // Check merkle root if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree()) - return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); + return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); return true; } -bool CBlock::AcceptBlock(CDiskBlockPos *dbp) +bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp) { // Check for duplicate uint256 hash = GetHash(); if (mapBlockIndex.count(hash)) - return error("AcceptBlock() : block already in mapBlockIndex"); + return state.Invalid(error("AcceptBlock() : block already in mapBlockIndex")); // Get prev block index CBlockIndex* pindexPrev = NULL; @@ -2075,26 +2115,26 @@ bool CBlock::AcceptBlock(CDiskBlockPos *dbp) if (hash != hashGenesisBlock) { map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashPrevBlock); if (mi == mapBlockIndex.end()) - return DoS(10, error("AcceptBlock() : prev block not found")); + return state.DoS(10, error("AcceptBlock() : prev block not found")); pindexPrev = (*mi).second; nHeight = pindexPrev->nHeight+1; // Check proof of work if (nBits != GetNextWorkRequired(pindexPrev, this)) - return DoS(100, error("AcceptBlock() : incorrect proof of work")); + return state.DoS(100, error("AcceptBlock() : incorrect proof of work")); // Check timestamp against prev if (GetBlockTime() <= pindexPrev->GetMedianTimePast()) - return error("AcceptBlock() : block's timestamp is too early"); + return state.Invalid(error("AcceptBlock() : block's timestamp is too early")); // Check that all transactions are finalized BOOST_FOREACH(const CTransaction& tx, vtx) if (!tx.IsFinal(nHeight, GetBlockTime())) - return DoS(10, error("AcceptBlock() : contains a non-final transaction")); + return state.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 DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight)); + return state.DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight)); // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: if (nVersion < 2) @@ -2102,7 +2142,7 @@ bool CBlock::AcceptBlock(CDiskBlockPos *dbp) if ((!fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) || (fTestNet && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100))) { - return error("AcceptBlock() : rejected nVersion=1 block"); + return state.Invalid(error("AcceptBlock() : rejected nVersion=1 block")); } } // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height @@ -2114,23 +2154,27 @@ bool CBlock::AcceptBlock(CDiskBlockPos *dbp) { CScript expect = CScript() << nHeight; if (!std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin())) - return DoS(100, error("AcceptBlock() : block height mismatch in coinbase")); + return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase")); } } } // Write block to history file - unsigned int nBlockSize = ::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION); - CDiskBlockPos blockPos; - if (dbp != NULL) - blockPos = *dbp; - if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, nTime, dbp != NULL)) - return error("AcceptBlock() : FindBlockPos failed"); - if (dbp == NULL) - if (!WriteToDisk(blockPos)) - return error("AcceptBlock() : WriteToDisk failed"); - if (!AddToBlockIndex(blockPos)) - return error("AcceptBlock() : AddToBlockIndex failed"); + try { + unsigned int nBlockSize = ::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION); + CDiskBlockPos blockPos; + if (dbp != NULL) + blockPos = *dbp; + if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, nTime, dbp != NULL)) + return error("AcceptBlock() : FindBlockPos failed"); + if (dbp == NULL) + if (!WriteToDisk(blockPos)) + return state.Abort(_("Failed to write block")); + if (!AddToBlockIndex(state, blockPos)) + return error("AcceptBlock() : AddToBlockIndex failed"); + } catch(std::runtime_error &e) { + return state.Abort(_("System error: ") + e.what()); + } // Relay inventory, but don't relay old inventory during initial block download int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); @@ -2157,17 +2201,17 @@ bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, uns return (nFound >= nRequired); } -bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) +bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) { // Check for duplicate uint256 hash = pblock->GetHash(); if (mapBlockIndex.count(hash)) - return error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, BlockHashStr(hash).c_str()); + return state.Invalid(error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, BlockHashStr(hash).c_str())); if (mapOrphanBlocks.count(hash)) - return error("ProcessBlock() : already have block (orphan) %s", BlockHashStr(hash).c_str()); + return state.Invalid(error("ProcessBlock() : already have block (orphan) %s", BlockHashStr(hash).c_str())); // Preliminary checks - if (!pblock->CheckBlock()) + if (!pblock->CheckBlock(state)) return error("ProcessBlock() : CheckBlock FAILED"); CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); @@ -2177,9 +2221,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime; if (deltaTime < 0) { - if (pfrom) - pfrom->Misbehaving(100); - return error("ProcessBlock() : block with timestamp before last checkpoint"); + return state.DoS(100, error("ProcessBlock() : block with timestamp before last checkpoint")); } CBigNum bnNewBlock; bnNewBlock.SetCompact(pblock->nBits); @@ -2187,9 +2229,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime)); if (bnNewBlock > bnRequired) { - if (pfrom) - pfrom->Misbehaving(100); - return error("ProcessBlock() : block with too little proof-of-work"); + return state.DoS(100, error("ProcessBlock() : block with too little proof-of-work")); } } @@ -2212,7 +2252,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) } // Store to disk - if (!pblock->AcceptBlock(dbp)) + if (!pblock->AcceptBlock(state, dbp)) return error("ProcessBlock() : AcceptBlock FAILED"); // Recursively process any orphan blocks that depended on this one @@ -2226,7 +2266,9 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) ++mi) { CBlock* pblockOrphan = (*mi).second; - if (pblockOrphan->AcceptBlock()) + // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan resolution (that is, feeding people an invalid block based on LegitBlockX in order to get anyone relaying LegitBlockX banned) + CValidationState stateDummy; + if (pblockOrphan->AcceptBlock(stateDummy)) vWorkQueue.push_back(pblockOrphan->GetHash()); mapOrphanBlocks.erase(pblockOrphan->GetHash()); delete pblockOrphan; @@ -2245,21 +2287,176 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) +CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter) +{ + header = block.GetBlockHeader(); + + vector<bool> vMatch; + vector<uint256> vHashes; + + vMatch.reserve(block.vtx.size()); + vHashes.reserve(block.vtx.size()); + + for (unsigned int i = 0; i < block.vtx.size(); i++) + { + uint256 hash = block.vtx[i].GetHash(); + if (filter.IsRelevantAndUpdate(block.vtx[i], hash)) + { + vMatch.push_back(true); + vMatchedTxn.push_back(make_pair(i, hash)); + } + else + vMatch.push_back(false); + vHashes.push_back(hash); + } + + txn = CPartialMerkleTree(vHashes, vMatch); +} + + + + + + + + +uint256 CPartialMerkleTree::CalcHash(int height, unsigned int pos, const std::vector<uint256> &vTxid) { + if (height == 0) { + // hash at height 0 is the txids themself + return vTxid[pos]; + } else { + // calculate left hash + uint256 left = CalcHash(height-1, pos*2, vTxid), right; + // calculate right hash if not beyong the end of the array - copy left hash otherwise1 + if (pos*2+1 < CalcTreeWidth(height-1)) + right = CalcHash(height-1, pos*2+1, vTxid); + else + right = left; + // combine subhashes + return Hash(BEGIN(left), END(left), BEGIN(right), END(right)); + } +} + +void CPartialMerkleTree::TraverseAndBuild(int height, unsigned int pos, const std::vector<uint256> &vTxid, const std::vector<bool> &vMatch) { + // determine whether this node is the parent of at least one matched txid + bool fParentOfMatch = false; + for (unsigned int p = pos << height; p < (pos+1) << height && p < nTransactions; p++) + fParentOfMatch |= vMatch[p]; + // store as flag bit + vBits.push_back(fParentOfMatch); + if (height==0 || !fParentOfMatch) { + // if at height 0, or nothing interesting below, store hash and stop + vHash.push_back(CalcHash(height, pos, vTxid)); + } else { + // otherwise, don't store any hash, but descend into the subtrees + TraverseAndBuild(height-1, pos*2, vTxid, vMatch); + if (pos*2+1 < CalcTreeWidth(height-1)) + TraverseAndBuild(height-1, pos*2+1, vTxid, vMatch); + } +} + +uint256 CPartialMerkleTree::TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector<uint256> &vMatch) { + if (nBitsUsed >= vBits.size()) { + // overflowed the bits array - failure + fBad = true; + return 0; + } + bool fParentOfMatch = vBits[nBitsUsed++]; + if (height==0 || !fParentOfMatch) { + // if at height 0, or nothing interesting below, use stored hash and do not descend + if (nHashUsed >= vHash.size()) { + // overflowed the hash array - failure + fBad = true; + return 0; + } + const uint256 &hash = vHash[nHashUsed++]; + if (height==0 && fParentOfMatch) // in case of height 0, we have a matched txid + vMatch.push_back(hash); + return hash; + } else { + // otherwise, descend into the subtrees to extract matched txids and hashes + uint256 left = TraverseAndExtract(height-1, pos*2, nBitsUsed, nHashUsed, vMatch), right; + if (pos*2+1 < CalcTreeWidth(height-1)) + right = TraverseAndExtract(height-1, pos*2+1, nBitsUsed, nHashUsed, vMatch); + else + right = left; + // and combine them before returning + return Hash(BEGIN(left), END(left), BEGIN(right), END(right)); + } +} + +CPartialMerkleTree::CPartialMerkleTree(const std::vector<uint256> &vTxid, const std::vector<bool> &vMatch) : nTransactions(vTxid.size()), fBad(false) { + // reset state + vBits.clear(); + vHash.clear(); + + // calculate height of tree + int nHeight = 0; + while (CalcTreeWidth(nHeight) > 1) + nHeight++; + + // traverse the partial tree + TraverseAndBuild(nHeight, 0, vTxid, vMatch); +} + +CPartialMerkleTree::CPartialMerkleTree() : nTransactions(0), fBad(true) {} + +uint256 CPartialMerkleTree::ExtractMatches(std::vector<uint256> &vMatch) { + vMatch.clear(); + // An empty set will not work + if (nTransactions == 0) + return 0; + // check for excessively high numbers of transactions + if (nTransactions > MAX_BLOCK_SIZE / 60) // 60 is the lower bound for the size of a serialized CTransaction + return 0; + // there can never be more hashes provided than one for every txid + if (vHash.size() > nTransactions) + return 0; + // there must be at least one bit per node in the partial tree, and at least one node per hash + if (vBits.size() < vHash.size()) + return 0; + // calculate height of tree + int nHeight = 0; + while (CalcTreeWidth(nHeight) > 1) + nHeight++; + // traverse the partial tree + unsigned int nBitsUsed = 0, nHashUsed = 0; + uint256 hashMerkleRoot = TraverseAndExtract(nHeight, 0, nBitsUsed, nHashUsed, vMatch); + // verify that no problems occured during the tree traversal + if (fBad) + return 0; + // verify that all bits were consumed (except for the padding caused by serializing it as a byte sequence) + if ((nBitsUsed+7)/8 != (vBits.size()+7)/8) + return 0; + // verify that all hashes were consumed + if (nHashUsed != vHash.size()) + return 0; + return hashMerkleRoot; +} + + + + + + + +bool AbortNode(const std::string &strMessage) { + fRequestShutdown = true; + strMiscWarning = strMessage; + printf("*** %s\n", strMessage.c_str()); + uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_ERROR | CClientUIInterface::MODAL); + StartShutdown(); + return false; +} + bool CheckDiskSpace(uint64 nAdditionalBytes) { uint64 nFreeBytesAvailable = filesystem::space(GetDataDir()).available; // Check for nMinDiskSpace bytes (currently 50MB) if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes) - { - fShutdown = true; - string strMessage = _("Error: Disk space is low!"); - strMiscWarning = strMessage; - printf("*** %s\n", strMessage.c_str()); - uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_ERROR); - StartShutdown(); - return false; - } + return AbortNode(_("Error: Disk space is low!")); + return true; } @@ -2358,6 +2555,10 @@ bool static LoadBlockIndexDB() pblocktree->ReadReindexing(fReindexing); fReindex |= fReindexing; + // Check whether we have a transaction index + pblocktree->ReadFlag("txindex", fTxIndex); + printf("LoadBlockIndex(): transaction index %s\n", fTxIndex ? "enabled" : "disabled"); + // Load hashBestChain pointer to end of best chain pindexBest = pcoinsTip->GetBestBlock(); if (pindexBest == NULL) @@ -2377,39 +2578,93 @@ bool static LoadBlockIndexDB() BlockHashStr(hashBestChain).c_str(), nBestHeight, DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str()); + return true; +} + +bool VerifyDB() { + if (pindexBest == NULL || pindexBest->pprev == NULL) + return true; + // Verify blocks in the best chain - int nCheckLevel = GetArg("-checklevel", 1); - int nCheckDepth = GetArg( "-checkblocks", 2500); + int nCheckLevel = GetArg("-checklevel", 3); + int nCheckDepth = GetArg( "-checkblocks", 288); if (nCheckDepth == 0) nCheckDepth = 1000000000; // suffices until the year 19000 if (nCheckDepth > nBestHeight) nCheckDepth = nBestHeight; + nCheckLevel = std::max(0, std::min(4, nCheckLevel)); printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); - CBlockIndex* pindexFork = NULL; + CCoinsViewCache coins(*pcoinsTip, true); + CBlockIndex* pindexState = pindexBest; + CBlockIndex* pindexFailure = NULL; + int nGoodTransactions = 0; + CValidationState state; for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) { if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth) break; CBlock block; + // check level 0: read from disk if (!block.ReadFromDisk(pindex)) - return error("LoadBlockIndex() : block.ReadFromDisk failed"); + return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); // check level 1: verify block validity - if (nCheckLevel>0 && !block.CheckBlock()) - { - printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); - pindexFork = pindex->pprev; + if (nCheckLevel >= 1 && !block.CheckBlock(state)) + return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + // check level 2: verify undo validity + if (nCheckLevel >= 2 && pindex) { + CBlockUndo undo; + CDiskBlockPos pos = pindex->GetUndoPos(); + if (!pos.IsNull()) { + if (!undo.ReadFromDisk(pos, pindex->pprev->GetBlockHash())) + return error("VerifyDB() : *** found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + } + } + // check level 3: check for inconsistencies during memory-only disconnect of tip blocks + if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= 2*nCoinCacheSize + 32000) { + bool fClean = true; + if (!block.DisconnectBlock(state, pindex, coins, &fClean)) + return error("VerifyDB() : *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + pindexState = pindex->pprev; + if (!fClean) { + nGoodTransactions = 0; + pindexFailure = pindex; + } else + nGoodTransactions += block.vtx.size(); } - // TODO: stronger verifications } - if (pindexFork && !fRequestShutdown) - { - // TODO: reorg back - return error("LoadBlockIndex(): chain database corrupted"); + if (pindexFailure) + return error("VerifyDB() : *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", pindexBest->nHeight - pindexFailure->nHeight + 1, nGoodTransactions); + + // check level 4: try reconnecting blocks + if (nCheckLevel >= 4) { + CBlockIndex *pindex = pindexState; + while (pindex != pindexBest && !fRequestShutdown) { + pindex = pindex->pnext; + CBlock block; + if (!block.ReadFromDisk(pindex)) + return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + if (!block.ConnectBlock(state, pindex, coins)) + return error("VerifyDB() : *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + } } + printf("No coin database inconsistencies in last %i blocks (%i transactions)\n", pindexBest->nHeight - pindexState->nHeight, nGoodTransactions); + return true; } +void UnloadBlockIndex() +{ + mapBlockIndex.clear(); + setBlockIndexValid.clear(); + pindexGenesisBlock = NULL; + nBestHeight = 0; + bnBestChainWork = 0; + bnBestInvalidWork = 0; + hashBestChain = 0; + pindexBest = NULL; +} + bool LoadBlockIndex() { if (fTestNet) @@ -2421,20 +2676,28 @@ bool LoadBlockIndex() hashGenesisBlock = uint256("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"); } - if (fReindex) - return true; - // // Load block index from databases // - if (!LoadBlockIndexDB()) + if (!fReindex && !LoadBlockIndexDB()) return false; - // - // Init with genesis block - // - if (mapBlockIndex.empty()) - { + return true; +} + + +bool InitBlockIndex() { + // Check whether we're already initialized + if (pindexGenesisBlock != NULL) + return true; + + // Use the provided setting for -txindex in the new database + fTxIndex = GetBoolArg("-txindex", false); + pblocktree->WriteFlag("txindex", fTxIndex); + printf("Initializing databases...\n"); + + // Only add the genesis block if not reindexing (in which case we reuse the one already on disk) + if (!fReindex) { // Genesis Block: // CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, vtx=1) // CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0) @@ -2475,14 +2738,19 @@ bool LoadBlockIndex() assert(hash == hashGenesisBlock); // Start new block file - unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); - CDiskBlockPos blockPos; - if (!FindBlockPos(blockPos, nBlockSize+8, 0, block.nTime)) - return error("AcceptBlock() : FindBlockPos failed"); - if (!block.WriteToDisk(blockPos)) - return error("LoadBlockIndex() : writing genesis block to disk failed"); - if (!block.AddToBlockIndex(blockPos)) - return error("LoadBlockIndex() : genesis block not accepted"); + try { + unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); + CDiskBlockPos blockPos; + CValidationState state; + if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.nTime)) + return error("AcceptBlock() : FindBlockPos failed"); + if (!block.WriteToDisk(blockPos)) + return error("LoadBlockIndex() : writing genesis block to disk failed"); + if (!block.AddToBlockIndex(state, blockPos)) + return error("LoadBlockIndex() : genesis block not accepted"); + } catch(std::runtime_error &e) { + return error("LoadBlockIndex() : failed to initialize block database: %s", e.what()); + } } return true; @@ -2565,7 +2833,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) int64 nStart = GetTimeMillis(); int nLoaded = 0; - { + try { CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION); uint64 nStartByte = 0; if (dbp) { @@ -2611,14 +2879,19 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) LOCK(cs_main); if (dbp) dbp->nPos = nBlockPos; - if (ProcessBlock(NULL, &block, dbp)) + CValidationState state; + if (ProcessBlock(state, NULL, &block, dbp)) nLoaded++; + if (state.IsError()) + break; } } catch (std::exception &e) { printf("%s() : Deserialize or I/O error caught during load\n", __PRETTY_FUNCTION__); } } fclose(fileIn); + } catch(std::runtime_error &e) { + AbortNode(_("Error: system error: ") + e.what()); } if (nLoaded > 0) printf("Loaded %i blocks from external file in %"PRI64d"ms\n", nLoaded, GetTimeMillis() - nStart); @@ -2780,6 +3053,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vRecv >> pfrom->strSubVer; if (!vRecv.empty()) vRecv >> pfrom->nStartingHeight; + if (!vRecv.empty()) + vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message + else + pfrom->fRelayTxes = true; if (pfrom->fInbound && addrMe.IsRoutable()) { @@ -3003,6 +3280,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (fDebugNet || (vInv.size() != 1)) printf("received getdata (%"PRIszu" invsz)\n", vInv.size()); + vector<CInv> vNotFound; BOOST_FOREACH(const CInv& inv, vInv) { if (fShutdown) @@ -3010,7 +3288,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (fDebugNet || (vInv.size() == 1)) printf("received getdata for: %s\n", inv.ToString().c_str()); - if (inv.type == MSG_BLOCK) + if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) { // Send block from disk map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(inv.hash); @@ -3018,7 +3296,29 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { CBlock block; block.ReadFromDisk((*mi).second); - pfrom->PushMessage("block", block); + if (inv.type == MSG_BLOCK) + pfrom->PushMessage("block", block); + else // MSG_FILTERED_BLOCK) + { + LOCK(pfrom->cs_filter); + if (pfrom->pfilter) + { + CMerkleBlock merkleBlock(block, *pfrom->pfilter); + pfrom->PushMessage("merkleblock", merkleBlock); + // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see + // This avoids hurting performance by pointlessly requiring a round-trip + // Note that there is currently no way for a node to request any single transactions we didnt send here - + // they must either disconnect and retry or request the full block. + // Thus, the protocol spec specified allows for us to provide duplicate txn here, + // however we MUST always provide at least what the remote peer needs + typedef std::pair<unsigned int, uint256> PairType; + BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn) + if (!pfrom->setInventoryKnown.count(CInv(MSG_TX, pair.second))) + pfrom->PushMessage("tx", block.vtx[pair.first]); + } + // else + // no response + } // Trigger them to send a getblocks request for the next batch of inventory if (inv.hash == pfrom->hashContinue) @@ -3053,12 +3353,27 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) ss.reserve(1000); ss << tx; pfrom->PushMessage("tx", ss); + pushed = true; } } + if (!pushed) { + vNotFound.push_back(inv); + } } - // Track requests for our stuff + // Track requests for our stuff. Inventory(inv.hash); + + if (!vNotFound.empty()) { + // Let the peer know that we didn't find what it asked for, so it doesn't + // have to wait around forever. Currently only SPV clients actually care + // about this message: it's needed when they are recursively walking the + // dependencies of relevant unconfirmed transactions. SPV clients want to + // do that because they want to know about (and store and rebroadcast and + // risk analyze) the dependencies of transactions relevant to them, without + // having to download the entire memory pool. + pfrom->PushMessage("notfound", vNotFound); + } } } @@ -3146,10 +3461,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->AddInventoryKnown(inv); bool fMissingInputs = false; - if (tx.AcceptToMemoryPool(true, &fMissingInputs)) + CValidationState state; + if (tx.AcceptToMemoryPool(state, true, true, &fMissingInputs)) { - SyncWithWallets(inv.hash, tx, NULL, true); - RelayMessage(inv, vMsg); + RelayTransaction(tx, inv.hash, vMsg); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); vEraseQueue.push_back(inv.hash); @@ -3167,21 +3482,22 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CDataStream(vMsg) >> tx; CInv inv(MSG_TX, tx.GetHash()); bool fMissingInputs2 = false; + // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get anyone relaying LegitTxX banned) + CValidationState stateDummy; - if (tx.AcceptToMemoryPool(true, &fMissingInputs2)) + if (tx.AcceptToMemoryPool(stateDummy, true, true, &fMissingInputs2)) { printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); - SyncWithWallets(inv.hash, tx, NULL, true); - RelayMessage(inv, vMsg); + RelayTransaction(tx, inv.hash, vMsg); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); vEraseQueue.push_back(inv.hash); } else if (!fMissingInputs2) { - // invalid orphan + // invalid or too-little-fee orphan vEraseQueue.push_back(inv.hash); - printf(" removed invalid orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); + printf(" removed orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); } } } @@ -3198,7 +3514,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (nEvicted > 0) printf("mapOrphan overflow, removed %u tx\n", nEvicted); } - if (tx.nDoS) pfrom->Misbehaving(tx.nDoS); + int nDoS; + if (state.IsInvalid(nDoS)) + pfrom->Misbehaving(nDoS); } @@ -3213,9 +3531,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CInv inv(MSG_BLOCK, block.GetHash()); pfrom->AddInventoryKnown(inv); - if (ProcessBlock(pfrom, &block)) + CValidationState state; + if (ProcessBlock(state, pfrom, &block)) mapAlreadyAskedFor.erase(inv); - if (block.nDoS) pfrom->Misbehaving(block.nDoS); + int nDoS; + if (state.IsInvalid(nDoS)) + pfrom->Misbehaving(nDoS); } @@ -3231,13 +3552,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) else if (strCommand == "mempool") { std::vector<uint256> vtxid; + LOCK2(mempool.cs, pfrom->cs_filter); mempool.queryHashes(vtxid); vector<CInv> vInv; - for (unsigned int i = 0; i < vtxid.size(); i++) { - CInv inv(MSG_TX, vtxid[i]); - vInv.push_back(inv); - if (i == (MAX_INV_SZ - 1)) - break; + BOOST_FOREACH(uint256& hash, vtxid) { + CInv inv(MSG_TX, hash); + if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(mempool.lookup(hash), hash)) || + (!pfrom->pfilter)) + vInv.push_back(inv); + if (vInv.size() == MAX_INV_SZ) + break; } if (vInv.size() > 0) pfrom->PushMessage("inv", vInv); @@ -3297,6 +3621,53 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } + else if (strCommand == "filterload") + { + CBloomFilter filter; + vRecv >> filter; + + if (!filter.IsWithinSizeConstraints()) + // There is no excuse for sending a too-large filter + pfrom->Misbehaving(100); + else + { + LOCK(pfrom->cs_filter); + delete pfrom->pfilter; + pfrom->pfilter = new CBloomFilter(filter); + } + pfrom->fRelayTxes = true; + } + + + else if (strCommand == "filteradd") + { + vector<unsigned char> vData; + vRecv >> vData; + + // Nodes must NEVER send a data item > 520 bytes (the max size for a script data object, + // and thus, the maximum size any matched object can have) in a filteradd message + if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) + { + pfrom->Misbehaving(100); + } else { + LOCK(pfrom->cs_filter); + if (pfrom->pfilter) + pfrom->pfilter->insert(vData); + else + pfrom->Misbehaving(100); + } + } + + + else if (strCommand == "filterclear") + { + LOCK(pfrom->cs_filter); + delete pfrom->pfilter; + pfrom->pfilter = NULL; + pfrom->fRelayTxes = true; + } + + else { // Ignore unknown commands for extensibility @@ -3453,7 +3824,12 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } // Resend wallet transactions that haven't gotten in a block yet - ResendWalletTransactions(); + // Except during reindex, importing and IBD, when old wallet + // transactions become unconfirmed and spams other nodes. + if (!fReindex && !fImporting && !IsInitialBlockDownload()) + { + ResendWalletTransactions(); + } // Address refresh broadcast static int64 nLastRebroadcast; @@ -3732,12 +4108,13 @@ public: } }; -CBlock* CreateNewBlock(CReserveKey& reservekey) +CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) { // Create new block - auto_ptr<CBlock> pblock(new CBlock()); - if (!pblock.get()) + auto_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate()); + if(!pblocktemplate.get()) return NULL; + CBlock *pblock = &pblocktemplate->block; // pointer for convenience // Create coinbase tx CTransaction txNew; @@ -3748,6 +4125,8 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) // Add our coinbase tx as first transaction pblock->vtx.push_back(txNew); + pblocktemplate->vTxFees.push_back(-1); // updated at end + pblocktemplate->vTxSigOps.push_back(-1); // updated at end // Largest block you're willing to create: unsigned int nBlockMaxSize = GetArg("-blockmaxsize", MAX_BLOCK_SIZE_GEN/2); @@ -3912,12 +4291,13 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) continue; - if (!tx.CheckInputs(viewTemp, CS_ALWAYS, SCRIPT_VERIFY_P2SH)) + CValidationState state; + if (!tx.CheckInputs(state, viewTemp, true, SCRIPT_VERIFY_P2SH)) continue; CTxUndo txundo; uint256 hash = tx.GetHash(); - if (!tx.UpdateCoins(viewTemp, txundo, pindexPrev->nHeight+1, hash)) + if (!tx.UpdateCoins(state, viewTemp, txundo, pindexPrev->nHeight+1, hash)) continue; // push changes from the second layer cache to the first one @@ -3925,6 +4305,8 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) // Added pblock->vtx.push_back(tx); + pblocktemplate->vTxFees.push_back(nTxFees); + pblocktemplate->vTxSigOps.push_back(nTxSigOps); nBlockSize += nTxSize; ++nBlockTx; nBlockSigOps += nTxSigOps; @@ -3959,23 +4341,26 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) printf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize); pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees); + pblocktemplate->vTxFees[0] = -nFees; // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); pblock->UpdateTime(pindexPrev); - pblock->nBits = GetNextWorkRequired(pindexPrev, pblock.get()); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock); pblock->nNonce = 0; pblock->vtx[0].vin[0].scriptSig = CScript() << OP_0 << OP_0; + pblocktemplate->vTxSigOps[0] = pblock->vtx[0].GetLegacySigOpCount(); CBlockIndex indexDummy(*pblock); indexDummy.pprev = pindexPrev; indexDummy.nHeight = pindexPrev->nHeight + 1; CCoinsViewCache viewNew(*pcoinsTip, true); - if (!pblock->ConnectBlock(&indexDummy, viewNew, true)) + CValidationState state; + if (!pblock->ConnectBlock(state, &indexDummy, viewNew, true)) throw std::runtime_error("CreateNewBlock() : ConnectBlock failed"); } - return pblock.release(); + return pblocktemplate.release(); } @@ -4073,7 +4458,8 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) } // Process this block the same as if we had received it from another node - if (!ProcessBlock(NULL, pblock)) + CValidationState state; + if (!ProcessBlock(state, NULL, pblock)) return error("BitcoinMiner : ProcessBlock, block not accepted"); } @@ -4118,10 +4504,11 @@ void static BitcoinMiner(CWallet *pwallet) unsigned int nTransactionsUpdatedLast = nTransactionsUpdated; CBlockIndex* pindexPrev = pindexBest; - auto_ptr<CBlock> pblock(CreateNewBlock(reservekey)); - if (!pblock.get()) + auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlock(reservekey)); + if (!pblocktemplate.get()) return; - IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce); + CBlock *pblock = &pblocktemplate->block; + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); printf("Running BitcoinMiner with %"PRIszu" transactions in block (%u bytes)\n", pblock->vtx.size(), ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); @@ -4134,7 +4521,7 @@ void static BitcoinMiner(CWallet *pwallet) char pdatabuf[128+16]; char* pdata = alignup<16>(pdatabuf); char phash1buf[64+16]; char* phash1 = alignup<16>(phash1buf); - FormatHashBuffers(pblock.get(), pmidstate, pdata, phash1); + FormatHashBuffers(pblock, pmidstate, pdata, phash1); unsigned int& nBlockTime = *(unsigned int*)(pdata + 64 + 4); unsigned int& nBlockBits = *(unsigned int*)(pdata + 64 + 8); @@ -4170,7 +4557,7 @@ void static BitcoinMiner(CWallet *pwallet) assert(hash == pblock->GetHash()); SetThreadPriority(THREAD_PRIORITY_NORMAL); - CheckWork(pblock.get(), *pwalletMain, reservekey); + CheckWork(pblock, *pwalletMain, reservekey); SetThreadPriority(THREAD_PRIORITY_LOWEST); break; } |