Switch CCoinsView and chainstate db from per-txid to per-txout

This patch makes several related changes:
* Changes the CCoinsView virtual methods (GetCoins, HaveCoins, ...)
  to be COutPoint/Coin-based rather than txid/CCoins-based.
* Changes the chainstate db to a new incompatible format that is also
  COutPoint/Coin based.
* Implements reconstruction code for hash_serialized_2.
* Adapts the coins_tests unit tests (thanks to Russell Yanofsky).

A side effect of the new CCoinsView model is that we can no longer
use the (unreliable) test for transaction outputs in the UTXO set
to determine whether we already have a particular transaction.
This commit is contained in:
Pieter Wuille
2017-04-25 11:29:39 -07:00
parent 4ec0d9e794
commit 5083079688
15 changed files with 363 additions and 373 deletions

View File

@@ -524,9 +524,9 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem
indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash);
if (it2 != mapTx.end())
continue;
const CCoins *coins = pcoins->AccessCoins(txin.prevout.hash);
if (nCheckFrequency != 0) assert(coins);
if (!coins || (coins->IsCoinBase() && ((signed long)nMemPoolHeight) - coins->nHeight < COINBASE_MATURITY)) {
const Coin &coin = pcoins->AccessCoin(txin.prevout);
if (nCheckFrequency != 0) assert(!coin.IsPruned());
if (coin.IsPruned() || (coin.IsCoinBase() && ((signed long)nMemPoolHeight) - coin.nHeight < COINBASE_MATURITY)) {
txToRemove.insert(it);
break;
}
@@ -654,8 +654,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
parentSigOpCost += it2->GetSigOpCost();
}
} else {
const CCoins* coins = pcoins->AccessCoins(txin.prevout.hash);
assert(coins && coins->IsAvailable(txin.prevout.n));
assert(pcoins->HaveCoins(txin.prevout));
}
// Check whether its inputs are marked in mapNextTx.
auto it3 = mapNextTx.find(txin.prevout);
@@ -891,20 +890,24 @@ bool CTxMemPool::HasNoInputsOf(const CTransaction &tx) const
CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { }
bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) const {
bool CCoinsViewMemPool::GetCoins(const COutPoint &outpoint, Coin &coin) const {
// 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 the underlying cache risks returning a pruned entry instead.
CTransactionRef ptx = mempool.get(txid);
CTransactionRef ptx = mempool.get(outpoint.hash);
if (ptx) {
coins = CCoins(*ptx, MEMPOOL_HEIGHT);
return true;
if (outpoint.n < ptx->vout.size()) {
coin = Coin(ptx->vout[outpoint.n], MEMPOOL_HEIGHT, false);
return true;
} else {
return false;
}
}
return (base->GetCoins(txid, coins) && !coins.IsPruned());
return (base->GetCoins(outpoint, coin) && !coin.IsPruned());
}
bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) const {
return mempool.exists(txid) || base->HaveCoins(txid);
bool CCoinsViewMemPool::HaveCoins(const COutPoint &outpoint) const {
return mempool.exists(outpoint) || base->HaveCoins(outpoint);
}
size_t CTxMemPool::DynamicMemoryUsage() const {
@@ -1015,7 +1018,7 @@ void CTxMemPool::trackPackageRemoved(const CFeeRate& rate) {
}
}
void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<uint256>* pvNoSpendsRemaining) {
void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<COutPoint>* pvNoSpendsRemaining) {
LOCK(cs);
unsigned nTxnRemoved = 0;
@@ -1046,11 +1049,10 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<uint256>* pvNoSpendsRe
if (pvNoSpendsRemaining) {
BOOST_FOREACH(const CTransaction& tx, txn) {
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
if (exists(txin.prevout.hash))
continue;
auto iter = mapNextTx.lower_bound(COutPoint(txin.prevout.hash, 0));
if (iter == mapNextTx.end() || iter->first->hash != txin.prevout.hash)
pvNoSpendsRemaining->push_back(txin.prevout.hash);
if (exists(txin.prevout.hash)) continue;
if (!mapNextTx.count(txin.prevout)) {
pvNoSpendsRemaining->push_back(txin.prevout);
}
}
}
}
@@ -1067,3 +1069,5 @@ bool CTxMemPool::TransactionWithinChainLimit(const uint256& txid, size_t chainLi
return it == mapTx.end() || (it->GetCountWithAncestors() < chainLimit &&
it->GetCountWithDescendants() < chainLimit);
}
SaltedTxidHasher::SaltedTxidHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {}