Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Bugfix: remove conflicting transactions from memory pool #2033

Merged
merged 1 commit into from

5 participants

Pieter Wuille BitcoinPullTester Gregory Maxwell Gavin Andresen R E Broadley
Pieter Wuille
Owner

When a transaction A is in the memory pool, while a transaction B (which shares an input with A) gets accepted into a block, A was kept forever in the memory pool.

This problem exists in probably all versions of Bitcoin ever. On v0.7.1, it can be demonstrating by mining with 7e15b68 applied. Every few hours, it seems such a transactions that conflicts with the memory pool gets mined (successful double spends?).

Fixing this results in less transactions in the memory pool, and faster construction of new blocks.

This should apply cleanly on v0.7.0, v0.7.1 and HEAD.

BitcoinPullTester

Automatic sanity-testing: PASSED, see http://jenkins.bluematt.me/pull-tester/c52f3b86ceb4b2d933f919e06275c670f31dbdb6 for binaries and test log.

Pieter Wuille
Owner

I just observed mempool transactions depending on spent inputs on v0.7.1 with this patch - maybe it doesn't work.

Pieter Wuille sipa Bugfix: remove conflicting transactions from memory pool
When a transaction A is in the memory pool, while a transaction B
(which shares an input with A) gets accepted into a block, A was
kept forever in the memory pool.

This commit adds a CTxMemPool::removeConflicts method, which
removes transactions that conflict with a given transaction, and
all their children.

This results in less transactions in the memory pool, and faster
construction of new blocks.
231b399
Pieter Wuille
Owner

Ok, seems the implementation didn't actually remove recursively. Should be fixed now.

BitcoinPullTester

Automatic sanity-testing: PASSED, see http://jenkins.bluematt.me/pull-tester/231b399952fd620ee0f72b1947024dba9651630d for binaries and test log.

Gregory Maxwell
Owner

ACK. I note we do not yet have any mempool unit tests. But I did test it with a bunch of real mainnet reorgs.

Gavin Andresen

ACK

Pieter Wuille sipa merged commit cd7fb7d into from
R E Broadley

did this fix a bug that had been present since version 1?

Pieter Wuille
Owner

@rebroad Yes, indeed. Not a bad one, though.

R E Broadley

@sipa it looks like it was a bug that could have successfully created a DOS attack that may even have broken the network - i.e. create loads of conflicting transactions so that the mempool fills up to its limit and then the nodes would have stopped passing on new transactions... yes/no?

Gregory Maxwell
Owner

No, not really— because it requires mining blocks that have later spends than everyone else had accepted. So it's naturally limited by the attacker's ability to mine blocks.

Pieter Wuille sipa deleted the branch
R E Broadley
Gregory Maxwell
Owner

Anyone who controls a large amount of hash power has much more potent attacks available to them than this. (E.g. orphan flooding)

R E Broadley

Why would orphan flooding be a more potent attack than filling the mempool? I'd have thought the orphan pool filling up would be far less of a problem than the mempool filling up. Sorry, probably a discussion for #bitcoin...

Gregory Maxwell
Owner

Not orphan transactions. Orphan blocks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 25, 2012
  1. Pieter Wuille

    Bugfix: remove conflicting transactions from memory pool

    sipa authored
    When a transaction A is in the memory pool, while a transaction B
    (which shares an input with A) gets accepted into a block, A was
    kept forever in the memory pool.
    
    This commit adds a CTxMemPool::removeConflicts method, which
    removes transactions that conflict with a given transaction, and
    all their children.
    
    This results in less transactions in the memory pool, and faster
    construction of new blocks.
This page is out of date. Refresh to see the latest.
Showing with 28 additions and 3 deletions.
  1. +26 −2 src/main.cpp
  2. +2 −1  src/main.h
28 src/main.cpp
View
@@ -818,7 +818,7 @@ bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx)
}
-bool CTxMemPool::remove(CTransaction &tx)
+bool CTxMemPool::remove(const CTransaction &tx, bool fRecursive)
{
// Remove transaction from memory pool
{
@@ -826,6 +826,13 @@ bool CTxMemPool::remove(CTransaction &tx)
uint256 hash = tx.GetHash();
if (mapTx.count(hash))
{
+ if (fRecursive) {
+ for (unsigned int i = 0; i < tx.vout.size(); i++) {
+ std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(COutPoint(hash, i));
+ if (it != mapNextTx.end())
+ remove(*it->second.ptx, true);
+ }
+ }
BOOST_FOREACH(const CTxIn& txin, tx.vin)
mapNextTx.erase(txin.prevout);
mapTx.erase(hash);
@@ -835,6 +842,21 @@ bool CTxMemPool::remove(CTransaction &tx)
return true;
}
+bool CTxMemPool::removeConflicts(const CTransaction &tx)
+{
+ // Remove transactions which depend on inputs of tx, recursively
+ LOCK(cs);
+ BOOST_FOREACH(const CTxIn &txin, tx.vin) {
+ std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(txin.prevout);
+ if (it != mapNextTx.end()) {
+ const CTransaction &txConflict = *it->second.ptx;
+ if (txConflict != tx)
+ remove(txConflict, true);
+ }
+ }
+ return true;
+}
+
void CTxMemPool::clear()
{
LOCK(cs);
@@ -1762,8 +1784,10 @@ bool SetBestChain(CBlockIndex* pindexNew)
tx.AcceptToMemoryPool(false);
// Delete redundant memory transactions that are in the connected branch
- BOOST_FOREACH(CTransaction& tx, vDelete)
+ BOOST_FOREACH(CTransaction& tx, vDelete) {
mempool.remove(tx);
+ mempool.removeConflicts(tx);
+ }
// Update best block in wallet (so we can detect restored wallets)
if (!fIsInitialDownload)
3  src/main.h
View
@@ -1814,7 +1814,8 @@ class CTxMemPool
bool accept(CTransaction &tx, bool fCheckInputs, bool* pfMissingInputs);
bool addUnchecked(const uint256& hash, CTransaction &tx);
- bool remove(CTransaction &tx);
+ bool remove(const CTransaction &tx, bool fRecursive = false);
+ bool removeConflicts(const CTransaction &tx);
void clear();
void queryHashes(std::vector<uint256>& vtxid);
void pruneSpent(const uint256& hash, CCoins &coins);
Something went wrong with that request. Please try again.