Skip to content

Commit

Permalink
Track coinbase spends in CTxMemPoolEntry
Browse files Browse the repository at this point in the history
Coming from btc@7e49f5f8b4e237d7212d027a7bea4bbd52c9e7b6
  • Loading branch information
furszy committed Jul 3, 2020
1 parent f607f18 commit bb77808
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 19 deletions.
25 changes: 23 additions & 2 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1001,7 +1001,18 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState& state, const CTransa
if (!hasZcSpendInputs)
dPriority = view.GetPriority(tx, chainHeight, inChainInputValue);

CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainHeight, pool.HasNoInputsOf(tx), inChainInputValue);
// Keep track of transactions that spend a coinbase, which we re-scan
// during reorgs to ensure COINBASE_MATURITY is still met.
bool fSpendsCoinbaseOrCoinstake = false;
for (const CTxIn &txin : tx.vin) {
const CCoins *coins = view.AccessCoins(txin.prevout.hash);
if (coins->IsCoinBase() || coins->IsCoinStake()) {
fSpendsCoinbaseOrCoinstake = true;
break;
}
}

CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainHeight, pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbaseOrCoinstake);
unsigned int nSize = entry.GetTxSize();

// Don't accept it if it can't get into a block
Expand Down Expand Up @@ -1226,7 +1237,17 @@ bool AcceptableInputs(CTxMemPool& pool, CValidationState& state, const CTransact
CAmount inChainInputValue;
double dPriority = view.GetPriority(tx, chainHeight, inChainInputValue);

CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainHeight, mempool.HasNoInputsOf(tx), inChainInputValue);
// Keep track of transactions that spend a coinbase, which we re-scan
// during reorgs to ensure COINBASE_MATURITY is still met.
bool fSpendsCoinbaseOrCoinstake = false;
for (const CTxIn &txin : tx.vin) {
const CCoins *coins = view.AccessCoins(txin.prevout.hash);
if (coins->IsCoinBase() || coins->IsCoinStake()) {
fSpendsCoinbaseOrCoinstake = true;
break;
}
}
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainHeight, mempool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbaseOrCoinstake);
unsigned int nSize = entry.GetTxSize();

// Don't accept it if it can't get into a block
Expand Down
24 changes: 13 additions & 11 deletions src/test/miner_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
{
tx.vout[0].nValue -= 1000000;
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()).SpendsCoinbaseOrCoinstake(spendsCoinbase).FromTx(tx));
tx.vin[0].prevout.hash = hash;
}
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey, pwalletMain, false));
Expand All @@ -133,7 +134,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
{
tx.vout[0].nValue -= 10000000;
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()).SpendsCoinbaseOrCoinstake(spendsCoinbase).FromTx(tx));
tx.vin[0].prevout.hash = hash;
}
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey, pwalletMain, false));
Expand All @@ -152,15 +154,15 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
tx.vout[0].nValue = 4900000000LL;
hash = tx.GetHash();
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbaseOrCoinstake(true).FromTx(tx));
tx.vin[0].prevout.hash = hash;
tx.vin.resize(2);
tx.vin[1].scriptSig = CScript() << OP_1;
tx.vin[1].prevout.hash = txFirst[0]->GetHash();
tx.vin[1].prevout.n = 0;
tx.vout[0].nValue = 5900000000LL;
hash = tx.GetHash();
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbaseOrCoinstake(true).FromTx(tx));
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey, pwalletMain, false));
delete pblocktemplate;
mempool.clear();
Expand All @@ -171,7 +173,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vin[0].scriptSig = CScript() << OP_0 << OP_1;
tx.vout[0].nValue = 0;
hash = tx.GetHash();
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbaseOrCoinstake(false).FromTx(tx));
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey, pwalletMain, false));
delete pblocktemplate;
mempool.clear();
Expand All @@ -184,12 +186,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
script = CScript() << OP_0;
tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script));
hash = tx.GetHash();
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbaseOrCoinstake(true).FromTx(tx));
tx.vin[0].prevout.hash = hash;
tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(script.begin(), script.end());
tx.vout[0].nValue -= 1000000;
hash = tx.GetHash();
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbaseOrCoinstake(false).FromTx(tx));
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey, pwalletMain, false));
delete pblocktemplate;
mempool.clear();
Expand All @@ -200,10 +202,10 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vout[0].nValue = 4900000000LL;
tx.vout[0].scriptPubKey = CScript() << OP_1;
hash = tx.GetHash();
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbaseOrCoinstake(true).FromTx(tx));
tx.vout[0].scriptPubKey = CScript() << OP_2;
hash = tx.GetHash();
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbaseOrCoinstake(true).FromTx(tx));
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey, pwalletMain, false));
delete pblocktemplate;
mempool.clear();
Expand All @@ -229,7 +231,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vout[0].scriptPubKey = CScript() << OP_1;
tx.nLockTime = chainActive.Tip()->nHeight+1;
hash = tx.GetHash();
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbaseOrCoinstake(true).FromTx(tx));
BOOST_CHECK(!CheckFinalTx(tx, LOCKTIME_MEDIAN_TIME_PAST));

// time locked
Expand All @@ -243,7 +245,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx2.vout[0].scriptPubKey = CScript() << OP_1;
tx2.nLockTime = chainActive.Tip()->GetMedianTimePast()+1;
hash = tx2.GetHash();
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx2));
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbaseOrCoinstake(true).FromTx(tx2));
BOOST_CHECK(!CheckFinalTx(tx2, LOCKTIME_MEDIAN_TIME_PAST));

BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey, pwalletMain, false));
Expand Down
2 changes: 1 addition & 1 deletion src/test/test_pivx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CMutableTransaction &tx, CTxMemPo
CAmount inChainValue = hasNoDependencies ? txn.GetValueOut() : 0;

return CTxMemPoolEntry(txn, nFee, nTime, dPriority, nHeight,
hasNoDependencies, inChainValue);
hasNoDependencies, inChainValue, spendsCoinbaseOrCoinstake);
}

[[noreturn]] void Shutdown(void* parg)
Expand Down
4 changes: 3 additions & 1 deletion src/test/test_pivx.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,11 @@ struct TestMemPoolEntryHelper
double dPriority;
unsigned int nHeight;
bool hadNoDependencies;
bool spendsCoinbaseOrCoinstake;

TestMemPoolEntryHelper() :
nFee(0), nTime(0), dPriority(0.0), nHeight(1),
hadNoDependencies(false) { }
hadNoDependencies(false), spendsCoinbaseOrCoinstake(false) { }

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

Expand All @@ -76,6 +77,7 @@ struct TestMemPoolEntryHelper
TestMemPoolEntryHelper &Priority(double _priority) { dPriority = _priority; return *this; }
TestMemPoolEntryHelper &Height(unsigned int _height) { nHeight = _height; return *this; }
TestMemPoolEntryHelper &HadNoDependencies(bool _hnd) { hadNoDependencies = _hnd; return *this; }
TestMemPoolEntryHelper &SpendsCoinbaseOrCoinstake(bool _flag) { spendsCoinbaseOrCoinstake = _flag; return *this; }
};

#endif
7 changes: 4 additions & 3 deletions src/txmempool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@

CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
int64_t _nTime, double _entryPriority,
unsigned int _entryHeight, bool poolHasNoInputsOf, CAmount _inChainInputValue) :
tx(_tx), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight), hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue)
unsigned int _entryHeight, bool poolHasNoInputsOf, CAmount _inChainInputValue,
bool _spendsCoinbaseOrCoinstake) :
tx(_tx), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight), hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue), spendsCoinbaseOrCoinstake(_spendsCoinbaseOrCoinstake)
{
nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
nModSize = tx.CalculateModifiedSize(nTxSize);
Expand Down Expand Up @@ -472,7 +473,7 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache* pcoins, unsigned int nMem
const CTransaction& tx = it->GetTx();
if (!IsFinalTx(tx, nMemPoolHeight, GetAdjustedTime())) {
transactionsToRemove.push_back(tx);
} else {
} else if (it->GetSpendsCoinbaseOrCoinstake()) {
for (const CTxIn& txin : tx.vin) {
indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash);
if (it2 != mapTx.end())
Expand Down
5 changes: 4 additions & 1 deletion src/txmempool.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class CTxMemPoolEntry
unsigned int entryHeight; //! Chain height when entering the mempool
bool hadNoDependencies; //! Not dependent on any other txs when it entered the mempool
CAmount inChainInputValue; //! Sum of all txin values that are already in blockchain
bool spendsCoinbaseOrCoinstake; //! keep track of transactions that spend a coinbase or a coinstake

// Information about descendants of this transaction that are in the
// mempool; if we remove this transaction we must remove all of these
Expand All @@ -83,7 +84,7 @@ class CTxMemPoolEntry
public:
CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
int64_t _nTime, double _entryPriority, unsigned int _entryHeight,
bool poolHasNoInputsOf, CAmount _inChainInputValue);
bool poolHasNoInputsOf, CAmount _inChainInputValue, bool _spendsCoinbaseOrCoinstake);
CTxMemPoolEntry(const CTxMemPoolEntry& other);

const CTransaction& GetTx() const { return this->tx; }
Expand Down Expand Up @@ -113,6 +114,8 @@ class CTxMemPoolEntry
uint64_t GetCountWithDescendants() const { return nCountWithDescendants; }
uint64_t GetSizeWithDescendants() const { return nSizeWithDescendants; }
CAmount GetFeesWithDescendants() const { return nFeesWithDescendants; }

bool GetSpendsCoinbaseOrCoinstake() const { return spendsCoinbaseOrCoinstake; }
};

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

0 comments on commit bb77808

Please sign in to comment.