diff --git a/src/txmempool.h b/src/txmempool.h --- a/src/txmempool.h +++ b/src/txmempool.h @@ -919,7 +919,8 @@ * CCoinsView that brings transactions from a mempool into view. * It does not check for spendings by memory pool transactions. * Instead, it provides access to all Coins which are either unspent in the - * base CCoinsView, or are outputs from any mempool transaction! + * base CCoinsView, are outputs from any mempool transaction, or are + * tracked temporarily to allow transaction dependencies in package validation. * This allows transaction replacement to work as expected, as you want to * have all inputs "available" to check signatures, and any cycles in the * dependency graph are checked directly in AcceptToMemoryPool. @@ -928,12 +929,21 @@ * as long as the conflicting transaction is not yet confirmed. */ class CCoinsViewMemPool : public CCoinsViewBacked { + /** + * Coins made available by transactions being validated. Tracking these + * allows for package validation, since we can access transaction outputs + * without submitting them to mempool. + */ + std::unordered_map m_temp_added; + protected: const CTxMemPool &mempool; public: CCoinsViewMemPool(CCoinsView *baseIn, const CTxMemPool &mempoolIn); bool GetCoin(const COutPoint &outpoint, Coin &coin) const override; + /** Add the coins created by this transaction. */ + void PackageAddTransaction(const CTransactionRef &tx); }; /** diff --git a/src/txmempool.cpp b/src/txmempool.cpp --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -1067,6 +1067,13 @@ : CCoinsViewBacked(baseIn), mempool(mempoolIn) {} bool CCoinsViewMemPool::GetCoin(const COutPoint &outpoint, Coin &coin) const { + // Check to see if the inputs are made available by another tx in the + // package. These Coins would not be available in the underlying CoinsView. + if (auto it = m_temp_added.find(outpoint); it != m_temp_added.end()) { + coin = it->second; + return true; + } + // If an entry in the mempool exists, always return that one, as it's // guaranteed to never conflict with the underlying cache, and it cannot // have pruned entries (as it contains full) transactions. First checking @@ -1082,6 +1089,13 @@ return base->GetCoin(outpoint, coin); } +void CCoinsViewMemPool::PackageAddTransaction(const CTransactionRef &tx) { + for (unsigned int n = 0; n < tx->vout.size(); ++n) { + m_temp_added.emplace(COutPoint(tx->GetId(), n), + Coin(tx->vout[n], MEMPOOL_HEIGHT, false)); + } +} + size_t CTxMemPool::DynamicMemoryUsage() const { LOCK(cs); // Estimate the overhead of mapTx to be 12 pointers + an allocation, as no