Changeset View
Changeset View
Standalone View
Standalone View
src/coins.cpp
Show All 16 Lines | bool CCoinsView::HaveCoin(const COutPoint &outpoint) const { | ||||
return false; | return false; | ||||
} | } | ||||
uint256 CCoinsView::GetBestBlock() const { | uint256 CCoinsView::GetBestBlock() const { | ||||
return uint256(); | return uint256(); | ||||
} | } | ||||
std::vector<uint256> CCoinsView::GetHeadBlocks() const { | std::vector<uint256> CCoinsView::GetHeadBlocks() const { | ||||
return std::vector<uint256>(); | return std::vector<uint256>(); | ||||
} | } | ||||
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { | bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, | ||||
CUtxoCommit *commitDelta) { | |||||
return false; | return false; | ||||
} | } | ||||
CCoinsViewCursor *CCoinsView::Cursor() const { | CCoinsViewCursor *CCoinsView::Cursor() const { | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
CUtxoCommit *CCoinsView::GetCommitment() const { | |||||
return nullptr; | |||||
} | |||||
CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) {} | CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) {} | ||||
bool CCoinsViewBacked::GetCoin(const COutPoint &outpoint, Coin &coin) const { | bool CCoinsViewBacked::GetCoin(const COutPoint &outpoint, Coin &coin) const { | ||||
return base->GetCoin(outpoint, coin); | return base->GetCoin(outpoint, coin); | ||||
} | } | ||||
bool CCoinsViewBacked::HaveCoin(const COutPoint &outpoint) const { | bool CCoinsViewBacked::HaveCoin(const COutPoint &outpoint) const { | ||||
return base->HaveCoin(outpoint); | return base->HaveCoin(outpoint); | ||||
} | } | ||||
uint256 CCoinsViewBacked::GetBestBlock() const { | uint256 CCoinsViewBacked::GetBestBlock() const { | ||||
return base->GetBestBlock(); | return base->GetBestBlock(); | ||||
} | } | ||||
std::vector<uint256> CCoinsViewBacked::GetHeadBlocks() const { | std::vector<uint256> CCoinsViewBacked::GetHeadBlocks() const { | ||||
return base->GetHeadBlocks(); | return base->GetHeadBlocks(); | ||||
} | } | ||||
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { | void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { | ||||
base = &viewIn; | base = &viewIn; | ||||
} | } | ||||
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, | bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, | ||||
const uint256 &hashBlock) { | CUtxoCommit *commitDelta) { | ||||
return base->BatchWrite(mapCoins, hashBlock); | return base->BatchWrite(mapCoins, hashBlock, commitDelta); | ||||
} | } | ||||
CCoinsViewCursor *CCoinsViewBacked::Cursor() const { | CCoinsViewCursor *CCoinsViewBacked::Cursor() const { | ||||
return base->Cursor(); | return base->Cursor(); | ||||
} | } | ||||
size_t CCoinsViewBacked::EstimateSize() const { | size_t CCoinsViewBacked::EstimateSize() const { | ||||
return base->EstimateSize(); | return base->EstimateSize(); | ||||
} | } | ||||
CUtxoCommit *CCoinsViewBacked::GetCommitment() const { | |||||
return base->GetCommitment(); | |||||
} | |||||
SaltedOutpointHasher::SaltedOutpointHasher() | SaltedOutpointHasher::SaltedOutpointHasher() | ||||
: k0(GetRand(std::numeric_limits<uint64_t>::max())), | : k0(GetRand(std::numeric_limits<uint64_t>::max())), | ||||
k1(GetRand(std::numeric_limits<uint64_t>::max())) {} | k1(GetRand(std::numeric_limits<uint64_t>::max())) {} | ||||
CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) | CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) | ||||
: CCoinsViewBacked(baseIn), cachedCoinsUsage(0) {} | : CCoinsViewBacked(baseIn), cachedCoinsUsage(0), | ||||
cacheUtxoCommitDelta(nullptr) {} | |||||
CCoinsViewCache::~CCoinsViewCache() { | |||||
if (cacheUtxoCommitDelta != nullptr) { | |||||
delete cacheUtxoCommitDelta; | |||||
} | |||||
} | |||||
void CCoinsViewCache::CalculateCommitment() { | |||||
// TODO: Currently this is called *before* the cache is filled, | |||||
// and the commitment is maintained on AddCoin/SpentCoin | |||||
// It is probably faster to do it *after* the cache is filled, | |||||
// This does require us to change the in-memory representation | |||||
// of Coin to include the scriptpubkeys for spent outputs | |||||
// We can't start maintaining the commitment in the middle of | |||||
// a cache batch | |||||
assert(cacheCoins.empty()); | |||||
assert(cacheUtxoCommitDelta == nullptr); | |||||
cacheUtxoCommitDelta = new CUtxoCommit(); | |||||
} | |||||
size_t CCoinsViewCache::DynamicMemoryUsage() const { | size_t CCoinsViewCache::DynamicMemoryUsage() const { | ||||
return memusage::DynamicUsage(cacheCoins) + cachedCoinsUsage; | return memusage::DynamicUsage(cacheCoins) + cachedCoinsUsage; | ||||
} | } | ||||
CCoinsMap::iterator | CCoinsMap::iterator | ||||
CCoinsViewCache::FetchCoin(const COutPoint &outpoint) const { | CCoinsViewCache::FetchCoin(const COutPoint &outpoint) const { | ||||
CCoinsMap::iterator it = cacheCoins.find(outpoint); | CCoinsMap::iterator it = cacheCoins.find(outpoint); | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | if (!possible_overwrite) { | ||||
"Adding new coin that replaces non-pruned entry"); | "Adding new coin that replaces non-pruned entry"); | ||||
} | } | ||||
fresh = !(it->second.flags & CCoinsCacheEntry::DIRTY); | fresh = !(it->second.flags & CCoinsCacheEntry::DIRTY); | ||||
} | } | ||||
it->second.coin = std::move(coin); | it->second.coin = std::move(coin); | ||||
it->second.flags |= | it->second.flags |= | ||||
CCoinsCacheEntry::DIRTY | (fresh ? CCoinsCacheEntry::FRESH : 0); | CCoinsCacheEntry::DIRTY | (fresh ? CCoinsCacheEntry::FRESH : 0); | ||||
cachedCoinsUsage += it->second.coin.DynamicMemoryUsage(); | cachedCoinsUsage += it->second.coin.DynamicMemoryUsage(); | ||||
if (cacheUtxoCommitDelta != nullptr) { | |||||
cacheUtxoCommitDelta->Add(outpoint, it->second.coin); | |||||
} | |||||
} | } | ||||
void AddCoins(CCoinsViewCache &cache, const CTransaction &tx, int nHeight, | void AddCoins(CCoinsViewCache &cache, const CTransaction &tx, int nHeight, | ||||
bool check) { | bool check) { | ||||
bool fCoinbase = tx.IsCoinBase(); | bool fCoinbase = tx.IsCoinBase(); | ||||
const uint256 &txid = tx.GetHash(); | const uint256 &txid = tx.GetHash(); | ||||
for (size_t i = 0; i < tx.vout.size(); ++i) { | for (size_t i = 0; i < tx.vout.size(); ++i) { | ||||
bool overwrite = check ? cache.HaveCoin(COutPoint(txid, i)) : fCoinbase; | bool overwrite = check ? cache.HaveCoin(COutPoint(txid, i)) : fCoinbase; | ||||
// Always set the possible_overwrite flag to AddCoin for coinbase txn, | // Always set the possible_overwrite flag to AddCoin for coinbase txn, | ||||
// in order to correctly deal with the pre-BIP30 occurrences of | // in order to correctly deal with the pre-BIP30 occurrences of | ||||
// duplicate coinbase transactions. | // duplicate coinbase transactions. | ||||
cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase), | cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase), | ||||
overwrite); | overwrite); | ||||
} | } | ||||
} | } | ||||
bool CCoinsViewCache::SpendCoin(const COutPoint &outpoint, Coin *moveout) { | bool CCoinsViewCache::SpendCoin(const COutPoint &outpoint, Coin *moveout) { | ||||
CCoinsMap::iterator it = FetchCoin(outpoint); | CCoinsMap::iterator it = FetchCoin(outpoint); | ||||
if (it == cacheCoins.end()) { | if (it == cacheCoins.end()) { | ||||
return false; | return false; | ||||
} | } | ||||
if (cacheUtxoCommitDelta != nullptr) { | |||||
cacheUtxoCommitDelta->Remove(outpoint, it->second.coin); | |||||
} | |||||
cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage(); | cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage(); | ||||
if (moveout) { | if (moveout) { | ||||
*moveout = std::move(it->second.coin); | *moveout = std::move(it->second.coin); | ||||
} | } | ||||
if (it->second.flags & CCoinsCacheEntry::FRESH) { | if (it->second.flags & CCoinsCacheEntry::FRESH) { | ||||
cacheCoins.erase(it); | cacheCoins.erase(it); | ||||
} else { | } else { | ||||
it->second.flags |= CCoinsCacheEntry::DIRTY; | it->second.flags |= CCoinsCacheEntry::DIRTY; | ||||
Show All 29 Lines | uint256 CCoinsViewCache::GetBestBlock() const { | ||||
return hashBlock; | return hashBlock; | ||||
} | } | ||||
void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) { | void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) { | ||||
hashBlock = hashBlockIn; | hashBlock = hashBlockIn; | ||||
} | } | ||||
bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, | bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, | ||||
const uint256 &hashBlockIn) { | const uint256 &hashBlockIn, | ||||
CUtxoCommit *commitDelta) { | |||||
// Merge commitment | |||||
if (commitDelta != nullptr) { | |||||
if (cacheUtxoCommitDelta == nullptr) { | |||||
cacheUtxoCommitDelta = new CUtxoCommit(); | |||||
} | |||||
cacheUtxoCommitDelta->Add(*commitDelta); | |||||
} else { | |||||
// Stop maintaining commitment | |||||
// If our child-cache doesn't supply one we can no longer | |||||
// rely on it | |||||
if (cacheUtxoCommitDelta != nullptr) { | |||||
delete cacheUtxoCommitDelta; | |||||
cacheUtxoCommitDelta = nullptr; | |||||
} | |||||
} | |||||
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { | for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { | ||||
// Ignore non-dirty entries (optimization). | // Ignore non-dirty entries (optimization). | ||||
if (it->second.flags & CCoinsCacheEntry::DIRTY) { | if (it->second.flags & CCoinsCacheEntry::DIRTY) { | ||||
CCoinsMap::iterator itUs = cacheCoins.find(it->first); | CCoinsMap::iterator itUs = cacheCoins.find(it->first); | ||||
if (itUs == cacheCoins.end()) { | if (itUs == cacheCoins.end()) { | ||||
// The parent cache does not have an entry, while the child does | // The parent cache does not have an entry, while the child does | ||||
// We can ignore it if it's both FRESH and pruned in the child | // We can ignore it if it's both FRESH and pruned in the child | ||||
if (!(it->second.flags & CCoinsCacheEntry::FRESH && | if (!(it->second.flags & CCoinsCacheEntry::FRESH && | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { | ||||
CCoinsMap::iterator itOld = it++; | CCoinsMap::iterator itOld = it++; | ||||
mapCoins.erase(itOld); | mapCoins.erase(itOld); | ||||
} | } | ||||
hashBlock = hashBlockIn; | hashBlock = hashBlockIn; | ||||
return true; | return true; | ||||
} | } | ||||
bool CCoinsViewCache::Flush() { | bool CCoinsViewCache::Flush() { | ||||
bool fOk = base->BatchWrite(cacheCoins, hashBlock); | bool fOk = base->BatchWrite(cacheCoins, hashBlock, cacheUtxoCommitDelta); | ||||
cacheCoins.clear(); | cacheCoins.clear(); | ||||
if (cacheUtxoCommitDelta != nullptr) { | |||||
cacheUtxoCommitDelta->Clear(); | |||||
} | |||||
cachedCoinsUsage = 0; | cachedCoinsUsage = 0; | ||||
return fOk; | return fOk; | ||||
} | } | ||||
CUtxoCommit *CCoinsViewCache::GetCommitment() const { | |||||
CUtxoCommit *parent = base->GetCommitment(); | |||||
if (parent == nullptr || cacheUtxoCommitDelta == nullptr) { | |||||
return nullptr; | |||||
ivan_s: If it's ever the case that `parent != nullptr` but `cacheUtxoCommitDelta == nullptr`, there… | |||||
jasonbcoxAuthorUnsubmitted Not Done Inline ActionsProbably best to log an error for the specific case that you described. jasonbcox: Probably best to log an error for the specific case that you described. | |||||
tomtomtom7Unsubmitted Not Done Inline ActionsYes. I believe this is possible due to the way assumevalid works. Initially the headers aren't in yet, so blocks aren't assumed valid and the commitment is maintained. Once assumevalid kicks in, the commitment is cleared. tomtomtom7: Yes. I believe this is possible due to the way assumevalid works. Initially the headers aren't… | |||||
tomtomtom7Unsubmitted Not Done Inline ActionsYes. Good point about unique_ptr. Will change. tomtomtom7: Yes. Good point about unique_ptr. Will change. | |||||
} else { | |||||
// merge this commitment delta with parent commitment | |||||
CUtxoCommit *result = new CUtxoCommit(); | |||||
result->Add(*parent); | |||||
result->Add(*this->cacheUtxoCommitDelta); | |||||
return result; | |||||
} | |||||
} | |||||
bool CCoinsViewCache::HasCommitmentDelta() const { | |||||
return cacheUtxoCommitDelta != nullptr; | |||||
} | |||||
void CCoinsViewCache::Uncache(const COutPoint &outpoint) { | void CCoinsViewCache::Uncache(const COutPoint &outpoint) { | ||||
CCoinsMap::iterator it = cacheCoins.find(outpoint); | CCoinsMap::iterator it = cacheCoins.find(outpoint); | ||||
if (it != cacheCoins.end() && it->second.flags == 0) { | if (it != cacheCoins.end() && it->second.flags == 0) { | ||||
cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage(); | cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage(); | ||||
cacheCoins.erase(it); | cacheCoins.erase(it); | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 74 Lines • Show Last 20 Lines |
If it's ever the case that parent != nullptr but cacheUtxoCommitDelta == nullptr, there might be a memory leak. Also the caller of this method should not have to perform a delete. I think it would be better to work with a unique_ptr in this case.