Track coinbase spends in CTxMemPoolEntry
This allows us to optimize CTxMemPool::removeForReorg.
This commit is contained in:
committed by
Jack Grigg
parent
fe5cef0555
commit
a4b2518068
@@ -83,7 +83,7 @@ TEST(Mempool, PriorityStatsDoNotCrash) {
|
|||||||
unsigned int nHeight = 92045;
|
unsigned int nHeight = 92045;
|
||||||
double dPriority = view.GetPriority(tx, nHeight);
|
double dPriority = view.GetPriority(tx, nHeight);
|
||||||
|
|
||||||
CTxMemPoolEntry entry(tx, nFees, nTime, dPriority, nHeight, true);
|
CTxMemPoolEntry entry(tx, nFees, nTime, dPriority, nHeight, true, false);
|
||||||
|
|
||||||
// Check it does not crash (ie. the death test fails)
|
// Check it does not crash (ie. the death test fails)
|
||||||
EXPECT_NONFATAL_FAILURE(EXPECT_DEATH(testPool.addUnchecked(tx.GetHash(), entry), ""), "");
|
EXPECT_NONFATAL_FAILURE(EXPECT_DEATH(testPool.addUnchecked(tx.GetHash(), entry), ""), "");
|
||||||
|
|||||||
13
src/main.cpp
13
src/main.cpp
@@ -1188,7 +1188,18 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||||||
CAmount nFees = nValueIn-nValueOut;
|
CAmount nFees = nValueIn-nValueOut;
|
||||||
double dPriority = view.GetPriority(tx, chainActive.Height());
|
double dPriority = view.GetPriority(tx, chainActive.Height());
|
||||||
|
|
||||||
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), mempool.HasNoInputsOf(tx));
|
// Keep track of transactions that spend a coinbase, which we re-scan
|
||||||
|
// during reorgs to ensure COINBASE_MATURITY is still met.
|
||||||
|
bool fSpendsCoinbase = false;
|
||||||
|
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
|
||||||
|
const CCoins *coins = view.AccessCoins(txin.prevout.hash);
|
||||||
|
if (coins->IsCoinBase()) {
|
||||||
|
fSpendsCoinbase = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), mempool.HasNoInputsOf(tx), fSpendsCoinbase);
|
||||||
unsigned int nSize = entry.GetTxSize();
|
unsigned int nSize = entry.GetTxSize();
|
||||||
|
|
||||||
// Accept a tx if it contains joinsplits and has at least the default fee specified by z_sendmany.
|
// Accept a tx if it contains joinsplits and has at least the default fee specified by z_sendmany.
|
||||||
|
|||||||
@@ -285,7 +285,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||||||
{
|
{
|
||||||
tx.vout[0].nValue -= 10;
|
tx.vout[0].nValue -= 10;
|
||||||
hash = tx.GetHash();
|
hash = tx.GetHash();
|
||||||
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
|
bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase
|
||||||
|
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
|
||||||
tx.vin[0].prevout.hash = hash;
|
tx.vin[0].prevout.hash = hash;
|
||||||
}
|
}
|
||||||
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
|
||||||
@@ -305,7 +306,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||||||
{
|
{
|
||||||
tx.vout[0].nValue -= 350;
|
tx.vout[0].nValue -= 350;
|
||||||
hash = tx.GetHash();
|
hash = tx.GetHash();
|
||||||
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
|
bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase
|
||||||
|
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
|
||||||
tx.vin[0].prevout.hash = hash;
|
tx.vin[0].prevout.hash = hash;
|
||||||
}
|
}
|
||||||
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
|
||||||
@@ -324,7 +326,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||||||
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
|
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
|
||||||
tx.vout[0].nValue = 39000LL;
|
tx.vout[0].nValue = 39000LL;
|
||||||
hash = tx.GetHash();
|
hash = tx.GetHash();
|
||||||
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
|
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||||
tx.vin[0].prevout.hash = hash;
|
tx.vin[0].prevout.hash = hash;
|
||||||
tx.vin.resize(2);
|
tx.vin.resize(2);
|
||||||
tx.vin[1].scriptSig = CScript() << OP_1;
|
tx.vin[1].scriptSig = CScript() << OP_1;
|
||||||
@@ -332,7 +334,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||||||
tx.vin[1].prevout.n = 0;
|
tx.vin[1].prevout.n = 0;
|
||||||
tx.vout[0].nValue = 49000LL;
|
tx.vout[0].nValue = 49000LL;
|
||||||
hash = tx.GetHash();
|
hash = tx.GetHash();
|
||||||
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
|
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||||
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
|
||||||
delete pblocktemplate;
|
delete pblocktemplate;
|
||||||
mempool.clear();
|
mempool.clear();
|
||||||
@@ -343,7 +345,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||||||
tx.vin[0].scriptSig = CScript() << OP_0 << OP_1;
|
tx.vin[0].scriptSig = CScript() << OP_0 << OP_1;
|
||||||
tx.vout[0].nValue = 0;
|
tx.vout[0].nValue = 0;
|
||||||
hash = tx.GetHash();
|
hash = tx.GetHash();
|
||||||
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
|
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
|
||||||
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
|
||||||
delete pblocktemplate;
|
delete pblocktemplate;
|
||||||
mempool.clear();
|
mempool.clear();
|
||||||
@@ -356,12 +358,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||||||
script = CScript() << OP_0;
|
script = CScript() << OP_0;
|
||||||
tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script));
|
tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script));
|
||||||
hash = tx.GetHash();
|
hash = tx.GetHash();
|
||||||
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
|
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||||
tx.vin[0].prevout.hash = hash;
|
tx.vin[0].prevout.hash = hash;
|
||||||
tx.vin[0].scriptSig = CScript() << (std::vector<unsigned char>)script;
|
tx.vin[0].scriptSig = CScript() << (std::vector<unsigned char>)script;
|
||||||
tx.vout[0].nValue -= 10000;
|
tx.vout[0].nValue -= 10000;
|
||||||
hash = tx.GetHash();
|
hash = tx.GetHash();
|
||||||
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
|
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
|
||||||
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
|
||||||
delete pblocktemplate;
|
delete pblocktemplate;
|
||||||
mempool.clear();
|
mempool.clear();
|
||||||
@@ -372,10 +374,10 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||||||
tx.vout[0].nValue = 49000LL;
|
tx.vout[0].nValue = 49000LL;
|
||||||
tx.vout[0].scriptPubKey = CScript() << OP_1;
|
tx.vout[0].scriptPubKey = CScript() << OP_1;
|
||||||
hash = tx.GetHash();
|
hash = tx.GetHash();
|
||||||
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
|
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||||
tx.vout[0].scriptPubKey = CScript() << OP_2;
|
tx.vout[0].scriptPubKey = CScript() << OP_2;
|
||||||
hash = tx.GetHash();
|
hash = tx.GetHash();
|
||||||
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
|
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||||
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
|
||||||
delete pblocktemplate;
|
delete pblocktemplate;
|
||||||
mempool.clear();
|
mempool.clear();
|
||||||
@@ -401,7 +403,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||||||
tx.vout[0].scriptPubKey = CScript() << OP_1;
|
tx.vout[0].scriptPubKey = CScript() << OP_1;
|
||||||
tx.nLockTime = chainActive.Tip()->nHeight+1;
|
tx.nLockTime = chainActive.Tip()->nHeight+1;
|
||||||
hash = tx.GetHash();
|
hash = tx.GetHash();
|
||||||
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
|
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||||
BOOST_CHECK(!CheckFinalTx(tx, LOCKTIME_MEDIAN_TIME_PAST));
|
BOOST_CHECK(!CheckFinalTx(tx, LOCKTIME_MEDIAN_TIME_PAST));
|
||||||
|
|
||||||
// time locked
|
// time locked
|
||||||
@@ -415,7 +417,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||||||
tx2.vout[0].scriptPubKey = CScript() << OP_1;
|
tx2.vout[0].scriptPubKey = CScript() << OP_1;
|
||||||
tx2.nLockTime = chainActive.Tip()->GetMedianTimePast()+1;
|
tx2.nLockTime = chainActive.Tip()->GetMedianTimePast()+1;
|
||||||
hash = tx2.GetHash();
|
hash = tx2.GetHash();
|
||||||
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx2));
|
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx2));
|
||||||
BOOST_CHECK(!CheckFinalTx(tx2, LOCKTIME_MEDIAN_TIME_PAST));
|
BOOST_CHECK(!CheckFinalTx(tx2, LOCKTIME_MEDIAN_TIME_PAST));
|
||||||
|
|
||||||
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
|
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ TestingSetup::~TestingSetup()
|
|||||||
|
|
||||||
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CMutableTransaction &tx, CTxMemPool *pool) {
|
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CMutableTransaction &tx, CTxMemPool *pool) {
|
||||||
return CTxMemPoolEntry(tx, nFee, nTime, dPriority, nHeight,
|
return CTxMemPoolEntry(tx, nFee, nTime, dPriority, nHeight,
|
||||||
pool ? pool->HasNoInputsOf(tx) : hadNoDependencies);
|
pool ? pool->HasNoInputsOf(tx) : hadNoDependencies, spendsCoinbase);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown(void* parg)
|
void Shutdown(void* parg)
|
||||||
|
|||||||
@@ -47,10 +47,11 @@ struct TestMemPoolEntryHelper
|
|||||||
double dPriority;
|
double dPriority;
|
||||||
unsigned int nHeight;
|
unsigned int nHeight;
|
||||||
bool hadNoDependencies;
|
bool hadNoDependencies;
|
||||||
|
bool spendsCoinbase;
|
||||||
|
|
||||||
TestMemPoolEntryHelper() :
|
TestMemPoolEntryHelper() :
|
||||||
nFee(0), nTime(0), dPriority(0.0), nHeight(1),
|
nFee(0), nTime(0), dPriority(0.0), nHeight(1),
|
||||||
hadNoDependencies(false) { }
|
hadNoDependencies(false), spendsCoinbase(false) { }
|
||||||
|
|
||||||
CTxMemPoolEntry FromTx(CMutableTransaction &tx, CTxMemPool *pool = NULL);
|
CTxMemPoolEntry FromTx(CMutableTransaction &tx, CTxMemPool *pool = NULL);
|
||||||
|
|
||||||
@@ -60,5 +61,6 @@ struct TestMemPoolEntryHelper
|
|||||||
TestMemPoolEntryHelper &Priority(double _priority) { dPriority = _priority; return *this; }
|
TestMemPoolEntryHelper &Priority(double _priority) { dPriority = _priority; return *this; }
|
||||||
TestMemPoolEntryHelper &Height(unsigned int _height) { nHeight = _height; return *this; }
|
TestMemPoolEntryHelper &Height(unsigned int _height) { nHeight = _height; return *this; }
|
||||||
TestMemPoolEntryHelper &HadNoDependencies(bool _hnd) { hadNoDependencies = _hnd; return *this; }
|
TestMemPoolEntryHelper &HadNoDependencies(bool _hnd) { hadNoDependencies = _hnd; return *this; }
|
||||||
|
TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; }
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -19,16 +19,19 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
CTxMemPoolEntry::CTxMemPoolEntry():
|
CTxMemPoolEntry::CTxMemPoolEntry():
|
||||||
nFee(0), nTxSize(0), nModSize(0), nUsageSize(0), nTime(0), dPriority(0.0), hadNoDependencies(false)
|
nFee(0), nTxSize(0), nModSize(0), nUsageSize(0), nTime(0), dPriority(0.0),
|
||||||
|
hadNoDependencies(false), spendsCoinbase(false)
|
||||||
{
|
{
|
||||||
nHeight = MEMPOOL_HEIGHT;
|
nHeight = MEMPOOL_HEIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
|
CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
|
||||||
int64_t _nTime, double _dPriority,
|
int64_t _nTime, double _dPriority,
|
||||||
unsigned int _nHeight, bool poolHasNoInputsOf):
|
unsigned int _nHeight, bool poolHasNoInputsOf,
|
||||||
|
bool _spendsCoinbase):
|
||||||
tx(_tx), nFee(_nFee), nTime(_nTime), dPriority(_dPriority), nHeight(_nHeight),
|
tx(_tx), nFee(_nFee), nTime(_nTime), dPriority(_dPriority), nHeight(_nHeight),
|
||||||
hadNoDependencies(poolHasNoInputsOf)
|
hadNoDependencies(poolHasNoInputsOf),
|
||||||
|
spendsCoinbase(_spendsCoinbase)
|
||||||
{
|
{
|
||||||
nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
||||||
nModSize = tx.CalculateModifiedSize(nTxSize);
|
nModSize = tx.CalculateModifiedSize(nTxSize);
|
||||||
@@ -177,7 +180,7 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem
|
|||||||
const CTransaction& tx = it->GetTx();
|
const CTransaction& tx = it->GetTx();
|
||||||
if (!IsFinalTx(tx, nMemPoolHeight, GetAdjustedTime())) {
|
if (!IsFinalTx(tx, nMemPoolHeight, GetAdjustedTime())) {
|
||||||
transactionsToRemove.push_back(tx);
|
transactionsToRemove.push_back(tx);
|
||||||
} else {
|
} else if (it->GetSpendsCoinbase()) {
|
||||||
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
|
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
|
||||||
indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash);
|
indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash);
|
||||||
if (it2 != mapTx.end())
|
if (it2 != mapTx.end())
|
||||||
|
|||||||
@@ -50,10 +50,12 @@ private:
|
|||||||
double dPriority; //! Priority when entering the mempool
|
double dPriority; //! Priority when entering the mempool
|
||||||
unsigned int nHeight; //! Chain height when entering the mempool
|
unsigned int nHeight; //! Chain height when entering the mempool
|
||||||
bool hadNoDependencies; //! Not dependent on any other txs when it entered the mempool
|
bool hadNoDependencies; //! Not dependent on any other txs when it entered the mempool
|
||||||
|
bool spendsCoinbase; //! keep track of transactions that spend a coinbase
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
|
CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
|
||||||
int64_t _nTime, double _dPriority, unsigned int _nHeight, bool poolHasNoInputsOf = false);
|
int64_t _nTime, double _dPriority, unsigned int _nHeight,
|
||||||
|
bool poolHasNoInputsOf, bool spendsCoinbase);
|
||||||
CTxMemPoolEntry();
|
CTxMemPoolEntry();
|
||||||
CTxMemPoolEntry(const CTxMemPoolEntry& other);
|
CTxMemPoolEntry(const CTxMemPoolEntry& other);
|
||||||
|
|
||||||
@@ -66,6 +68,8 @@ public:
|
|||||||
unsigned int GetHeight() const { return nHeight; }
|
unsigned int GetHeight() const { return nHeight; }
|
||||||
bool WasClearAtEntry() const { return hadNoDependencies; }
|
bool WasClearAtEntry() const { return hadNoDependencies; }
|
||||||
size_t DynamicMemoryUsage() const { return nUsageSize; }
|
size_t DynamicMemoryUsage() const { return nUsageSize; }
|
||||||
|
|
||||||
|
bool GetSpendsCoinbase() const { return spendsCoinbase; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// extracts a TxMemPoolEntry's transaction hash
|
// extracts a TxMemPoolEntry's transaction hash
|
||||||
|
|||||||
Reference in New Issue
Block a user