Skip to content

Commit 7e49f5f

Browse files
committed
Track coinbase spends in CTxMemPoolEntry
This allows us to optimize CTxMemPool::removeForReorg.
1 parent bb8ea1f commit 7e49f5f

File tree

6 files changed

+38
-18
lines changed

6 files changed

+38
-18
lines changed

src/main.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -953,7 +953,18 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
953953
CAmount inChainInputValue;
954954
double dPriority = view.GetPriority(tx, chainActive.Height(), inChainInputValue);
955955

956-
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue);
956+
// Keep track of transactions that spend a coinbase, which we re-scan
957+
// during reorgs to ensure COINBASE_MATURITY is still met.
958+
bool fSpendsCoinbase = false;
959+
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
960+
const CCoins *coins = view.AccessCoins(txin.prevout.hash);
961+
if (coins->IsCoinBase()) {
962+
fSpendsCoinbase = true;
963+
break;
964+
}
965+
}
966+
967+
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase);
957968
unsigned int nSize = entry.GetTxSize();
958969

959970
// Don't accept it if it can't get into a block

src/test/miner_tests.cpp

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
119119
{
120120
tx.vout[0].nValue -= 1000000;
121121
hash = tx.GetHash();
122-
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
122+
bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase
123+
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
123124
tx.vin[0].prevout.hash = hash;
124125
}
125126
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
@@ -139,7 +140,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
139140
{
140141
tx.vout[0].nValue -= 10000000;
141142
hash = tx.GetHash();
142-
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
143+
bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase
144+
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
143145
tx.vin[0].prevout.hash = hash;
144146
}
145147
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
@@ -158,15 +160,15 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
158160
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
159161
tx.vout[0].nValue = 4900000000LL;
160162
hash = tx.GetHash();
161-
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
163+
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
162164
tx.vin[0].prevout.hash = hash;
163165
tx.vin.resize(2);
164166
tx.vin[1].scriptSig = CScript() << OP_1;
165167
tx.vin[1].prevout.hash = txFirst[0]->GetHash();
166168
tx.vin[1].prevout.n = 0;
167169
tx.vout[0].nValue = 5900000000LL;
168170
hash = tx.GetHash();
169-
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
171+
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
170172
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
171173
delete pblocktemplate;
172174
mempool.clear();
@@ -177,7 +179,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
177179
tx.vin[0].scriptSig = CScript() << OP_0 << OP_1;
178180
tx.vout[0].nValue = 0;
179181
hash = tx.GetHash();
180-
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
182+
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
181183
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
182184
delete pblocktemplate;
183185
mempool.clear();
@@ -190,12 +192,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
190192
script = CScript() << OP_0;
191193
tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script));
192194
hash = tx.GetHash();
193-
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
195+
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
194196
tx.vin[0].prevout.hash = hash;
195197
tx.vin[0].scriptSig = CScript() << (std::vector<unsigned char>)script;
196198
tx.vout[0].nValue -= 1000000;
197199
hash = tx.GetHash();
198-
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
200+
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
199201
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
200202
delete pblocktemplate;
201203
mempool.clear();
@@ -206,10 +208,10 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
206208
tx.vout[0].nValue = 4900000000LL;
207209
tx.vout[0].scriptPubKey = CScript() << OP_1;
208210
hash = tx.GetHash();
209-
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
211+
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
210212
tx.vout[0].scriptPubKey = CScript() << OP_2;
211213
hash = tx.GetHash();
212-
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
214+
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
213215
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
214216
delete pblocktemplate;
215217
mempool.clear();
@@ -235,7 +237,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
235237
tx.vout[0].scriptPubKey = CScript() << OP_1;
236238
tx.nLockTime = chainActive.Tip()->nHeight+1;
237239
hash = tx.GetHash();
238-
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
240+
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
239241
BOOST_CHECK(!CheckFinalTx(tx, LOCKTIME_MEDIAN_TIME_PAST));
240242

241243
// time locked
@@ -249,7 +251,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
249251
tx2.vout[0].scriptPubKey = CScript() << OP_1;
250252
tx2.nLockTime = chainActive.Tip()->GetMedianTimePast()+1;
251253
hash = tx2.GetHash();
252-
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx2));
254+
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx2));
253255
BOOST_CHECK(!CheckFinalTx(tx2, LOCKTIME_MEDIAN_TIME_PAST));
254256

255257
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));

src/test/test_bitcoin.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CMutableTransaction &tx, CTxMemPo
150150
CAmount inChainValue = hasNoDependencies ? txn.GetValueOut() : 0;
151151

152152
return CTxMemPoolEntry(txn, nFee, nTime, dPriority, nHeight,
153-
hasNoDependencies, inChainValue);
153+
hasNoDependencies, inChainValue, spendsCoinbase);
154154
}
155155

156156
void Shutdown(void* parg)

src/test/test_bitcoin.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,11 @@ struct TestMemPoolEntryHelper
6565
double dPriority;
6666
unsigned int nHeight;
6767
bool hadNoDependencies;
68+
bool spendsCoinbase;
6869

6970
TestMemPoolEntryHelper() :
7071
nFee(0), nTime(0), dPriority(0.0), nHeight(1),
71-
hadNoDependencies(false) { }
72+
hadNoDependencies(false), spendsCoinbase(false) { }
7273

7374
CTxMemPoolEntry FromTx(CMutableTransaction &tx, CTxMemPool *pool = NULL);
7475

@@ -78,5 +79,6 @@ struct TestMemPoolEntryHelper
7879
TestMemPoolEntryHelper &Priority(double _priority) { dPriority = _priority; return *this; }
7980
TestMemPoolEntryHelper &Height(unsigned int _height) { nHeight = _height; return *this; }
8081
TestMemPoolEntryHelper &HadNoDependencies(bool _hnd) { hadNoDependencies = _hnd; return *this; }
82+
TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; }
8183
};
8284
#endif

src/txmempool.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ using namespace std;
2121

2222
CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
2323
int64_t _nTime, double _entryPriority, unsigned int _entryHeight,
24-
bool poolHasNoInputsOf, CAmount _inChainInputValue):
24+
bool poolHasNoInputsOf, CAmount _inChainInputValue,
25+
bool _spendsCoinbase):
2526
tx(_tx), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight),
26-
hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue)
27+
hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue),
28+
spendsCoinbase(_spendsCoinbase)
2729
{
2830
nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
2931
nModSize = tx.CalculateModifiedSize(nTxSize);
@@ -488,7 +490,7 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem
488490
const CTransaction& tx = it->GetTx();
489491
if (!IsFinalTx(tx, nMemPoolHeight, GetAdjustedTime())) {
490492
transactionsToRemove.push_back(tx);
491-
} else {
493+
} else if (it->GetSpendsCoinbase()) {
492494
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
493495
indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash);
494496
if (it2 != mapTx.end())

src/txmempool.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class CTxMemPoolEntry
6767
unsigned int entryHeight; //! Chain height when entering the mempool
6868
bool hadNoDependencies; //! Not dependent on any other txs when it entered the mempool
6969
CAmount inChainInputValue; //! Sum of all txin values that are already in blockchain
70+
bool spendsCoinbase; //! keep track of transactions that spend a coinbase
7071

7172
// Information about descendants of this transaction that are in the
7273
// mempool; if we remove this transaction we must remove all of these
@@ -80,7 +81,7 @@ class CTxMemPoolEntry
8081
public:
8182
CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
8283
int64_t _nTime, double _entryPriority, unsigned int _entryHeight,
83-
bool poolHasNoInputsOf, CAmount _inChainInputValue);
84+
bool poolHasNoInputsOf, CAmount _inChainInputValue, bool spendsCoinbase);
8485
CTxMemPoolEntry(const CTxMemPoolEntry& other);
8586

8687
const CTransaction& GetTx() const { return this->tx; }
@@ -109,6 +110,8 @@ class CTxMemPoolEntry
109110
uint64_t GetCountWithDescendants() const { return nCountWithDescendants; }
110111
uint64_t GetSizeWithDescendants() const { return nSizeWithDescendants; }
111112
CAmount GetFeesWithDescendants() const { return nFeesWithDescendants; }
113+
114+
bool GetSpendsCoinbase() const { return spendsCoinbase; }
112115
};
113116

114117
// Helpers for modifying CTxMemPool::mapTx, which is a boost multi_index.

0 commit comments

Comments
 (0)