diff options
Diffstat (limited to 'src/validation.cpp')
| -rw-r--r-- | src/validation.cpp | 161 |
1 files changed, 105 insertions, 56 deletions
diff --git a/src/validation.cpp b/src/validation.cpp index 2ad3dadeb..fe8f8365b 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -46,8 +46,6 @@ #include <boost/math/distributions/poisson.hpp> #include <boost/thread.hpp> -using namespace std; - #if defined(NDEBUG) # error "Bitcoin cannot be compiled without assertions." #endif @@ -90,7 +88,7 @@ static void CheckBlockIndex(const Consensus::Params& consensusParams); /** Constant stuff for coinbase transactions we create: */ CScript COINBASE_FLAGS; -const string strMessageMagic = "Bitcoin Signed Message:\n"; +const std::string strMessageMagic = "Bitcoin Signed Message:\n"; // Internal stuff namespace { @@ -123,11 +121,11 @@ namespace { * as good as our current tip or better. Entries may be failed, though, and pruning nodes may be * missing the data for the block. */ - set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates; + std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates; /** All pairs A->B, where A (or one of its ancestors) misses transactions, but B has transactions. * Pruned nodes may have entries where B is missing data. */ - multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked; + std::multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked; CCriticalSection cs_LastBlockFile; std::vector<CBlockFileInfo> vinfoBlockFile; @@ -151,12 +149,45 @@ namespace { arith_uint256 nLastPreciousChainwork = 0; /** Dirty block index entries. */ - set<CBlockIndex*> setDirtyBlockIndex; + std::set<CBlockIndex*> setDirtyBlockIndex; /** Dirty block file entries. */ - set<int> setDirtyFileInfo; + std::set<int> setDirtyFileInfo; } // anon namespace +/* Use this class to start tracking transactions that are removed from the + * mempool and pass all those transactions through SyncTransaction when the + * object goes out of scope. This is currently only used to call SyncTransaction + * on conflicts removed from the mempool during block connection. Applied in + * ActivateBestChain around ActivateBestStep which in turn calls: + * ConnectTip->removeForBlock->removeConflicts + */ +class MemPoolConflictRemovalTracker +{ +private: + std::vector<CTransactionRef> conflictedTxs; + CTxMemPool &pool; + +public: + MemPoolConflictRemovalTracker(CTxMemPool &_pool) : pool(_pool) { + pool.NotifyEntryRemoved.connect(boost::bind(&MemPoolConflictRemovalTracker::NotifyEntryRemoved, this, _1, _2)); + } + + void NotifyEntryRemoved(CTransactionRef txRemoved, MemPoolRemovalReason reason) { + if (reason == MemPoolRemovalReason::CONFLICT) { + conflictedTxs.push_back(txRemoved); + } + } + + ~MemPoolConflictRemovalTracker() { + pool.NotifyEntryRemoved.disconnect(boost::bind(&MemPoolConflictRemovalTracker::NotifyEntryRemoved, this, _1, _2)); + for (const auto& tx : conflictedTxs) { + GetMainSignals().SyncTransaction(*tx, NULL, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK); + } + conflictedTxs.clear(); + } +}; + CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) { // Find the first block the caller has in the main chain @@ -484,7 +515,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe // Check for duplicate inputs - note that this check is slow so we skip it in CheckBlock if (fCheckDuplicateInputs) { - set<COutPoint> vInOutPoints; + std::set<COutPoint> vInOutPoints; for (const auto& txin : tx.vin) { if (!vInOutPoints.insert(txin.prevout).second) @@ -540,8 +571,8 @@ static bool IsCurrentForFeeEstimation() } bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool fLimitFree, - bool* pfMissingInputs, int64_t nAcceptTime, bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, - std::vector<uint256>& vHashTxnToUncache) + bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced, + bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, std::vector<uint256>& vHashTxnToUncache) { const CTransaction& tx = *ptx; const uint256 hash = tx.GetHash(); @@ -563,7 +594,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C } // Rather not work on nonstandard transactions (unless -testnet/-regtest) - string reason; + std::string reason; if (fRequireStandard && !IsStandardTx(tx, reason, witnessEnabled)) return state.DoS(0, false, REJECT_NONSTANDARD, reason); @@ -578,7 +609,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-in-mempool"); // Check for conflicts with in-memory transactions - set<uint256> setConflicts; + std::set<uint256> setConflicts; { LOCK(pool.cs); // protect pool.mapNextTx BOOST_FOREACH(const CTxIn &txin, tx.vin) @@ -794,10 +825,11 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C // subsequent RemoveStaged() and addUnchecked() calls don't guarantee // mempool consistency for us. LOCK(pool.cs); - if (setConflicts.size()) + const bool fReplacementTransaction = setConflicts.size(); + if (fReplacementTransaction) { CFeeRate newFeeRate(nModifiedFees, nSize); - set<uint256> setConflictsParents; + std::set<uint256> setConflictsParents; const int maxDescendantsToVisit = 100; CTxMemPool::setEntries setIterConflicting; BOOST_FOREACH(const uint256 &hashConflicting, setConflicts) @@ -898,14 +930,14 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C // Finally in addition to paying more fees than the conflicts the // new transaction must pay for its own bandwidth. CAmount nDeltaFees = nModifiedFees - nConflictingFees; - if (nDeltaFees < ::minRelayTxFee.GetFee(nSize)) + if (nDeltaFees < ::incrementalRelayFee.GetFee(nSize)) { return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient fee", false, strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s", hash.ToString(), FormatMoney(nDeltaFees), - FormatMoney(::minRelayTxFee.GetFee(nSize)))); + FormatMoney(::incrementalRelayFee.GetFee(nSize)))); } } @@ -921,12 +953,13 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C // SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we // need to turn both off, and compare against just turning off CLEANSTACK // to see if the failure is specifically due to witness validation. - if (!tx.HasWitness() && CheckInputs(tx, state, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, txdata) && - !CheckInputs(tx, state, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, txdata)) { + CValidationState stateDummy; // Want reported failures to be from first CheckInputs + if (!tx.HasWitness() && CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, txdata) && + !CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, txdata)) { // Only the witness is missing, so the transaction itself may be fine. state.SetCorruptionPossible(); } - return false; + return false; // state filled in by CheckInputs } // Check again against just the consensus-critical mandatory script @@ -952,13 +985,16 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C hash.ToString(), FormatMoney(nModifiedFees - nConflictingFees), (int)nSize - (int)nConflictingSize); + if (plTxnReplaced) + plTxnReplaced->push_back(it->GetSharedTx()); } - pool.RemoveStaged(allConflicting, false); + pool.RemoveStaged(allConflicting, false, MemPoolRemovalReason::REPLACED); - // This transaction should only count for fee estimation if - // the node is not behind and it is not dependent on any other - // transactions in the mempool - bool validForFeeEstimation = IsCurrentForFeeEstimation() && pool.HasNoInputsOf(tx); + // This transaction should only count for fee estimation if it isn't a + // BIP 125 replacement transaction (may not be widely supported), the + // node is not behind, and the transaction is not dependent on any other + // transactions in the mempool. + bool validForFeeEstimation = !fReplacementTransaction && IsCurrentForFeeEstimation() && pool.HasNoInputsOf(tx); // Store transaction in memory pool.addUnchecked(hash, entry, setAncestors, validForFeeEstimation); @@ -977,10 +1013,11 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C } bool AcceptToMemoryPoolWithTime(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, - bool* pfMissingInputs, int64_t nAcceptTime, bool fOverrideMempoolLimit, const CAmount nAbsurdFee) + bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced, + bool fOverrideMempoolLimit, const CAmount nAbsurdFee) { std::vector<uint256> vHashTxToUncache; - bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, nAcceptTime, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache); + bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, nAcceptTime, plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache); if (!res) { BOOST_FOREACH(const uint256& hashTx, vHashTxToUncache) pcoinsTip->Uncache(hashTx); @@ -992,9 +1029,10 @@ bool AcceptToMemoryPoolWithTime(CTxMemPool& pool, CValidationState &state, const } bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, - bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount nAbsurdFee) + bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced, + bool fOverrideMempoolLimit, const CAmount nAbsurdFee) { - return AcceptToMemoryPoolWithTime(pool, state, tx, fLimitFree, pfMissingInputs, GetTime(), fOverrideMempoolLimit, nAbsurdFee); + return AcceptToMemoryPoolWithTime(pool, state, tx, fLimitFree, pfMissingInputs, GetTime(), plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee); } /** Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock */ @@ -2012,18 +2050,18 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode, int n { std::vector<std::pair<int, const CBlockFileInfo*> > vFiles; vFiles.reserve(setDirtyFileInfo.size()); - for (set<int>::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) { - vFiles.push_back(make_pair(*it, &vinfoBlockFile[*it])); + for (std::set<int>::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) { + vFiles.push_back(std::make_pair(*it, &vinfoBlockFile[*it])); setDirtyFileInfo.erase(it++); } std::vector<const CBlockIndex*> vBlocks; vBlocks.reserve(setDirtyBlockIndex.size()); - for (set<CBlockIndex*>::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) { + for (std::set<CBlockIndex*>::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) { vBlocks.push_back(*it); setDirtyBlockIndex.erase(it++); } if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) { - return AbortNode(state, "Files to write to block index database"); + return AbortNode(state, "Failed to write to block index database"); } } // Finally remove any pruned files @@ -2160,8 +2198,8 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara const CTransaction& tx = *it; // ignore validation errors in resurrected transactions CValidationState stateDummy; - if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, it, false, NULL, true)) { - mempool.removeRecursive(tx); + if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, it, false, NULL, NULL, true)) { + mempool.removeRecursive(tx, MemPoolRemovalReason::REORG); } else if (mempool.exists(tx.GetHash())) { vHashUpdate.push_back(tx.GetHash()); } @@ -2448,6 +2486,14 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, bool fInitialDownload; { LOCK(cs_main); + { // TODO: Tempoarily ensure that mempool removals are notified before + // connected transactions. This shouldn't matter, but the abandoned + // state of transactions in our wallet is currently cleared when we + // receive another notification and there is a race condition where + // notification of a connected conflict might cause an outside process + // to abandon a transaction and then have it inadvertantly cleared by + // the notification that the conflicted transaction was evicted. + MemPoolConflictRemovalTracker mrt(mempool); CBlockIndex *pindexOldTip = chainActive.Tip(); if (pindexMostWork == NULL) { pindexMostWork = FindMostWorkChain(); @@ -2469,20 +2515,23 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, pindexNewTip = chainActive.Tip(); pindexFork = chainActive.FindFork(pindexOldTip); fInitialDownload = IsInitialBlockDownload(); + + // throw all transactions though the signal-interface + + } // MemPoolConflictRemovalTracker destroyed and conflict evictions are notified + + // Transactions in the connnected block are notified + for (const auto& pair : connectTrace.blocksConnected) { + assert(pair.second); + const CBlock& block = *(pair.second); + for (unsigned int i = 0; i < block.vtx.size(); i++) + GetMainSignals().SyncTransaction(*block.vtx[i], pair.first, i); + } } // When we reach this point, we switched to a new tip (stored in pindexNewTip). // Notifications/callbacks that can run without cs_main - // throw all transactions though the signal-interface - // while _not_ holding the cs_main lock - for (const auto& pair : connectTrace.blocksConnected) { - assert(pair.second); - const CBlock& block = *(pair.second); - for (unsigned int i = 0; i < block.vtx.size(); i++) - GetMainSignals().SyncTransaction(*block.vtx[i], pair.first, i); - } - // Notify external listeners about the new tip. GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload); @@ -2619,7 +2668,7 @@ CBlockIndex* AddToBlockIndex(const CBlockHeader& block) // to avoid miners withholding blocks but broadcasting headers, to get a // competitive advantage. pindexNew->nSequenceId = 0; - BlockMap::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; + BlockMap::iterator mi = mapBlockIndex.insert(std::make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); BlockMap::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock); if (miPrev != mapBlockIndex.end()) @@ -2656,7 +2705,7 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl if (pindexNew->pprev == NULL || pindexNew->pprev->nChainTx) { // If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS. - deque<CBlockIndex*> queue; + std::deque<CBlockIndex*> queue; queue.push_back(pindexNew); // Recursively process any descendant blocks that now may be eligible to be connected. @@ -3278,7 +3327,7 @@ void PruneOneBlockFile(const int fileNumber) void UnlinkPrunedFiles(std::set<int>& setFilesToPrune) { - for (set<int>::iterator it = setFilesToPrune.begin(); it != setFilesToPrune.end(); ++it) { + for (std::set<int>::iterator it = setFilesToPrune.begin(); it != setFilesToPrune.end(); ++it) { CDiskBlockPos pos(*it, 0); boost::filesystem::remove(GetBlockPosFilename(pos, "blk")); boost::filesystem::remove(GetBlockPosFilename(pos, "rev")); @@ -3296,7 +3345,7 @@ void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeig return; // last block to prune is the lesser of (user-specified height, MIN_BLOCKS_TO_KEEP from the tip) - unsigned int nLastBlockWeCanPrune = min((unsigned)nManualPruneHeight, chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP); + unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP); int count=0; for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) { if (vinfoBlockFile[fileNumber].nSize == 0 || vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) @@ -3423,8 +3472,8 @@ CBlockIndex * InsertBlockIndex(uint256 hash) // Create new CBlockIndex* pindexNew = new CBlockIndex(); if (!pindexNew) - throw runtime_error(std::string(__func__) + ": new CBlockIndex failed"); - mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; + throw std::runtime_error(std::string(__func__) + ": new CBlockIndex failed"); + mi = mapBlockIndex.insert(std::make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); return pindexNew; @@ -3438,12 +3487,12 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) boost::this_thread::interruption_point(); // Calculate nChainWork - vector<pair<int, CBlockIndex*> > vSortedByHeight; + std::vector<std::pair<int, CBlockIndex*> > vSortedByHeight; vSortedByHeight.reserve(mapBlockIndex.size()); BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) { CBlockIndex* pindex = item.second; - vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex)); + vSortedByHeight.push_back(std::make_pair(pindex->nHeight, pindex)); } sort(vSortedByHeight.begin(), vSortedByHeight.end()); BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight) @@ -3494,7 +3543,7 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) // Check presence of blk files LogPrintf("Checking all blk files are present...\n"); - set<int> setBlkDataFiles; + std::set<int> setBlkDataFiles; BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) { CBlockIndex* pindex = item.second; @@ -3593,7 +3642,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); // check level 1: verify block validity if (nCheckLevel >= 1 && !CheckBlock(block, state, chainparams.GetConsensus())) - return error("%s: *** found bad block at %d, hash=%s (%s)\n", __func__, + return error("%s: *** found bad block at %d, hash=%s (%s)\n", __func__, pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state)); // check level 2: verify undo validity if (nCheckLevel >= 2 && pindex) { @@ -3764,7 +3813,7 @@ bool LoadBlockIndex(const CChainParams& chainparams) return true; } -bool InitBlockIndex(const CChainParams& chainparams) +bool InitBlockIndex(const CChainParams& chainparams) { LOCK(cs_main); @@ -3881,7 +3930,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB NotifyHeaderTip(); // Recursively process earlier encountered successors of this block - deque<uint256> queue; + std::deque<uint256> queue; queue.push_back(hash); while (!queue.empty()) { uint256 head = queue.front(); @@ -4198,7 +4247,7 @@ void DumpMempool(void) { LOCK(mempool.cs); for (const auto &i : mempool.mapDeltas) { - mapDeltas[i.first] = i.second.first; + mapDeltas[i.first] = i.second.second; } vinfo = mempool.infoAll(); } |