Skip to content

Commit 27e8d22

Browse files
committed
Merge pull request #5945
ad9e86d Keep mempool consistent during block-reorgs (Gavin Andresen)
2 parents 2dc679d + ad9e86d commit 27e8d22

File tree

3 files changed

+117
-0
lines changed

3 files changed

+117
-0
lines changed

src/Makefile.test.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ BITCOIN_TESTS =\
5050
test/hash_tests.cpp \
5151
test/key_tests.cpp \
5252
test/main_tests.cpp \
53+
test/mempool_tests.cpp \
5354
test/miner_tests.cpp \
5455
test/mruset_tests.cpp \
5556
test/multisig_tests.cpp \

src/test/mempool_tests.cpp

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Copyright (c) 2011-2014 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include "main.h"
6+
#include "txmempool.h"
7+
#include "util.h"
8+
9+
#include "test/test_bitcoin.h"
10+
11+
#include <boost/test/unit_test.hpp>
12+
#include <list>
13+
14+
BOOST_FIXTURE_TEST_SUITE(mempool_tests, TestingSetup)
15+
16+
BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
17+
{
18+
// Test CTxMemPool::remove functionality
19+
20+
// Parent transaction with three children,
21+
// and three grand-children:
22+
CMutableTransaction txParent;
23+
txParent.vin.resize(1);
24+
txParent.vin[0].scriptSig = CScript() << OP_11;
25+
txParent.vout.resize(3);
26+
for (int i = 0; i < 3; i++)
27+
{
28+
txParent.vout[i].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
29+
txParent.vout[i].nValue = 33000LL;
30+
}
31+
CMutableTransaction txChild[3];
32+
for (int i = 0; i < 3; i++)
33+
{
34+
txChild[i].vin.resize(1);
35+
txChild[i].vin[0].scriptSig = CScript() << OP_11;
36+
txChild[i].vin[0].prevout.hash = txParent.GetHash();
37+
txChild[i].vin[0].prevout.n = i;
38+
txChild[i].vout.resize(1);
39+
txChild[i].vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
40+
txChild[i].vout[0].nValue = 11000LL;
41+
}
42+
CMutableTransaction txGrandChild[3];
43+
for (int i = 0; i < 3; i++)
44+
{
45+
txGrandChild[i].vin.resize(1);
46+
txGrandChild[i].vin[0].scriptSig = CScript() << OP_11;
47+
txGrandChild[i].vin[0].prevout.hash = txChild[i].GetHash();
48+
txGrandChild[i].vin[0].prevout.n = 0;
49+
txGrandChild[i].vout.resize(1);
50+
txGrandChild[i].vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
51+
txGrandChild[i].vout[0].nValue = 11000LL;
52+
}
53+
54+
55+
CTxMemPool testPool(CFeeRate(0));
56+
std::list<CTransaction> removed;
57+
58+
// Nothing in pool, remove should do nothing:
59+
testPool.remove(txParent, removed, true);
60+
BOOST_CHECK_EQUAL(removed.size(), 0);
61+
62+
// Just the parent:
63+
testPool.addUnchecked(txParent.GetHash(), CTxMemPoolEntry(txParent, 0, 0, 0.0, 1));
64+
testPool.remove(txParent, removed, true);
65+
BOOST_CHECK_EQUAL(removed.size(), 1);
66+
removed.clear();
67+
68+
// Parent, children, grandchildren:
69+
testPool.addUnchecked(txParent.GetHash(), CTxMemPoolEntry(txParent, 0, 0, 0.0, 1));
70+
for (int i = 0; i < 3; i++)
71+
{
72+
testPool.addUnchecked(txChild[i].GetHash(), CTxMemPoolEntry(txChild[i], 0, 0, 0.0, 1));
73+
testPool.addUnchecked(txGrandChild[i].GetHash(), CTxMemPoolEntry(txGrandChild[i], 0, 0, 0.0, 1));
74+
}
75+
// Remove Child[0], GrandChild[0] should be removed:
76+
testPool.remove(txChild[0], removed, true);
77+
BOOST_CHECK_EQUAL(removed.size(), 2);
78+
removed.clear();
79+
// ... make sure grandchild and child are gone:
80+
testPool.remove(txGrandChild[0], removed, true);
81+
BOOST_CHECK_EQUAL(removed.size(), 0);
82+
testPool.remove(txChild[0], removed, true);
83+
BOOST_CHECK_EQUAL(removed.size(), 0);
84+
// Remove parent, all children/grandchildren should go:
85+
testPool.remove(txParent, removed, true);
86+
BOOST_CHECK_EQUAL(removed.size(), 5);
87+
BOOST_CHECK_EQUAL(testPool.size(), 0);
88+
removed.clear();
89+
90+
// Add children and grandchildren, but NOT the parent (simulate the parent being in a block)
91+
for (int i = 0; i < 3; i++)
92+
{
93+
testPool.addUnchecked(txChild[i].GetHash(), CTxMemPoolEntry(txChild[i], 0, 0, 0.0, 1));
94+
testPool.addUnchecked(txGrandChild[i].GetHash(), CTxMemPoolEntry(txGrandChild[i], 0, 0, 0.0, 1));
95+
}
96+
// Now remove the parent, as might happen if a block-re-org occurs but the parent cannot be
97+
// put into the mempool (maybe because it is non-standard):
98+
testPool.remove(txParent, removed, true);
99+
BOOST_CHECK_EQUAL(removed.size(), 6);
100+
BOOST_CHECK_EQUAL(testPool.size(), 0);
101+
removed.clear();
102+
}
103+
104+
BOOST_AUTO_TEST_SUITE_END()

src/txmempool.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,18 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list<CTransaction>& rem
444444
LOCK(cs);
445445
std::deque<uint256> txToRemove;
446446
txToRemove.push_back(origTx.GetHash());
447+
if (fRecursive && !mapTx.count(origTx.GetHash())) {
448+
// If recursively removing but origTx isn't in the mempool
449+
// be sure to remove any children that are in the pool. This can
450+
// happen during chain re-orgs if origTx isn't re-accepted into
451+
// the mempool for any reason.
452+
for (unsigned int i = 0; i < origTx.vout.size(); i++) {
453+
std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(COutPoint(origTx.GetHash(), i));
454+
if (it == mapNextTx.end())
455+
continue;
456+
txToRemove.push_back(it->second.ptx->GetHash());
457+
}
458+
}
447459
while (!txToRemove.empty())
448460
{
449461
uint256 hash = txToRemove.front();

0 commit comments

Comments
 (0)