scripted-diff: various renames for per-utxo consistency
Thanks to John Newberry for pointing these out. -BEGIN VERIFY SCRIPT- sed -i 's/\<GetCoins\>/GetCoin/g' src/*.cpp src/*.h src/*/*.cpp src/*/*.h sed -i 's/\<HaveCoins\>/HaveCoin/g' src/*.cpp src/*.h src/*/*.cpp src/*/*.h sed -i 's/\<HaveCoinsInCache\>/HaveCoinInCache/g' src/*.cpp src/*.h src/*/*.cpp src/*/*.h sed -i 's/\<IsPruned\>/IsSpent/g' src/*.cpp src/*.h src/*/*.cpp src/*/*.h sed -i 's/\<FetchCoins\>/FetchCoin/g' src/*.cpp src/*.h src/*/*.cpp src/*/*.h sed -i 's/\<CoinsEntry\>/CoinEntry/g' src/*.cpp src/*.h src/*/*.cpp src/*/*.h sed -i 's/\<vHashTxnToUncache\>/coins_to_uncache/g' src/*.cpp src/*.h src/*/*.cpp src/*/*.h sed -i 's/\<vHashTxToUncache\>/coins_to_uncache/g' src/*.cpp src/*.h src/*/*.cpp src/*/*.h sed -i 's/\<fHadTxInCache\>/had_coin_in_cache/g' src/*.cpp src/*.h src/*/*.cpp src/*/*.h sed -i 's/\<coinbaseids\>/coinbase_coins/g' src/*.cpp src/*.h src/*/*.cpp src/*/*.h sed -i 's/\<disconnectedids\>/disconnected_coins/g' src/*.cpp src/*.h src/*/*.cpp src/*/*.h sed -i 's/\<duplicateids\>/duplicate_coins/g' src/*.cpp src/*.h src/*/*.cpp src/*/*.h sed -i 's/\<oldcoins\>/old_coin/g' src/test/coins_tests.cpp sed -i 's/\<origcoins\>/orig_coin/g' src/*.cpp src/*.h src/*/*.cpp src/*/*.h -END VERIFY SCRIPT-
This commit is contained in:
parent
a5e02bc7f8
commit
589827975f
16 changed files with 132 additions and 132 deletions
|
@ -562,7 +562,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
|
||||||
|
|
||||||
{
|
{
|
||||||
const Coin& coin = view.AccessCoin(out);
|
const Coin& coin = view.AccessCoin(out);
|
||||||
if (!coin.IsPruned() && coin.out.scriptPubKey != scriptPubKey) {
|
if (!coin.IsSpent() && coin.out.scriptPubKey != scriptPubKey) {
|
||||||
std::string err("Previous output scriptPubKey mismatch:\n");
|
std::string err("Previous output scriptPubKey mismatch:\n");
|
||||||
err = err + ScriptToAsmStr(coin.out.scriptPubKey) + "\nvs:\n"+
|
err = err + ScriptToAsmStr(coin.out.scriptPubKey) + "\nvs:\n"+
|
||||||
ScriptToAsmStr(scriptPubKey);
|
ScriptToAsmStr(scriptPubKey);
|
||||||
|
@ -598,7 +598,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
|
||||||
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
|
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
|
||||||
CTxIn& txin = mergedTx.vin[i];
|
CTxIn& txin = mergedTx.vin[i];
|
||||||
const Coin& coin = view.AccessCoin(txin.prevout);
|
const Coin& coin = view.AccessCoin(txin.prevout);
|
||||||
if (coin.IsPruned()) {
|
if (coin.IsSpent()) {
|
||||||
fComplete = false;
|
fComplete = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,16 +10,16 @@
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
bool CCoinsView::GetCoins(const COutPoint &outpoint, Coin &coin) const { return false; }
|
bool CCoinsView::GetCoin(const COutPoint &outpoint, Coin &coin) const { return false; }
|
||||||
bool CCoinsView::HaveCoins(const COutPoint &outpoint) const { return false; }
|
bool CCoinsView::HaveCoin(const COutPoint &outpoint) const { return false; }
|
||||||
uint256 CCoinsView::GetBestBlock() const { return uint256(); }
|
uint256 CCoinsView::GetBestBlock() const { return uint256(); }
|
||||||
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return false; }
|
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return false; }
|
||||||
CCoinsViewCursor *CCoinsView::Cursor() const { return 0; }
|
CCoinsViewCursor *CCoinsView::Cursor() const { return 0; }
|
||||||
|
|
||||||
|
|
||||||
CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
|
CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
|
||||||
bool CCoinsViewBacked::GetCoins(const COutPoint &outpoint, Coin &coin) const { return base->GetCoins(outpoint, coin); }
|
bool CCoinsViewBacked::GetCoin(const COutPoint &outpoint, Coin &coin) const { return base->GetCoin(outpoint, coin); }
|
||||||
bool CCoinsViewBacked::HaveCoins(const COutPoint &outpoint) const { return base->HaveCoins(outpoint); }
|
bool CCoinsViewBacked::HaveCoin(const COutPoint &outpoint) const { return base->HaveCoin(outpoint); }
|
||||||
uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
|
uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
|
||||||
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
|
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
|
||||||
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); }
|
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); }
|
||||||
|
@ -34,15 +34,15 @@ size_t CCoinsViewCache::DynamicMemoryUsage() const {
|
||||||
return memusage::DynamicUsage(cacheCoins) + cachedCoinsUsage;
|
return memusage::DynamicUsage(cacheCoins) + cachedCoinsUsage;
|
||||||
}
|
}
|
||||||
|
|
||||||
CCoinsMap::iterator CCoinsViewCache::FetchCoins(const COutPoint &outpoint) const {
|
CCoinsMap::iterator CCoinsViewCache::FetchCoin(const COutPoint &outpoint) const {
|
||||||
CCoinsMap::iterator it = cacheCoins.find(outpoint);
|
CCoinsMap::iterator it = cacheCoins.find(outpoint);
|
||||||
if (it != cacheCoins.end())
|
if (it != cacheCoins.end())
|
||||||
return it;
|
return it;
|
||||||
Coin tmp;
|
Coin tmp;
|
||||||
if (!base->GetCoins(outpoint, tmp))
|
if (!base->GetCoin(outpoint, tmp))
|
||||||
return cacheCoins.end();
|
return cacheCoins.end();
|
||||||
CCoinsMap::iterator ret = cacheCoins.emplace(std::piecewise_construct, std::forward_as_tuple(outpoint), std::forward_as_tuple(std::move(tmp))).first;
|
CCoinsMap::iterator ret = cacheCoins.emplace(std::piecewise_construct, std::forward_as_tuple(outpoint), std::forward_as_tuple(std::move(tmp))).first;
|
||||||
if (ret->second.coin.IsPruned()) {
|
if (ret->second.coin.IsSpent()) {
|
||||||
// The parent only has an empty entry for this outpoint; we can consider our
|
// The parent only has an empty entry for this outpoint; we can consider our
|
||||||
// version as fresh.
|
// version as fresh.
|
||||||
ret->second.flags = CCoinsCacheEntry::FRESH;
|
ret->second.flags = CCoinsCacheEntry::FRESH;
|
||||||
|
@ -51,8 +51,8 @@ CCoinsMap::iterator CCoinsViewCache::FetchCoins(const COutPoint &outpoint) const
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CCoinsViewCache::GetCoins(const COutPoint &outpoint, Coin &coin) const {
|
bool CCoinsViewCache::GetCoin(const COutPoint &outpoint, Coin &coin) const {
|
||||||
CCoinsMap::const_iterator it = FetchCoins(outpoint);
|
CCoinsMap::const_iterator it = FetchCoin(outpoint);
|
||||||
if (it != cacheCoins.end()) {
|
if (it != cacheCoins.end()) {
|
||||||
coin = it->second.coin;
|
coin = it->second.coin;
|
||||||
return true;
|
return true;
|
||||||
|
@ -61,7 +61,7 @@ bool CCoinsViewCache::GetCoins(const COutPoint &outpoint, Coin &coin) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possible_overwrite) {
|
void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possible_overwrite) {
|
||||||
assert(!coin.IsPruned());
|
assert(!coin.IsSpent());
|
||||||
if (coin.out.scriptPubKey.IsUnspendable()) return;
|
if (coin.out.scriptPubKey.IsUnspendable()) return;
|
||||||
CCoinsMap::iterator it;
|
CCoinsMap::iterator it;
|
||||||
bool inserted;
|
bool inserted;
|
||||||
|
@ -71,7 +71,7 @@ void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possi
|
||||||
cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
|
cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
|
||||||
}
|
}
|
||||||
if (!possible_overwrite) {
|
if (!possible_overwrite) {
|
||||||
if (!it->second.coin.IsPruned()) {
|
if (!it->second.coin.IsSpent()) {
|
||||||
throw std::logic_error("Adding new coin that replaces non-pruned entry");
|
throw std::logic_error("Adding new coin that replaces non-pruned entry");
|
||||||
}
|
}
|
||||||
fresh = !(it->second.flags & CCoinsCacheEntry::DIRTY);
|
fresh = !(it->second.flags & CCoinsCacheEntry::DIRTY);
|
||||||
|
@ -92,7 +92,7 @@ void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CCoinsViewCache::SpendCoin(const COutPoint &outpoint, Coin* moveout) {
|
void CCoinsViewCache::SpendCoin(const COutPoint &outpoint, Coin* moveout) {
|
||||||
CCoinsMap::iterator it = FetchCoins(outpoint);
|
CCoinsMap::iterator it = FetchCoin(outpoint);
|
||||||
if (it == cacheCoins.end()) return;
|
if (it == cacheCoins.end()) return;
|
||||||
cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
|
cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
|
||||||
if (moveout) {
|
if (moveout) {
|
||||||
|
@ -109,7 +109,7 @@ void CCoinsViewCache::SpendCoin(const COutPoint &outpoint, Coin* moveout) {
|
||||||
static const Coin coinEmpty;
|
static const Coin coinEmpty;
|
||||||
|
|
||||||
const Coin& CCoinsViewCache::AccessCoin(const COutPoint &outpoint) const {
|
const Coin& CCoinsViewCache::AccessCoin(const COutPoint &outpoint) const {
|
||||||
CCoinsMap::const_iterator it = FetchCoins(outpoint);
|
CCoinsMap::const_iterator it = FetchCoin(outpoint);
|
||||||
if (it == cacheCoins.end()) {
|
if (it == cacheCoins.end()) {
|
||||||
return coinEmpty;
|
return coinEmpty;
|
||||||
} else {
|
} else {
|
||||||
|
@ -117,12 +117,12 @@ const Coin& CCoinsViewCache::AccessCoin(const COutPoint &outpoint) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CCoinsViewCache::HaveCoins(const COutPoint &outpoint) const {
|
bool CCoinsViewCache::HaveCoin(const COutPoint &outpoint) const {
|
||||||
CCoinsMap::const_iterator it = FetchCoins(outpoint);
|
CCoinsMap::const_iterator it = FetchCoin(outpoint);
|
||||||
return (it != cacheCoins.end() && !it->second.coin.IsPruned());
|
return (it != cacheCoins.end() && !it->second.coin.IsSpent());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CCoinsViewCache::HaveCoinsInCache(const COutPoint &outpoint) const {
|
bool CCoinsViewCache::HaveCoinInCache(const COutPoint &outpoint) const {
|
||||||
CCoinsMap::const_iterator it = cacheCoins.find(outpoint);
|
CCoinsMap::const_iterator it = cacheCoins.find(outpoint);
|
||||||
return it != cacheCoins.end();
|
return it != cacheCoins.end();
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,7 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
|
||||||
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 && it->second.coin.IsPruned())) {
|
if (!(it->second.flags & CCoinsCacheEntry::FRESH && it->second.coin.IsSpent())) {
|
||||||
// Otherwise we will need to create it in the parent
|
// Otherwise we will need to create it in the parent
|
||||||
// and move the data up and mark it as dirty
|
// and move the data up and mark it as dirty
|
||||||
CCoinsCacheEntry& entry = cacheCoins[it->first];
|
CCoinsCacheEntry& entry = cacheCoins[it->first];
|
||||||
|
@ -162,11 +162,11 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
|
||||||
// parent cache entry has unspent outputs. If this ever happens,
|
// parent cache entry has unspent outputs. If this ever happens,
|
||||||
// it means the FRESH flag was misapplied and there is a logic
|
// it means the FRESH flag was misapplied and there is a logic
|
||||||
// error in the calling code.
|
// error in the calling code.
|
||||||
if ((it->second.flags & CCoinsCacheEntry::FRESH) && !itUs->second.coin.IsPruned())
|
if ((it->second.flags & CCoinsCacheEntry::FRESH) && !itUs->second.coin.IsSpent())
|
||||||
throw std::logic_error("FRESH flag misapplied to cache entry for base transaction with spendable outputs");
|
throw std::logic_error("FRESH flag misapplied to cache entry for base transaction with spendable outputs");
|
||||||
|
|
||||||
// Found the entry in the parent cache
|
// Found the entry in the parent cache
|
||||||
if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coin.IsPruned()) {
|
if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coin.IsSpent()) {
|
||||||
// The grandparent does not have an entry, and the child is
|
// The grandparent does not have an entry, and the child is
|
||||||
// modified and being pruned. This means we can just delete
|
// modified and being pruned. This means we can just delete
|
||||||
// it from the parent.
|
// it from the parent.
|
||||||
|
@ -229,7 +229,7 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
|
||||||
{
|
{
|
||||||
if (!tx.IsCoinBase()) {
|
if (!tx.IsCoinBase()) {
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
||||||
if (!HaveCoins(tx.vin[i].prevout)) {
|
if (!HaveCoin(tx.vin[i].prevout)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -244,7 +244,7 @@ const Coin& AccessByTxid(const CCoinsViewCache& view, const uint256& txid)
|
||||||
COutPoint iter(txid, 0);
|
COutPoint iter(txid, 0);
|
||||||
while (iter.n < MAX_OUTPUTS_PER_BLOCK) {
|
while (iter.n < MAX_OUTPUTS_PER_BLOCK) {
|
||||||
const Coin& alternate = view.AccessCoin(iter);
|
const Coin& alternate = view.AccessCoin(iter);
|
||||||
if (!alternate.IsPruned()) return alternate;
|
if (!alternate.IsSpent()) return alternate;
|
||||||
++iter.n;
|
++iter.n;
|
||||||
}
|
}
|
||||||
return coinEmpty;
|
return coinEmpty;
|
||||||
|
|
24
src/coins.h
24
src/coins.h
|
@ -58,7 +58,7 @@ public:
|
||||||
|
|
||||||
template<typename Stream>
|
template<typename Stream>
|
||||||
void Serialize(Stream &s) const {
|
void Serialize(Stream &s) const {
|
||||||
assert(!IsPruned());
|
assert(!IsSpent());
|
||||||
uint32_t code = nHeight * 2 + fCoinBase;
|
uint32_t code = nHeight * 2 + fCoinBase;
|
||||||
::Serialize(s, VARINT(code));
|
::Serialize(s, VARINT(code));
|
||||||
::Serialize(s, CTxOutCompressor(REF(out)));
|
::Serialize(s, CTxOutCompressor(REF(out)));
|
||||||
|
@ -73,7 +73,7 @@ public:
|
||||||
::Unserialize(s, REF(CTxOutCompressor(out)));
|
::Unserialize(s, REF(CTxOutCompressor(out)));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsPruned() const {
|
bool IsSpent() const {
|
||||||
return out.IsNull();
|
return out.IsNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,11 +147,11 @@ class CCoinsView
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
//! Retrieve the Coin (unspent transaction output) for a given outpoint.
|
//! Retrieve the Coin (unspent transaction output) for a given outpoint.
|
||||||
virtual bool GetCoins(const COutPoint &outpoint, Coin &coin) const;
|
virtual bool GetCoin(const COutPoint &outpoint, Coin &coin) const;
|
||||||
|
|
||||||
//! Just check whether we have data for a given outpoint.
|
//! Just check whether we have data for a given outpoint.
|
||||||
//! This may (but cannot always) return true for spent outputs.
|
//! This may (but cannot always) return true for spent outputs.
|
||||||
virtual bool HaveCoins(const COutPoint &outpoint) const;
|
virtual bool HaveCoin(const COutPoint &outpoint) const;
|
||||||
|
|
||||||
//! Retrieve the block hash whose state this CCoinsView currently represents
|
//! Retrieve the block hash whose state this CCoinsView currently represents
|
||||||
virtual uint256 GetBestBlock() const;
|
virtual uint256 GetBestBlock() const;
|
||||||
|
@ -179,8 +179,8 @@ protected:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CCoinsViewBacked(CCoinsView *viewIn);
|
CCoinsViewBacked(CCoinsView *viewIn);
|
||||||
bool GetCoins(const COutPoint &outpoint, Coin &coin) const override;
|
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override;
|
||||||
bool HaveCoins(const COutPoint &outpoint) const override;
|
bool HaveCoin(const COutPoint &outpoint) const override;
|
||||||
uint256 GetBestBlock() const override;
|
uint256 GetBestBlock() const override;
|
||||||
void SetBackend(CCoinsView &viewIn);
|
void SetBackend(CCoinsView &viewIn);
|
||||||
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override;
|
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override;
|
||||||
|
@ -207,22 +207,22 @@ public:
|
||||||
CCoinsViewCache(CCoinsView *baseIn);
|
CCoinsViewCache(CCoinsView *baseIn);
|
||||||
|
|
||||||
// Standard CCoinsView methods
|
// Standard CCoinsView methods
|
||||||
bool GetCoins(const COutPoint &outpoint, Coin &coin) const;
|
bool GetCoin(const COutPoint &outpoint, Coin &coin) const;
|
||||||
bool HaveCoins(const COutPoint &outpoint) const;
|
bool HaveCoin(const COutPoint &outpoint) const;
|
||||||
uint256 GetBestBlock() const;
|
uint256 GetBestBlock() const;
|
||||||
void SetBestBlock(const uint256 &hashBlock);
|
void SetBestBlock(const uint256 &hashBlock);
|
||||||
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock);
|
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if we have the given utxo already loaded in this cache.
|
* Check if we have the given utxo already loaded in this cache.
|
||||||
* The semantics are the same as HaveCoins(), but no calls to
|
* The semantics are the same as HaveCoin(), but no calls to
|
||||||
* the backing CCoinsView are made.
|
* the backing CCoinsView are made.
|
||||||
*/
|
*/
|
||||||
bool HaveCoinsInCache(const COutPoint &outpoint) const;
|
bool HaveCoinInCache(const COutPoint &outpoint) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a reference to Coin in the cache, or a pruned one if not found. This is
|
* Return a reference to Coin in the cache, or a pruned one if not found. This is
|
||||||
* more efficient than GetCoins. Modifications to other cache entries are
|
* more efficient than GetCoin. Modifications to other cache entries are
|
||||||
* allowed while accessing the returned pointer.
|
* allowed while accessing the returned pointer.
|
||||||
*/
|
*/
|
||||||
const Coin& AccessCoin(const COutPoint &output) const;
|
const Coin& AccessCoin(const COutPoint &output) const;
|
||||||
|
@ -273,7 +273,7 @@ public:
|
||||||
bool HaveInputs(const CTransaction& tx) const;
|
bool HaveInputs(const CTransaction& tx) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CCoinsMap::iterator FetchCoins(const COutPoint &outpoint) const;
|
CCoinsMap::iterator FetchCoin(const COutPoint &outpoint) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* By making the copy constructor private, we prevent accidentally using it when one intends to create a cache on top of a base cache.
|
* By making the copy constructor private, we prevent accidentally using it when one intends to create a cache on top of a base cache.
|
||||||
|
|
|
@ -214,7 +214,7 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
|
||||||
{
|
{
|
||||||
const COutPoint &prevout = tx.vin[i].prevout;
|
const COutPoint &prevout = tx.vin[i].prevout;
|
||||||
const Coin& coin = inputs.AccessCoin(prevout);
|
const Coin& coin = inputs.AccessCoin(prevout);
|
||||||
assert(!coin.IsPruned());
|
assert(!coin.IsSpent());
|
||||||
|
|
||||||
// If prev is coinbase, check that it's matured
|
// If prev is coinbase, check that it's matured
|
||||||
if (coin.IsCoinBase()) {
|
if (coin.IsCoinBase()) {
|
||||||
|
|
|
@ -146,9 +146,9 @@ class CCoinsViewErrorCatcher : public CCoinsViewBacked
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CCoinsViewErrorCatcher(CCoinsView* view) : CCoinsViewBacked(view) {}
|
CCoinsViewErrorCatcher(CCoinsView* view) : CCoinsViewBacked(view) {}
|
||||||
bool GetCoins(const COutPoint &outpoint, Coin &coin) const override {
|
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override {
|
||||||
try {
|
try {
|
||||||
return CCoinsViewBacked::GetCoins(outpoint, coin);
|
return CCoinsViewBacked::GetCoin(outpoint, coin);
|
||||||
} catch(const std::runtime_error& e) {
|
} catch(const std::runtime_error& e) {
|
||||||
uiInterface.ThreadSafeMessageBox(_("Error reading from database, shutting down."), "", CClientUIInterface::MSG_ERROR);
|
uiInterface.ThreadSafeMessageBox(_("Error reading from database, shutting down."), "", CClientUIInterface::MSG_ERROR);
|
||||||
LogPrintf("Error reading from database: %s\n", e.what());
|
LogPrintf("Error reading from database: %s\n", e.what());
|
||||||
|
|
|
@ -914,8 +914,8 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||||
return recentRejects->contains(inv.hash) ||
|
return recentRejects->contains(inv.hash) ||
|
||||||
mempool.exists(inv.hash) ||
|
mempool.exists(inv.hash) ||
|
||||||
mapOrphanTransactions.count(inv.hash) ||
|
mapOrphanTransactions.count(inv.hash) ||
|
||||||
pcoinsTip->HaveCoinsInCache(COutPoint(inv.hash, 0)) || // Best effort: only try output 0 and 1
|
pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 0)) || // Best effort: only try output 0 and 1
|
||||||
pcoinsTip->HaveCoinsInCache(COutPoint(inv.hash, 1));
|
pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 1));
|
||||||
}
|
}
|
||||||
case MSG_BLOCK:
|
case MSG_BLOCK:
|
||||||
case MSG_WITNESS_BLOCK:
|
case MSG_WITNESS_BLOCK:
|
||||||
|
|
|
@ -294,7 +294,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
|
||||||
COutPoint prevout = txin.prevout;
|
COutPoint prevout = txin.prevout;
|
||||||
|
|
||||||
Coin prev;
|
Coin prev;
|
||||||
if(pcoinsTip->GetCoins(prevout, prev))
|
if(pcoinsTip->GetCoin(prevout, prev))
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
strHTML += "<li>";
|
strHTML += "<li>";
|
||||||
|
|
|
@ -514,7 +514,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
|
||||||
for (size_t i = 0; i < vOutPoints.size(); i++) {
|
for (size_t i = 0; i < vOutPoints.size(); i++) {
|
||||||
bool hit = false;
|
bool hit = false;
|
||||||
Coin coin;
|
Coin coin;
|
||||||
if (view.GetCoins(vOutPoints[i], coin) && !mempool.isSpent(vOutPoints[i])) {
|
if (view.GetCoin(vOutPoints[i], coin) && !mempool.isSpent(vOutPoints[i])) {
|
||||||
hit = true;
|
hit = true;
|
||||||
outs.emplace_back(std::move(coin));
|
outs.emplace_back(std::move(coin));
|
||||||
}
|
}
|
||||||
|
|
|
@ -985,11 +985,11 @@ UniValue gettxout(const JSONRPCRequest& request)
|
||||||
if (fMempool) {
|
if (fMempool) {
|
||||||
LOCK(mempool.cs);
|
LOCK(mempool.cs);
|
||||||
CCoinsViewMemPool view(pcoinsTip, mempool);
|
CCoinsViewMemPool view(pcoinsTip, mempool);
|
||||||
if (!view.GetCoins(out, coin) || mempool.isSpent(out)) { // TODO: filtering spent coins should be done by the CCoinsViewMemPool
|
if (!view.GetCoin(out, coin) || mempool.isSpent(out)) { // TODO: filtering spent coins should be done by the CCoinsViewMemPool
|
||||||
return NullUniValue;
|
return NullUniValue;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!pcoinsTip->GetCoins(out, coin)) {
|
if (!pcoinsTip->GetCoin(out, coin)) {
|
||||||
return NullUniValue;
|
return NullUniValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -220,7 +220,7 @@ UniValue gettxoutproof(const JSONRPCRequest& request)
|
||||||
pblockindex = mapBlockIndex[hashBlock];
|
pblockindex = mapBlockIndex[hashBlock];
|
||||||
} else {
|
} else {
|
||||||
const Coin& coin = AccessByTxid(*pcoinsTip, oneTxid);
|
const Coin& coin = AccessByTxid(*pcoinsTip, oneTxid);
|
||||||
if (!coin.IsPruned() && coin.nHeight > 0 && coin.nHeight <= chainActive.Height()) {
|
if (!coin.IsSpent() && coin.nHeight > 0 && coin.nHeight <= chainActive.Height()) {
|
||||||
pblockindex = chainActive[coin.nHeight];
|
pblockindex = chainActive[coin.nHeight];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -696,7 +696,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
|
||||||
|
|
||||||
{
|
{
|
||||||
const Coin& coin = view.AccessCoin(out);
|
const Coin& coin = view.AccessCoin(out);
|
||||||
if (!coin.IsPruned() && coin.out.scriptPubKey != scriptPubKey) {
|
if (!coin.IsSpent() && coin.out.scriptPubKey != scriptPubKey) {
|
||||||
std::string err("Previous output scriptPubKey mismatch:\n");
|
std::string err("Previous output scriptPubKey mismatch:\n");
|
||||||
err = err + ScriptToAsmStr(coin.out.scriptPubKey) + "\nvs:\n"+
|
err = err + ScriptToAsmStr(coin.out.scriptPubKey) + "\nvs:\n"+
|
||||||
ScriptToAsmStr(scriptPubKey);
|
ScriptToAsmStr(scriptPubKey);
|
||||||
|
@ -768,7 +768,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
|
||||||
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
|
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
|
||||||
CTxIn& txin = mergedTx.vin[i];
|
CTxIn& txin = mergedTx.vin[i];
|
||||||
const Coin& coin = view.AccessCoin(txin.prevout);
|
const Coin& coin = view.AccessCoin(txin.prevout);
|
||||||
if (coin.IsPruned()) {
|
if (coin.IsSpent()) {
|
||||||
TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
|
TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -848,7 +848,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
|
||||||
bool fHaveChain = false;
|
bool fHaveChain = false;
|
||||||
for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) {
|
for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) {
|
||||||
const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o));
|
const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o));
|
||||||
fHaveChain = !existingCoin.IsPruned();
|
fHaveChain = !existingCoin.IsSpent();
|
||||||
}
|
}
|
||||||
bool fHaveMempool = mempool.exists(hashTx);
|
bool fHaveMempool = mempool.exists(hashTx);
|
||||||
if (!fHaveMempool && !fHaveChain) {
|
if (!fHaveMempool && !fHaveChain) {
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace
|
||||||
//! equality test
|
//! equality test
|
||||||
bool operator==(const Coin &a, const Coin &b) {
|
bool operator==(const Coin &a, const Coin &b) {
|
||||||
// Empty Coin objects are always equal.
|
// Empty Coin objects are always equal.
|
||||||
if (a.IsPruned() && b.IsPruned()) return true;
|
if (a.IsSpent() && b.IsSpent()) return true;
|
||||||
return a.fCoinBase == b.fCoinBase &&
|
return a.fCoinBase == b.fCoinBase &&
|
||||||
a.nHeight == b.nHeight &&
|
a.nHeight == b.nHeight &&
|
||||||
a.out == b.out;
|
a.out == b.out;
|
||||||
|
@ -37,24 +37,24 @@ class CCoinsViewTest : public CCoinsView
|
||||||
std::map<COutPoint, Coin> map_;
|
std::map<COutPoint, Coin> map_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool GetCoins(const COutPoint& outpoint, Coin& coin) const
|
bool GetCoin(const COutPoint& outpoint, Coin& coin) const
|
||||||
{
|
{
|
||||||
std::map<COutPoint, Coin>::const_iterator it = map_.find(outpoint);
|
std::map<COutPoint, Coin>::const_iterator it = map_.find(outpoint);
|
||||||
if (it == map_.end()) {
|
if (it == map_.end()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
coin = it->second;
|
coin = it->second;
|
||||||
if (coin.IsPruned() && insecure_rand() % 2 == 0) {
|
if (coin.IsSpent() && insecure_rand() % 2 == 0) {
|
||||||
// Randomly return false in case of an empty entry.
|
// Randomly return false in case of an empty entry.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HaveCoins(const COutPoint& outpoint) const
|
bool HaveCoin(const COutPoint& outpoint) const
|
||||||
{
|
{
|
||||||
Coin coin;
|
Coin coin;
|
||||||
return GetCoins(outpoint, coin);
|
return GetCoin(outpoint, coin);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 GetBestBlock() const { return hashBestBlock_; }
|
uint256 GetBestBlock() const { return hashBestBlock_; }
|
||||||
|
@ -65,7 +65,7 @@ public:
|
||||||
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
|
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
|
||||||
// Same optimization used in CCoinsViewDB is to only write dirty entries.
|
// Same optimization used in CCoinsViewDB is to only write dirty entries.
|
||||||
map_[it->first] = it->second.coin;
|
map_[it->first] = it->second.coin;
|
||||||
if (it->second.coin.IsPruned() && insecure_rand() % 3 == 0) {
|
if (it->second.coin.IsSpent() && insecure_rand() % 3 == 0) {
|
||||||
// Randomly delete empty entries on write.
|
// Randomly delete empty entries on write.
|
||||||
map_.erase(it->first);
|
map_.erase(it->first);
|
||||||
}
|
}
|
||||||
|
@ -151,20 +151,20 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
|
||||||
const Coin& entry = (insecure_rand() % 500 == 0) ? AccessByTxid(*stack.back(), txid) : stack.back()->AccessCoin(COutPoint(txid, 0));
|
const Coin& entry = (insecure_rand() % 500 == 0) ? AccessByTxid(*stack.back(), txid) : stack.back()->AccessCoin(COutPoint(txid, 0));
|
||||||
BOOST_CHECK(coin == entry);
|
BOOST_CHECK(coin == entry);
|
||||||
|
|
||||||
if (insecure_rand() % 5 == 0 || coin.IsPruned()) {
|
if (insecure_rand() % 5 == 0 || coin.IsSpent()) {
|
||||||
Coin newcoin;
|
Coin newcoin;
|
||||||
newcoin.out.nValue = insecure_rand();
|
newcoin.out.nValue = insecure_rand();
|
||||||
newcoin.nHeight = 1;
|
newcoin.nHeight = 1;
|
||||||
if (insecure_rand() % 16 == 0 && coin.IsPruned()) {
|
if (insecure_rand() % 16 == 0 && coin.IsSpent()) {
|
||||||
newcoin.out.scriptPubKey.assign(1 + (insecure_rand() & 0x3F), OP_RETURN);
|
newcoin.out.scriptPubKey.assign(1 + (insecure_rand() & 0x3F), OP_RETURN);
|
||||||
BOOST_CHECK(newcoin.out.scriptPubKey.IsUnspendable());
|
BOOST_CHECK(newcoin.out.scriptPubKey.IsUnspendable());
|
||||||
added_an_unspendable_entry = true;
|
added_an_unspendable_entry = true;
|
||||||
} else {
|
} else {
|
||||||
newcoin.out.scriptPubKey.assign(insecure_rand() & 0x3F, 0); // Random sizes so we can test memory usage accounting
|
newcoin.out.scriptPubKey.assign(insecure_rand() & 0x3F, 0); // Random sizes so we can test memory usage accounting
|
||||||
(coin.IsPruned() ? added_an_entry : updated_an_entry) = true;
|
(coin.IsSpent() ? added_an_entry : updated_an_entry) = true;
|
||||||
coin = newcoin;
|
coin = newcoin;
|
||||||
}
|
}
|
||||||
stack.back()->AddCoin(COutPoint(txid, 0), std::move(newcoin), !coin.IsPruned() || insecure_rand() & 1);
|
stack.back()->AddCoin(COutPoint(txid, 0), std::move(newcoin), !coin.IsSpent() || insecure_rand() & 1);
|
||||||
} else {
|
} else {
|
||||||
removed_an_entry = true;
|
removed_an_entry = true;
|
||||||
coin.Clear();
|
coin.Clear();
|
||||||
|
@ -177,20 +177,20 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
|
||||||
COutPoint out(txids[insecure_rand() % txids.size()], 0);
|
COutPoint out(txids[insecure_rand() % txids.size()], 0);
|
||||||
int cacheid = insecure_rand() % stack.size();
|
int cacheid = insecure_rand() % stack.size();
|
||||||
stack[cacheid]->Uncache(out);
|
stack[cacheid]->Uncache(out);
|
||||||
uncached_an_entry |= !stack[cacheid]->HaveCoinsInCache(out);
|
uncached_an_entry |= !stack[cacheid]->HaveCoinInCache(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Once every 1000 iterations and at the end, verify the full cache.
|
// Once every 1000 iterations and at the end, verify the full cache.
|
||||||
if (insecure_rand() % 1000 == 1 || i == NUM_SIMULATION_ITERATIONS - 1) {
|
if (insecure_rand() % 1000 == 1 || i == NUM_SIMULATION_ITERATIONS - 1) {
|
||||||
for (auto it = result.begin(); it != result.end(); it++) {
|
for (auto it = result.begin(); it != result.end(); it++) {
|
||||||
bool have = stack.back()->HaveCoins(it->first);
|
bool have = stack.back()->HaveCoin(it->first);
|
||||||
const Coin& coin = stack.back()->AccessCoin(it->first);
|
const Coin& coin = stack.back()->AccessCoin(it->first);
|
||||||
BOOST_CHECK(have == !coin.IsPruned());
|
BOOST_CHECK(have == !coin.IsSpent());
|
||||||
BOOST_CHECK(coin == it->second);
|
BOOST_CHECK(coin == it->second);
|
||||||
if (coin.IsPruned()) {
|
if (coin.IsSpent()) {
|
||||||
missed_an_entry = true;
|
missed_an_entry = true;
|
||||||
} else {
|
} else {
|
||||||
BOOST_CHECK(stack.back()->HaveCoinsInCache(it->first));
|
BOOST_CHECK(stack.back()->HaveCoinInCache(it->first));
|
||||||
found_an_entry = true;
|
found_an_entry = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -281,9 +281,9 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
|
||||||
stack.push_back(new CCoinsViewCacheTest(&base)); // Start with one cache.
|
stack.push_back(new CCoinsViewCacheTest(&base)); // Start with one cache.
|
||||||
|
|
||||||
// Track the txids we've used in various sets
|
// Track the txids we've used in various sets
|
||||||
std::set<COutPoint> coinbaseids;
|
std::set<COutPoint> coinbase_coins;
|
||||||
std::set<COutPoint> disconnectedids;
|
std::set<COutPoint> disconnected_coins;
|
||||||
std::set<COutPoint> duplicateids;
|
std::set<COutPoint> duplicate_coins;
|
||||||
std::set<COutPoint> utxoset;
|
std::set<COutPoint> utxoset;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) {
|
for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) {
|
||||||
|
@ -297,22 +297,22 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
|
||||||
tx.vout[0].nValue = i; //Keep txs unique unless intended to duplicate
|
tx.vout[0].nValue = i; //Keep txs unique unless intended to duplicate
|
||||||
tx.vout[0].scriptPubKey.assign(insecure_rand() & 0x3F, 0); // Random sizes so we can test memory usage accounting
|
tx.vout[0].scriptPubKey.assign(insecure_rand() & 0x3F, 0); // Random sizes so we can test memory usage accounting
|
||||||
unsigned int height = insecure_rand();
|
unsigned int height = insecure_rand();
|
||||||
Coin oldcoins;
|
Coin old_coin;
|
||||||
|
|
||||||
// 2/20 times create a new coinbase
|
// 2/20 times create a new coinbase
|
||||||
if (randiter % 20 < 2 || coinbaseids.size() < 10) {
|
if (randiter % 20 < 2 || coinbase_coins.size() < 10) {
|
||||||
// 1/10 of those times create a duplicate coinbase
|
// 1/10 of those times create a duplicate coinbase
|
||||||
if (insecure_rand() % 10 == 0 && coinbaseids.size()) {
|
if (insecure_rand() % 10 == 0 && coinbase_coins.size()) {
|
||||||
auto utxod = FindRandomFrom(coinbaseids);
|
auto utxod = FindRandomFrom(coinbase_coins);
|
||||||
// Reuse the exact same coinbase
|
// Reuse the exact same coinbase
|
||||||
tx = std::get<0>(utxod->second);
|
tx = std::get<0>(utxod->second);
|
||||||
// shouldn't be available for reconnection if its been duplicated
|
// shouldn't be available for reconnection if its been duplicated
|
||||||
disconnectedids.erase(utxod->first);
|
disconnected_coins.erase(utxod->first);
|
||||||
|
|
||||||
duplicateids.insert(utxod->first);
|
duplicate_coins.insert(utxod->first);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
coinbaseids.insert(COutPoint(tx.GetHash(), 0));
|
coinbase_coins.insert(COutPoint(tx.GetHash(), 0));
|
||||||
}
|
}
|
||||||
assert(CTransaction(tx).IsCoinBase());
|
assert(CTransaction(tx).IsCoinBase());
|
||||||
}
|
}
|
||||||
|
@ -322,21 +322,21 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
|
||||||
|
|
||||||
COutPoint prevout;
|
COutPoint prevout;
|
||||||
// 1/20 times reconnect a previously disconnected tx
|
// 1/20 times reconnect a previously disconnected tx
|
||||||
if (randiter % 20 == 2 && disconnectedids.size()) {
|
if (randiter % 20 == 2 && disconnected_coins.size()) {
|
||||||
auto utxod = FindRandomFrom(disconnectedids);
|
auto utxod = FindRandomFrom(disconnected_coins);
|
||||||
tx = std::get<0>(utxod->second);
|
tx = std::get<0>(utxod->second);
|
||||||
prevout = tx.vin[0].prevout;
|
prevout = tx.vin[0].prevout;
|
||||||
if (!CTransaction(tx).IsCoinBase() && !utxoset.count(prevout)) {
|
if (!CTransaction(tx).IsCoinBase() && !utxoset.count(prevout)) {
|
||||||
disconnectedids.erase(utxod->first);
|
disconnected_coins.erase(utxod->first);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this tx is already IN the UTXO, then it must be a coinbase, and it must be a duplicate
|
// If this tx is already IN the UTXO, then it must be a coinbase, and it must be a duplicate
|
||||||
if (utxoset.count(utxod->first)) {
|
if (utxoset.count(utxod->first)) {
|
||||||
assert(CTransaction(tx).IsCoinBase());
|
assert(CTransaction(tx).IsCoinBase());
|
||||||
assert(duplicateids.count(utxod->first));
|
assert(duplicate_coins.count(utxod->first));
|
||||||
}
|
}
|
||||||
disconnectedids.erase(utxod->first);
|
disconnected_coins.erase(utxod->first);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 16/20 times create a regular tx
|
// 16/20 times create a regular tx
|
||||||
|
@ -349,7 +349,7 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
|
||||||
assert(!CTransaction(tx).IsCoinBase());
|
assert(!CTransaction(tx).IsCoinBase());
|
||||||
}
|
}
|
||||||
// In this simple test coins only have two states, spent or unspent, save the unspent state to restore
|
// In this simple test coins only have two states, spent or unspent, save the unspent state to restore
|
||||||
oldcoins = result[prevout];
|
old_coin = result[prevout];
|
||||||
// Update the expected result of prevouthash to know these coins are spent
|
// Update the expected result of prevouthash to know these coins are spent
|
||||||
result[prevout].Clear();
|
result[prevout].Clear();
|
||||||
|
|
||||||
|
@ -357,7 +357,7 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
|
||||||
|
|
||||||
// The test is designed to ensure spending a duplicate coinbase will work properly
|
// The test is designed to ensure spending a duplicate coinbase will work properly
|
||||||
// if that ever happens and not resurrect the previously overwritten coinbase
|
// if that ever happens and not resurrect the previously overwritten coinbase
|
||||||
if (duplicateids.count(prevout)) {
|
if (duplicate_coins.count(prevout)) {
|
||||||
spent_a_duplicate_coinbase = true;
|
spent_a_duplicate_coinbase = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,21 +375,21 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
|
||||||
utxoset.insert(outpoint);
|
utxoset.insert(outpoint);
|
||||||
|
|
||||||
// Track this tx and undo info to use later
|
// Track this tx and undo info to use later
|
||||||
utxoData.emplace(outpoint, std::make_tuple(tx,undo,oldcoins));
|
utxoData.emplace(outpoint, std::make_tuple(tx,undo,old_coin));
|
||||||
} else if (utxoset.size()) {
|
} else if (utxoset.size()) {
|
||||||
//1/20 times undo a previous transaction
|
//1/20 times undo a previous transaction
|
||||||
auto utxod = FindRandomFrom(utxoset);
|
auto utxod = FindRandomFrom(utxoset);
|
||||||
|
|
||||||
CTransaction &tx = std::get<0>(utxod->second);
|
CTransaction &tx = std::get<0>(utxod->second);
|
||||||
CTxUndo &undo = std::get<1>(utxod->second);
|
CTxUndo &undo = std::get<1>(utxod->second);
|
||||||
Coin &origcoins = std::get<2>(utxod->second);
|
Coin &orig_coin = std::get<2>(utxod->second);
|
||||||
|
|
||||||
// Update the expected result
|
// Update the expected result
|
||||||
// Remove new outputs
|
// Remove new outputs
|
||||||
result[utxod->first].Clear();
|
result[utxod->first].Clear();
|
||||||
// If not coinbase restore prevout
|
// If not coinbase restore prevout
|
||||||
if (!tx.IsCoinBase()) {
|
if (!tx.IsCoinBase()) {
|
||||||
result[tx.vin[0].prevout] = origcoins;
|
result[tx.vin[0].prevout] = orig_coin;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disconnect the tx from the current UTXO
|
// Disconnect the tx from the current UTXO
|
||||||
|
@ -403,7 +403,7 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
|
||||||
ApplyTxInUndo(std::move(coin), *(stack.back()), out);
|
ApplyTxInUndo(std::move(coin), *(stack.back()), out);
|
||||||
}
|
}
|
||||||
// Store as a candidate for reconnection
|
// Store as a candidate for reconnection
|
||||||
disconnectedids.insert(utxod->first);
|
disconnected_coins.insert(utxod->first);
|
||||||
|
|
||||||
// Update the utxoset
|
// Update the utxoset
|
||||||
utxoset.erase(utxod->first);
|
utxoset.erase(utxod->first);
|
||||||
|
@ -414,9 +414,9 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
|
||||||
// Once every 1000 iterations and at the end, verify the full cache.
|
// Once every 1000 iterations and at the end, verify the full cache.
|
||||||
if (insecure_rand() % 1000 == 1 || i == NUM_SIMULATION_ITERATIONS - 1) {
|
if (insecure_rand() % 1000 == 1 || i == NUM_SIMULATION_ITERATIONS - 1) {
|
||||||
for (auto it = result.begin(); it != result.end(); it++) {
|
for (auto it = result.begin(); it != result.end(); it++) {
|
||||||
bool have = stack.back()->HaveCoins(it->first);
|
bool have = stack.back()->HaveCoin(it->first);
|
||||||
const Coin& coin = stack.back()->AccessCoin(it->first);
|
const Coin& coin = stack.back()->AccessCoin(it->first);
|
||||||
BOOST_CHECK(have == !coin.IsPruned());
|
BOOST_CHECK(have == !coin.IsSpent());
|
||||||
BOOST_CHECK(coin == it->second);
|
BOOST_CHECK(coin == it->second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -425,11 +425,11 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
|
||||||
if (utxoset.size() > 1 && insecure_rand() % 30) {
|
if (utxoset.size() > 1 && insecure_rand() % 30) {
|
||||||
stack[insecure_rand() % stack.size()]->Uncache(FindRandomFrom(utxoset)->first);
|
stack[insecure_rand() % stack.size()]->Uncache(FindRandomFrom(utxoset)->first);
|
||||||
}
|
}
|
||||||
if (disconnectedids.size() > 1 && insecure_rand() % 30) {
|
if (disconnected_coins.size() > 1 && insecure_rand() % 30) {
|
||||||
stack[insecure_rand() % stack.size()]->Uncache(FindRandomFrom(disconnectedids)->first);
|
stack[insecure_rand() % stack.size()]->Uncache(FindRandomFrom(disconnected_coins)->first);
|
||||||
}
|
}
|
||||||
if (duplicateids.size() > 1 && insecure_rand() % 30) {
|
if (duplicate_coins.size() > 1 && insecure_rand() % 30) {
|
||||||
stack[insecure_rand() % stack.size()]->Uncache(FindRandomFrom(duplicateids)->first);
|
stack[insecure_rand() % stack.size()]->Uncache(FindRandomFrom(duplicate_coins)->first);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (insecure_rand() % 100 == 0) {
|
if (insecure_rand() % 100 == 0) {
|
||||||
|
@ -537,11 +537,11 @@ void SetCoinsValue(CAmount value, Coin& coin)
|
||||||
{
|
{
|
||||||
assert(value != ABSENT);
|
assert(value != ABSENT);
|
||||||
coin.Clear();
|
coin.Clear();
|
||||||
assert(coin.IsPruned());
|
assert(coin.IsSpent());
|
||||||
if (value != PRUNED) {
|
if (value != PRUNED) {
|
||||||
coin.out.nValue = value;
|
coin.out.nValue = value;
|
||||||
coin.nHeight = 1;
|
coin.nHeight = 1;
|
||||||
assert(!coin.IsPruned());
|
assert(!coin.IsSpent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -567,7 +567,7 @@ void GetCoinsMapEntry(const CCoinsMap& map, CAmount& value, char& flags)
|
||||||
value = ABSENT;
|
value = ABSENT;
|
||||||
flags = NO_ENTRY;
|
flags = NO_ENTRY;
|
||||||
} else {
|
} else {
|
||||||
if (it->second.coin.IsPruned()) {
|
if (it->second.coin.IsSpent()) {
|
||||||
value = PRUNED;
|
value = PRUNED;
|
||||||
} else {
|
} else {
|
||||||
value = it->second.coin.out.nValue;
|
value = it->second.coin.out.nValue;
|
||||||
|
|
20
src/txdb.cpp
20
src/txdb.cpp
|
@ -27,10 +27,10 @@ static const char DB_LAST_BLOCK = 'l';
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct CoinsEntry {
|
struct CoinEntry {
|
||||||
COutPoint* outpoint;
|
COutPoint* outpoint;
|
||||||
char key;
|
char key;
|
||||||
CoinsEntry(const COutPoint* ptr) : outpoint(const_cast<COutPoint*>(ptr)), key(DB_COIN) {}
|
CoinEntry(const COutPoint* ptr) : outpoint(const_cast<COutPoint*>(ptr)), key(DB_COIN) {}
|
||||||
|
|
||||||
template<typename Stream>
|
template<typename Stream>
|
||||||
void Serialize(Stream &s) const {
|
void Serialize(Stream &s) const {
|
||||||
|
@ -53,12 +53,12 @@ CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(Get
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CCoinsViewDB::GetCoins(const COutPoint &outpoint, Coin &coin) const {
|
bool CCoinsViewDB::GetCoin(const COutPoint &outpoint, Coin &coin) const {
|
||||||
return db.Read(CoinsEntry(&outpoint), coin);
|
return db.Read(CoinEntry(&outpoint), coin);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CCoinsViewDB::HaveCoins(const COutPoint &outpoint) const {
|
bool CCoinsViewDB::HaveCoin(const COutPoint &outpoint) const {
|
||||||
return db.Exists(CoinsEntry(&outpoint));
|
return db.Exists(CoinEntry(&outpoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 CCoinsViewDB::GetBestBlock() const {
|
uint256 CCoinsViewDB::GetBestBlock() const {
|
||||||
|
@ -74,8 +74,8 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
|
||||||
size_t changed = 0;
|
size_t changed = 0;
|
||||||
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
|
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
|
||||||
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
|
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
|
||||||
CoinsEntry entry(&it->first);
|
CoinEntry entry(&it->first);
|
||||||
if (it->second.coin.IsPruned())
|
if (it->second.coin.IsSpent())
|
||||||
batch.Erase(entry);
|
batch.Erase(entry);
|
||||||
else
|
else
|
||||||
batch.Write(entry, it->second.coin);
|
batch.Write(entry, it->second.coin);
|
||||||
|
@ -130,7 +130,7 @@ CCoinsViewCursor *CCoinsViewDB::Cursor() const
|
||||||
i->pcursor->Seek(DB_COIN);
|
i->pcursor->Seek(DB_COIN);
|
||||||
// Cache key of first record
|
// Cache key of first record
|
||||||
if (i->pcursor->Valid()) {
|
if (i->pcursor->Valid()) {
|
||||||
CoinsEntry entry(&i->keyTmp.second);
|
CoinEntry entry(&i->keyTmp.second);
|
||||||
i->pcursor->GetKey(entry);
|
i->pcursor->GetKey(entry);
|
||||||
i->keyTmp.first = entry.key;
|
i->keyTmp.first = entry.key;
|
||||||
} else {
|
} else {
|
||||||
|
@ -167,7 +167,7 @@ bool CCoinsViewDBCursor::Valid() const
|
||||||
void CCoinsViewDBCursor::Next()
|
void CCoinsViewDBCursor::Next()
|
||||||
{
|
{
|
||||||
pcursor->Next();
|
pcursor->Next();
|
||||||
CoinsEntry entry(&keyTmp.second);
|
CoinEntry entry(&keyTmp.second);
|
||||||
if (!pcursor->Valid() || !pcursor->GetKey(entry)) {
|
if (!pcursor->Valid() || !pcursor->GetKey(entry)) {
|
||||||
keyTmp.first = 0; // Invalidate cached key after last record so that Valid() and GetKey() return false
|
keyTmp.first = 0; // Invalidate cached key after last record so that Valid() and GetKey() return false
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -71,8 +71,8 @@ protected:
|
||||||
public:
|
public:
|
||||||
CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false);
|
CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false);
|
||||||
|
|
||||||
bool GetCoins(const COutPoint &outpoint, Coin &coin) const override;
|
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override;
|
||||||
bool HaveCoins(const COutPoint &outpoint) const override;
|
bool HaveCoin(const COutPoint &outpoint) const override;
|
||||||
uint256 GetBestBlock() const override;
|
uint256 GetBestBlock() const override;
|
||||||
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override;
|
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override;
|
||||||
CCoinsViewCursor *Cursor() const override;
|
CCoinsViewCursor *Cursor() const override;
|
||||||
|
|
|
@ -525,8 +525,8 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem
|
||||||
if (it2 != mapTx.end())
|
if (it2 != mapTx.end())
|
||||||
continue;
|
continue;
|
||||||
const Coin &coin = pcoins->AccessCoin(txin.prevout);
|
const Coin &coin = pcoins->AccessCoin(txin.prevout);
|
||||||
if (nCheckFrequency != 0) assert(!coin.IsPruned());
|
if (nCheckFrequency != 0) assert(!coin.IsSpent());
|
||||||
if (coin.IsPruned() || (coin.IsCoinBase() && ((signed long)nMemPoolHeight) - coin.nHeight < COINBASE_MATURITY)) {
|
if (coin.IsSpent() || (coin.IsCoinBase() && ((signed long)nMemPoolHeight) - coin.nHeight < COINBASE_MATURITY)) {
|
||||||
txToRemove.insert(it);
|
txToRemove.insert(it);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -654,7 +654,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
||||||
parentSigOpCost += it2->GetSigOpCost();
|
parentSigOpCost += it2->GetSigOpCost();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assert(pcoins->HaveCoins(txin.prevout));
|
assert(pcoins->HaveCoin(txin.prevout));
|
||||||
}
|
}
|
||||||
// Check whether its inputs are marked in mapNextTx.
|
// Check whether its inputs are marked in mapNextTx.
|
||||||
auto it3 = mapNextTx.find(txin.prevout);
|
auto it3 = mapNextTx.find(txin.prevout);
|
||||||
|
@ -890,7 +890,7 @@ bool CTxMemPool::HasNoInputsOf(const CTransaction &tx) const
|
||||||
|
|
||||||
CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { }
|
CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { }
|
||||||
|
|
||||||
bool CCoinsViewMemPool::GetCoins(const COutPoint &outpoint, Coin &coin) const {
|
bool CCoinsViewMemPool::GetCoin(const COutPoint &outpoint, Coin &coin) const {
|
||||||
// If an entry in the mempool exists, always return that one, as it's guaranteed to never
|
// 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)
|
// 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.
|
// transactions. First checking the underlying cache risks returning a pruned entry instead.
|
||||||
|
@ -903,11 +903,11 @@ bool CCoinsViewMemPool::GetCoins(const COutPoint &outpoint, Coin &coin) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (base->GetCoins(outpoint, coin) && !coin.IsPruned());
|
return (base->GetCoin(outpoint, coin) && !coin.IsSpent());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CCoinsViewMemPool::HaveCoins(const COutPoint &outpoint) const {
|
bool CCoinsViewMemPool::HaveCoin(const COutPoint &outpoint) const {
|
||||||
return mempool.exists(outpoint) || base->HaveCoins(outpoint);
|
return mempool.exists(outpoint) || base->HaveCoin(outpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t CTxMemPool::DynamicMemoryUsage() const {
|
size_t CTxMemPool::DynamicMemoryUsage() const {
|
||||||
|
|
|
@ -679,8 +679,8 @@ protected:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn);
|
CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn);
|
||||||
bool GetCoins(const COutPoint &outpoint, Coin &coin) const;
|
bool GetCoin(const COutPoint &outpoint, Coin &coin) const;
|
||||||
bool HaveCoins(const COutPoint &outpoint) const;
|
bool HaveCoin(const COutPoint &outpoint) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // BITCOIN_TXMEMPOOL_H
|
#endif // BITCOIN_TXMEMPOOL_H
|
||||||
|
|
|
@ -269,7 +269,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool
|
||||||
for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) {
|
for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) {
|
||||||
const CTxIn& txin = tx.vin[txinIndex];
|
const CTxIn& txin = tx.vin[txinIndex];
|
||||||
Coin coin;
|
Coin coin;
|
||||||
if (!viewMemPool.GetCoins(txin.prevout, coin)) {
|
if (!viewMemPool.GetCoin(txin.prevout, coin)) {
|
||||||
return error("%s: Missing input", __func__);
|
return error("%s: Missing input", __func__);
|
||||||
}
|
}
|
||||||
if (coin.nHeight == MEMPOOL_HEIGHT) {
|
if (coin.nHeight == MEMPOOL_HEIGHT) {
|
||||||
|
@ -344,7 +344,7 @@ static bool IsCurrentForFeeEstimation()
|
||||||
|
|
||||||
bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool fLimitFree,
|
bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool fLimitFree,
|
||||||
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
|
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
|
||||||
bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, std::vector<COutPoint>& vHashTxnToUncache)
|
bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache)
|
||||||
{
|
{
|
||||||
const CTransaction& tx = *ptx;
|
const CTransaction& tx = *ptx;
|
||||||
const uint256 hash = tx.GetHash();
|
const uint256 hash = tx.GetHash();
|
||||||
|
@ -439,10 +439,10 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
||||||
// do we already have it?
|
// do we already have it?
|
||||||
for (size_t out = 0; out < tx.vout.size(); out++) {
|
for (size_t out = 0; out < tx.vout.size(); out++) {
|
||||||
COutPoint outpoint(hash, out);
|
COutPoint outpoint(hash, out);
|
||||||
bool fHadTxInCache = pcoinsTip->HaveCoinsInCache(outpoint);
|
bool had_coin_in_cache = pcoinsTip->HaveCoinInCache(outpoint);
|
||||||
if (view.HaveCoins(outpoint)) {
|
if (view.HaveCoin(outpoint)) {
|
||||||
if (!fHadTxInCache) {
|
if (!had_coin_in_cache) {
|
||||||
vHashTxnToUncache.push_back(outpoint);
|
coins_to_uncache.push_back(outpoint);
|
||||||
}
|
}
|
||||||
return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-known");
|
return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-known");
|
||||||
}
|
}
|
||||||
|
@ -450,10 +450,10 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
||||||
|
|
||||||
// do all inputs exist?
|
// do all inputs exist?
|
||||||
BOOST_FOREACH(const CTxIn txin, tx.vin) {
|
BOOST_FOREACH(const CTxIn txin, tx.vin) {
|
||||||
if (!pcoinsTip->HaveCoinsInCache(txin.prevout)) {
|
if (!pcoinsTip->HaveCoinInCache(txin.prevout)) {
|
||||||
vHashTxnToUncache.push_back(txin.prevout);
|
coins_to_uncache.push_back(txin.prevout);
|
||||||
}
|
}
|
||||||
if (!view.HaveCoins(txin.prevout)) {
|
if (!view.HaveCoin(txin.prevout)) {
|
||||||
if (pfMissingInputs) {
|
if (pfMissingInputs) {
|
||||||
*pfMissingInputs = true;
|
*pfMissingInputs = true;
|
||||||
}
|
}
|
||||||
|
@ -763,10 +763,10 @@ bool AcceptToMemoryPoolWithTime(CTxMemPool& pool, CValidationState &state, const
|
||||||
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
|
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
|
||||||
bool fOverrideMempoolLimit, const CAmount nAbsurdFee)
|
bool fOverrideMempoolLimit, const CAmount nAbsurdFee)
|
||||||
{
|
{
|
||||||
std::vector<COutPoint> vHashTxToUncache;
|
std::vector<COutPoint> coins_to_uncache;
|
||||||
bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, nAcceptTime, plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache);
|
bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, nAcceptTime, plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee, coins_to_uncache);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
BOOST_FOREACH(const COutPoint& hashTx, vHashTxToUncache)
|
BOOST_FOREACH(const COutPoint& hashTx, coins_to_uncache)
|
||||||
pcoinsTip->Uncache(hashTx);
|
pcoinsTip->Uncache(hashTx);
|
||||||
}
|
}
|
||||||
// After we've (potentially) uncached entries, ensure our coins cache is still within its size limits
|
// After we've (potentially) uncached entries, ensure our coins cache is still within its size limits
|
||||||
|
@ -819,7 +819,7 @@ bool GetTransaction(const uint256 &hash, CTransactionRef &txOut, const Consensus
|
||||||
|
|
||||||
if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
|
if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
|
||||||
const Coin& coin = AccessByTxid(*pcoinsTip, hash);
|
const Coin& coin = AccessByTxid(*pcoinsTip, hash);
|
||||||
if (!coin.IsPruned()) pindexSlow = chainActive[coin.nHeight];
|
if (!coin.IsSpent()) pindexSlow = chainActive[coin.nHeight];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pindexSlow) {
|
if (pindexSlow) {
|
||||||
|
@ -1117,7 +1117,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
||||||
const COutPoint &prevout = tx.vin[i].prevout;
|
const COutPoint &prevout = tx.vin[i].prevout;
|
||||||
const Coin& coin = inputs.AccessCoin(prevout);
|
const Coin& coin = inputs.AccessCoin(prevout);
|
||||||
assert(!coin.IsPruned());
|
assert(!coin.IsSpent());
|
||||||
|
|
||||||
// We very carefully only pass in things to CScriptCheck which
|
// We very carefully only pass in things to CScriptCheck which
|
||||||
// are clearly committed to by tx' witness hash. This provides
|
// are clearly committed to by tx' witness hash. This provides
|
||||||
|
@ -1254,14 +1254,14 @@ int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out)
|
||||||
{
|
{
|
||||||
bool fClean = true;
|
bool fClean = true;
|
||||||
|
|
||||||
if (view.HaveCoins(out)) fClean = false; // overwriting transaction output
|
if (view.HaveCoin(out)) fClean = false; // overwriting transaction output
|
||||||
|
|
||||||
if (undo.nHeight == 0) {
|
if (undo.nHeight == 0) {
|
||||||
// Missing undo metadata (height and coinbase). Older versions included this
|
// Missing undo metadata (height and coinbase). Older versions included this
|
||||||
// information only in undo records for the last spend of a transactions'
|
// information only in undo records for the last spend of a transactions'
|
||||||
// outputs. This implies that it must be present for some other output of the same tx.
|
// outputs. This implies that it must be present for some other output of the same tx.
|
||||||
const Coin& alternate = AccessByTxid(view, out.hash);
|
const Coin& alternate = AccessByTxid(view, out.hash);
|
||||||
if (!alternate.IsPruned()) {
|
if (!alternate.IsSpent()) {
|
||||||
undo.nHeight = alternate.nHeight;
|
undo.nHeight = alternate.nHeight;
|
||||||
undo.fCoinBase = alternate.fCoinBase;
|
undo.fCoinBase = alternate.fCoinBase;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1510,7 +1510,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
|
||||||
if (fEnforceBIP30) {
|
if (fEnforceBIP30) {
|
||||||
for (const auto& tx : block.vtx) {
|
for (const auto& tx : block.vtx) {
|
||||||
for (size_t o = 0; o < tx->vout.size(); o++) {
|
for (size_t o = 0; o < tx->vout.size(); o++) {
|
||||||
if (view.HaveCoins(COutPoint(tx->GetHash(), o))) {
|
if (view.HaveCoin(COutPoint(tx->GetHash(), o))) {
|
||||||
return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"),
|
return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"),
|
||||||
REJECT_INVALID, "bad-txns-BIP30");
|
REJECT_INVALID, "bad-txns-BIP30");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue