From 84674082b0c4cfcdd54fb97a29bc841aa7f691c2 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 5 Nov 2013 02:27:39 +0100 Subject: [PATCH] Make CCoinsView use block hashes instead of indices --- src/main.cpp | 57 +++++++++++++++++++++++-------------------- src/main.h | 24 +++++++++--------- src/rpcblockchain.cpp | 6 +++-- src/txdb.cpp | 23 ++++++++--------- src/txdb.h | 6 ++--- 5 files changed, 60 insertions(+), 56 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 2a133b3ea..5b330536f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -236,9 +236,9 @@ CBlockIndex *CChain::FindFork(const CBlockLocator &locator) const { bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) { return false; } bool CCoinsView::SetCoins(const uint256 &txid, const CCoins &coins) { return false; } bool CCoinsView::HaveCoins(const uint256 &txid) { return false; } -CBlockIndex *CCoinsView::GetBestBlock() { return NULL; } -bool CCoinsView::SetBestBlock(CBlockIndex *pindex) { return false; } -bool CCoinsView::BatchWrite(const std::map &mapCoins, CBlockIndex *pindex) { return false; } +uint256 CCoinsView::GetBestBlock() { return uint256(0); } +bool CCoinsView::SetBestBlock(const uint256 &hashBlock) { return false; } +bool CCoinsView::BatchWrite(const std::map &mapCoins, const uint256 &hashBlock) { return false; } bool CCoinsView::GetStats(CCoinsStats &stats) { return false; } @@ -246,13 +246,13 @@ CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { } bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) { return base->GetCoins(txid, coins); } bool CCoinsViewBacked::SetCoins(const uint256 &txid, const CCoins &coins) { return base->SetCoins(txid, coins); } bool CCoinsViewBacked::HaveCoins(const uint256 &txid) { return base->HaveCoins(txid); } -CBlockIndex *CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); } -bool CCoinsViewBacked::SetBestBlock(CBlockIndex *pindex) { return base->SetBestBlock(pindex); } +uint256 CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); } +bool CCoinsViewBacked::SetBestBlock(const uint256 &hashBlock) { return base->SetBestBlock(hashBlock); } void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } -bool CCoinsViewBacked::BatchWrite(const std::map &mapCoins, CBlockIndex *pindex) { return base->BatchWrite(mapCoins, pindex); } +bool CCoinsViewBacked::BatchWrite(const std::map &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); } bool CCoinsViewBacked::GetStats(CCoinsStats &stats) { return base->GetStats(stats); } -CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), pindexTip(NULL) { } +CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), hashBlock(0) { } bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) { if (cacheCoins.count(txid)) { @@ -293,26 +293,26 @@ bool CCoinsViewCache::HaveCoins(const uint256 &txid) { return FetchCoins(txid) != cacheCoins.end(); } -CBlockIndex *CCoinsViewCache::GetBestBlock() { - if (pindexTip == NULL) - pindexTip = base->GetBestBlock(); - return pindexTip; +uint256 CCoinsViewCache::GetBestBlock() { + if (hashBlock == uint256(0)) + hashBlock = base->GetBestBlock(); + return hashBlock; } -bool CCoinsViewCache::SetBestBlock(CBlockIndex *pindex) { - pindexTip = pindex; +bool CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) { + hashBlock = hashBlockIn; return true; } -bool CCoinsViewCache::BatchWrite(const std::map &mapCoins, CBlockIndex *pindex) { +bool CCoinsViewCache::BatchWrite(const std::map &mapCoins, const uint256 &hashBlockIn) { for (std::map::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) cacheCoins[it->first] = it->second; - pindexTip = pindex; + hashBlock = hashBlockIn; return true; } bool CCoinsViewCache::Flush() { - bool fOk = base->BatchWrite(cacheCoins, pindexTip); + bool fOk = base->BatchWrite(cacheCoins, hashBlock); if (fOk) cacheCoins.clear(); return fOk; @@ -1498,7 +1498,8 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach // While checking, GetBestBlock() refers to the parent block. // This is also true for mempool checks. - int nSpendHeight = inputs.GetBestBlock()->nHeight + 1; + CBlockIndex *pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second; + int nSpendHeight = pindexPrev->nHeight + 1; int64_t nValueIn = 0; int64_t nFees = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) @@ -1568,7 +1569,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) { - assert(pindex == view.GetBestBlock()); + assert(pindex->GetBlockHash() == view.GetBestBlock()); if (pfClean) *pfClean = false; @@ -1644,7 +1645,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex } // move best block pointer to prevout block - view.SetBestBlock(pindex->pprev); + view.SetBestBlock(pindex->pprev->GetBlockHash()); if (pfClean) { *pfClean = fClean; @@ -1693,12 +1694,13 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C return false; // verify that the view's current state corresponds to the previous block - assert(pindex->pprev == view.GetBestBlock()); + uint256 hashPrevBlock = pindex->pprev == NULL ? uint256(0) : pindex->pprev->GetBlockHash(); + assert(hashPrevBlock == view.GetBestBlock()); // Special case for the genesis block, skipping connection of its transactions // (its coinbase is unspendable) if (block.GetHash() == Params().HashGenesisBlock()) { - view.SetBestBlock(pindex); + view.SetBestBlock(pindex->GetBlockHash()); return true; } @@ -1828,7 +1830,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C return state.Abort(_("Failed to write transaction index")); // add this block to the view's block chain - assert(view.SetBestBlock(pindex)); + assert(view.SetBestBlock(pindex->GetBlockHash())); // Watch for transactions paying to me for (unsigned int i = 0; i < block.vtx.size(); i++) @@ -1846,7 +1848,9 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) CCoinsViewCache view(*pcoinsTip, true); // Find the fork (typically, there is none) - CBlockIndex* pfork = view.GetBestBlock(); + std::map::iterator it = mapBlockIndex.find(view.GetBestBlock()); + CBlockIndex* ptip = (it != mapBlockIndex.end()) ? it->second : NULL; + CBlockIndex* pfork = ptip; CBlockIndex* plonger = pindexNew; while (pfork && pfork != plonger) { @@ -1862,7 +1866,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) // List of what to disconnect (typically nothing) vector vDisconnect; - for (CBlockIndex* pindex = view.GetBestBlock(); pindex != pfork; pindex = pindex->pprev) + for (CBlockIndex* pindex = ptip; pindex != pfork; pindex = pindex->pprev) vDisconnect.push_back(pindex); // List of what to connect (typically only pindexNew) @@ -2687,9 +2691,10 @@ bool static LoadBlockIndexDB() LogPrintf("LoadBlockIndexDB(): transaction index %s\n", fTxIndex ? "enabled" : "disabled"); // Load pointer to end of best chain - chainActive.SetTip(pcoinsTip->GetBestBlock()); - if (chainActive.Tip() == NULL) + std::map::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); + if (it == mapBlockIndex.end()) return true; + chainActive.SetTip(it->second); LogPrintf("LoadBlockIndexDB(): hashBestChain=%s height=%d date=%s\n", chainActive.Tip()->GetBlockHash().ToString().c_str(), chainActive.Height(), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()).c_str()); diff --git a/src/main.h b/src/main.h index d71780261..b8db357ae 100644 --- a/src/main.h +++ b/src/main.h @@ -1062,14 +1062,14 @@ public: // This may (but cannot always) return true for fully spent transactions virtual bool HaveCoins(const uint256 &txid); - // Retrieve the block index whose state this CCoinsView currently represents - virtual CBlockIndex *GetBestBlock(); + // Retrieve the block hash whose state this CCoinsView currently represents + virtual uint256 GetBestBlock(); - // Modify the currently active block index - virtual bool SetBestBlock(CBlockIndex *pindex); + // Modify the currently active block hash + virtual bool SetBestBlock(const uint256 &hashBlock); // Do a bulk modification (multiple SetCoins + one SetBestBlock) - virtual bool BatchWrite(const std::map &mapCoins, CBlockIndex *pindex); + virtual bool BatchWrite(const std::map &mapCoins, const uint256 &hashBlock); // Calculate statistics about the unspent transaction output set virtual bool GetStats(CCoinsStats &stats); @@ -1089,10 +1089,10 @@ public: bool GetCoins(const uint256 &txid, CCoins &coins); bool SetCoins(const uint256 &txid, const CCoins &coins); bool HaveCoins(const uint256 &txid); - CBlockIndex *GetBestBlock(); - bool SetBestBlock(CBlockIndex *pindex); + uint256 GetBestBlock(); + bool SetBestBlock(const uint256 &hashBlock); void SetBackend(CCoinsView &viewIn); - bool BatchWrite(const std::map &mapCoins, CBlockIndex *pindex); + bool BatchWrite(const std::map &mapCoins, const uint256 &hashBlock); bool GetStats(CCoinsStats &stats); }; @@ -1100,7 +1100,7 @@ public: class CCoinsViewCache : public CCoinsViewBacked { protected: - CBlockIndex *pindexTip; + uint256 hashBlock; std::map cacheCoins; public: @@ -1110,9 +1110,9 @@ public: bool GetCoins(const uint256 &txid, CCoins &coins); bool SetCoins(const uint256 &txid, const CCoins &coins); bool HaveCoins(const uint256 &txid); - CBlockIndex *GetBestBlock(); - bool SetBestBlock(CBlockIndex *pindex); - bool BatchWrite(const std::map &mapCoins, CBlockIndex *pindex); + uint256 GetBestBlock(); + bool SetBestBlock(const uint256 &hashBlock); + bool BatchWrite(const std::map &mapCoins, const uint256 &hashBlock); // Return a modifiable reference to a CCoins. Check HaveCoins first. // Many methods explicitly require a CCoinsViewCache because of this method, to reduce diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 18e213257..483d8d2e8 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -245,11 +245,13 @@ Value gettxout(const Array& params, bool fHelp) if (n<0 || (unsigned int)n>=coins.vout.size() || coins.vout[n].IsNull()) return Value::null; - ret.push_back(Pair("bestblock", pcoinsTip->GetBestBlock()->GetBlockHash().GetHex())); + std::map::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); + CBlockIndex *pindex = it->second; + ret.push_back(Pair("bestblock", pindex->GetBlockHash().GetHex())); if ((unsigned int)coins.nHeight == MEMPOOL_HEIGHT) ret.push_back(Pair("confirmations", 0)); else - ret.push_back(Pair("confirmations", pcoinsTip->GetBestBlock()->nHeight - coins.nHeight + 1)); + ret.push_back(Pair("confirmations", pindex->nHeight - coins.nHeight + 1)); ret.push_back(Pair("value", ValueFromAmount(coins.vout[n].nValue))); Object o; ScriptPubKeyToJSON(coins.vout[n].scriptPubKey, o, true); diff --git a/src/txdb.cpp b/src/txdb.cpp index 3c8b0b7fe..99deb3140 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -40,30 +40,27 @@ bool CCoinsViewDB::HaveCoins(const uint256 &txid) { return db.Exists(make_pair('c', txid)); } -CBlockIndex *CCoinsViewDB::GetBestBlock() { +uint256 CCoinsViewDB::GetBestBlock() { uint256 hashBestChain; if (!db.Read('B', hashBestChain)) - return NULL; - std::map::iterator it = mapBlockIndex.find(hashBestChain); - if (it == mapBlockIndex.end()) - return NULL; - return it->second; + return uint256(0); + return hashBestChain; } -bool CCoinsViewDB::SetBestBlock(CBlockIndex *pindex) { +bool CCoinsViewDB::SetBestBlock(const uint256 &hashBlock) { CLevelDBBatch batch; - BatchWriteHashBestChain(batch, pindex->GetBlockHash()); + BatchWriteHashBestChain(batch, hashBlock); return db.WriteBatch(batch); } -bool CCoinsViewDB::BatchWrite(const std::map &mapCoins, CBlockIndex *pindex) { +bool CCoinsViewDB::BatchWrite(const std::map &mapCoins, const uint256 &hashBlock) { LogPrint("coindb", "Committing %u changed transactions to coin database...\n", (unsigned int)mapCoins.size()); CLevelDBBatch batch; for (std::map::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) BatchWriteCoins(batch, it->first, it->second); - if (pindex) - BatchWriteHashBestChain(batch, pindex->GetBlockHash()); + if (hashBlock != uint256(0)) + BatchWriteHashBestChain(batch, hashBlock); return db.WriteBatch(batch); } @@ -115,7 +112,7 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) { pcursor->SeekToFirst(); CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); - stats.hashBlock = GetBestBlock()->GetBlockHash(); + stats.hashBlock = GetBestBlock(); ss << stats.hashBlock; int64_t nTotalAmount = 0; while (pcursor->Valid()) { @@ -155,7 +152,7 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) { } } delete pcursor; - stats.nHeight = GetBestBlock()->nHeight; + stats.nHeight = mapBlockIndex.find(GetBestBlock())->second->nHeight; stats.hashSerialized = ss.GetHash(); stats.nTotalAmount = nTotalAmount; return true; diff --git a/src/txdb.h b/src/txdb.h index 8043a595a..7ce6585d3 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -29,9 +29,9 @@ public: bool GetCoins(const uint256 &txid, CCoins &coins); bool SetCoins(const uint256 &txid, const CCoins &coins); bool HaveCoins(const uint256 &txid); - CBlockIndex *GetBestBlock(); - bool SetBestBlock(CBlockIndex *pindex); - bool BatchWrite(const std::map &mapCoins, CBlockIndex *pindex); + uint256 GetBestBlock(); + bool SetBestBlock(const uint256 &hashBlock); + bool BatchWrite(const std::map &mapCoins, const uint256 &hashBlock); bool GetStats(CCoinsStats &stats); };