From 79d422acfa901efdea0989f789c600252c4f9c19 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 18 Mar 2016 22:39:41 -0700 Subject: [PATCH] Add withdraw-claimed tracking to mempool and enforce it --- src/main.cpp | 18 +++++++++++++++++- src/test/test_bitcoin.cpp | 3 ++- src/txmempool.cpp | 33 +++++++++++++++++++++++++++++++-- src/txmempool.h | 7 ++++++- 4 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 81142a578e..d41ae6f55a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1228,6 +1228,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C { CCoinsView dummy; CCoinsViewCache view(&dummy); + std::set > setWithdrawsSpent; CAmount nValueIn = 0; LockPoints lp; @@ -1261,6 +1262,18 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C if (!view.HaveInputs(tx)) return state.Invalid(false, REJECT_DUPLICATE, "bad-txns-inputs-spent"); + BOOST_FOREACH(const CTxIn &txin, tx.vin) + { + CCoins coins; + assert(view.GetCoins(txin.prevout.hash, coins)); + if (coins.vout[txin.prevout.n].scriptPubKey.IsWithdrawLock() && txin.scriptSig.IsWithdrawProof()) { + pair outpoint = make_pair(coins.vout[txin.prevout.n].scriptPubKey.GetWithdrawLockGenesisHash(), txin.scriptSig.GetWithdrawSpent()); + if (view.IsWithdrawSpent(outpoint)) + return state.Invalid(false, REJECT_CONFLICT, "withdraw-already-claimed"); + setWithdrawsSpent.insert(outpoint); + } + } + // Bring the best block into scope view.GetBestBlock(); @@ -1309,7 +1322,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C } } - CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOpsCost, lp); + CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOpsCost, lp, setWithdrawsSpent); unsigned int nSize = entry.GetTxSize(); // Check that the transaction doesn't have an excessive number of @@ -1533,6 +1546,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C return false; } + assert(setWithdrawsSpent2 == setWithdrawsSpent); setWithdrawsSpent2.clear(); // Check again against just the consensus-critical mandatory script @@ -1550,6 +1564,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C __func__, hash.ToString(), FormatStateMessage(state)); } + assert(setWithdrawsSpent2 == setWithdrawsSpent); + // Remove conflicting transactions from the mempool BOOST_FOREACH(const CTxMemPool::txiter it, allConflicting) { diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index f0918f4d8e..3fccb39f99 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -140,8 +140,9 @@ CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CTransaction &txn, CTxMemPool *po // Hack to assume either its completely dependent on other mempool txs or not at all CAmount inChainValue = hasNoDependencies ? txn.GetValueOut() : 0; + std::set > mapWithdrawsSpent; return CTxMemPoolEntry(txn, nFee, nTime, dPriority, nHeight, - hasNoDependencies, inChainValue, spendsCoinbase, sigOpCost, lp); + hasNoDependencies, inChainValue, spendsCoinbase, sigOpCost, lp, mapWithdrawsSpent); } void Shutdown(void* parg) diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 8204431b3a..3880d48f43 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -23,10 +23,10 @@ using namespace std; CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, int64_t _nTime, double _entryPriority, unsigned int _entryHeight, bool poolHasNoInputsOf, CAmount _inChainInputValue, - bool _spendsCoinbase, int64_t _sigOpsCost, LockPoints lp): + bool _spendsCoinbase, int64_t _sigOpsCost, LockPoints lp, std::set >& _setWithdrawsSpent): tx(std::make_shared(_tx)), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight), hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue), - spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp) + spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp), setWithdrawsSpent(_setWithdrawsSpent) { nTxWeight = GetTransactionWeight(_tx); nModSize = _tx.CalculateModifiedSize(GetTxSize()); @@ -447,6 +447,10 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, vTxHashes.emplace_back(tx.GetWitnessHash(), newit); newit->vTxHashesIdx = vTxHashes.size() - 1; + typedef std::pair WithdrawPair; + BOOST_FOREACH(const WithdrawPair& it, entry.setWithdrawsSpent) + assert(mapWithdrawsSpentToTxid.insert(std::make_pair(it, hash)).second); + return true; } @@ -456,6 +460,10 @@ void CTxMemPool::removeUnchecked(txiter it) BOOST_FOREACH(const CTxIn& txin, it->GetTx().vin) mapNextTx.erase(txin.prevout); + typedef std::pair WithdrawPair; + BOOST_FOREACH(const WithdrawPair& it2, it->setWithdrawsSpent) + assert(mapWithdrawsSpentToTxid.erase(it2)); + if (vTxHashes.size() > 1) { vTxHashes[it->vTxHashesIdx] = std::move(vTxHashes.back()); vTxHashes[it->vTxHashesIdx].second->vTxHashesIdx = it->vTxHashesIdx; @@ -660,6 +668,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const LOCK(cs); list waitingOnDependants; + set > setGlobalWithdrawsSpent; for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { unsigned int i = 0; checkTotal += it->GetTxSize(); @@ -741,6 +750,10 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const std::set > setWithdrawsSpent; assert(CheckInputs(tx, state, mempoolDuplicate, false, 0, false, txdata, setWithdrawsSpent, NULL)); UpdateCoins(tx, mempoolDuplicate, 1000000); + assert(setWithdrawsSpent == it->setWithdrawsSpent); + size_t prevWithdrawsCount = setGlobalWithdrawsSpent.size(); + setGlobalWithdrawsSpent.insert(setWithdrawsSpent.begin(), setWithdrawsSpent.end()); + assert(setGlobalWithdrawsSpent.size() == prevWithdrawsCount + setWithdrawsSpent.size()); } } unsigned int stepsSinceLastRemove = 0; @@ -757,6 +770,10 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const std::set > setWithdrawsSpent; assert(CheckInputs(entry->GetTx(), state, mempoolDuplicate, false, 0, false, txdata, setWithdrawsSpent, NULL)); UpdateCoins(entry->GetTx(), mempoolDuplicate, 1000000); + assert(setWithdrawsSpent == entry->setWithdrawsSpent); + size_t prevWithdrawsCount = setGlobalWithdrawsSpent.size(); + setGlobalWithdrawsSpent.insert(setWithdrawsSpent.begin(), setWithdrawsSpent.end()); + assert(setGlobalWithdrawsSpent.size() == prevWithdrawsCount + setWithdrawsSpent.size()); stepsSinceLastRemove = 0; } } @@ -768,6 +785,14 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const assert(&tx == it->second); } + for (std::set >::const_iterator it = setGlobalWithdrawsSpent.begin(); it != setGlobalWithdrawsSpent.end(); it++) { + assert(!pcoins->IsWithdrawSpent(*it)); + } + for (std::map, uint256>::const_iterator it = mapWithdrawsSpentToTxid.begin(); it != mapWithdrawsSpentToTxid.end(); it++) { + assert(setGlobalWithdrawsSpent.erase(it->first)); + } + assert(setGlobalWithdrawsSpent.size() == 0); + assert(totalTxSize == checkTotal); assert(innerUsage == cachedInnerUsage); } @@ -984,6 +1009,10 @@ bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) const { return mempool.exists(txid) || base->HaveCoins(txid); } +bool CCoinsViewMemPool::IsWithdrawSpent(const std::pair &outpoint) const { + return mempool.mapWithdrawsSpentToTxid.count(outpoint) || base->IsWithdrawSpent(outpoint); +} + size_t CTxMemPool::DynamicMemoryUsage() const { LOCK(cs); // Estimate the overhead of mapTx to be 15 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented. diff --git a/src/txmempool.h b/src/txmempool.h index afb328b5af..560c7183e5 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -107,10 +107,12 @@ private: int64_t nSigOpCostWithAncestors; public: + std::set > setWithdrawsSpent; + CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, int64_t _nTime, double _entryPriority, unsigned int _entryHeight, bool poolHasNoInputsOf, CAmount _inChainInputValue, bool spendsCoinbase, - int64_t nSigOpsCost, LockPoints lp); + int64_t nSigOpsCost, LockPoints lp, std::set >& setWithdrawsSpent); CTxMemPoolEntry(const CTxMemPoolEntry& other); const CTransaction& GetTx() const { return *this->tx; } @@ -473,6 +475,8 @@ public: const setEntries & GetMemPoolParents(txiter entry) const; const setEntries & GetMemPoolChildren(txiter entry) const; + + std::map, uint256> mapWithdrawsSpentToTxid; private: typedef std::map cacheMap; @@ -693,6 +697,7 @@ public: CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn); bool GetCoins(const uint256 &txid, CCoins &coins) const; bool HaveCoins(const uint256 &txid) const; + bool IsWithdrawSpent(const std::pair &outpoint) const; }; // We want to sort transactions by coin age priority