diff options
author | Pieter Wuille <pieter.wuille@gmail.com> | 2017-04-25 11:29:29 -0700 |
---|---|---|
committer | Pieter Wuille <pieter.wuille@gmail.com> | 2017-06-01 11:56:06 -0700 |
commit | 000391132608343c66488d62625c714814959bc9 (patch) | |
tree | 0d50712acb21403a32a3a013e54bdedc2b28aeb8 /src/coins.cpp | |
parent | bd83111a0fcfdb97204a0180bcf861d3b53bb6c2 (diff) |
Introduce new per-txout CCoinsViewCache functions
The new functions are:
* CCoinsViewCache::AddCoin: Add a single COutPoint/Coin pair.
* CCoinsViewCache::SpendCoin: Remove a single COutPoint.
* AddCoins: utility function that invokes CCoinsViewCache::AddCoin for
each output in a CTransaction.
* AccessByTxid: utility function that searches for any output with
a given txid.
* CCoinsViewCache::AccessCoin: retrieve the Coin for a COutPoint.
* CCoinsViewCache::HaveCoins: check whether a non-empty Coin exists
for a given COutPoint.
The AddCoin and SpendCoin methods will eventually replace ModifyCoins
and ModifyNewCoins, AddCoins will replace CCoins::FromTx, and the new
AccessCoins and HaveCoins functions will replace their per-txid
counterparts.
Note that AccessCoin for now returns a copy of the Coin object. In a
later commit it will be change to returning a const reference (which
keeps working in all call sites).
Diffstat (limited to 'src/coins.cpp')
-rw-r--r-- | src/coins.cpp | 85 |
1 files changed, 84 insertions, 1 deletions
diff --git a/src/coins.cpp b/src/coins.cpp index 02f424fad6..3ac46d0806 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -4,6 +4,7 @@ #include "coins.h" +#include "consensus/consensus.h" #include "memusage.h" #include "random.h" @@ -70,7 +71,7 @@ size_t CCoinsViewCache::DynamicMemoryUsage() const { return memusage::DynamicUsage(cacheCoins) + cachedCoinsUsage; } -CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const { +CCoinsMap::iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const { CCoinsMap::iterator it = cacheCoins.find(txid); if (it != cacheCoins.end()) return it; @@ -153,6 +154,58 @@ CCoinsModifier CCoinsViewCache::ModifyNewCoins(const uint256 &txid, bool coinbas return CCoinsModifier(*this, ret.first, 0); } +void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possible_overwrite) { + assert(!coin.IsPruned()); + if (coin.out.scriptPubKey.IsUnspendable()) return; + CCoinsMap::iterator it; + bool inserted; + std::tie(it, inserted) = cacheCoins.emplace(std::piecewise_construct, std::forward_as_tuple(outpoint.hash), std::tuple<>()); + bool fresh = false; + if (!inserted) { + cachedCoinsUsage -= it->second.coins.DynamicMemoryUsage(); + } + if (!possible_overwrite) { + if (it->second.coins.IsAvailable(outpoint.n)) { + throw std::logic_error("Adding new coin that replaces non-pruned entry"); + } + fresh = it->second.coins.IsPruned() && !(it->second.flags & CCoinsCacheEntry::DIRTY); + } + if (it->second.coins.vout.size() <= outpoint.n) { + it->second.coins.vout.resize(outpoint.n + 1); + } + it->second.coins.vout[outpoint.n] = std::move(coin.out); + it->second.coins.nHeight = coin.nHeight; + it->second.coins.fCoinBase = coin.fCoinBase; + it->second.flags |= CCoinsCacheEntry::DIRTY | (fresh ? CCoinsCacheEntry::FRESH : 0); + cachedCoinsUsage += it->second.coins.DynamicMemoryUsage(); +} + +void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight) { + bool fCoinbase = tx.IsCoinBase(); + const uint256& txid = tx.GetHash(); + for (size_t i = 0; i < tx.vout.size(); ++i) { + // Pass fCoinbase as the possible_overwrite flag to AddCoin, in order to correctly + // deal with the pre-BIP30 occurrances of duplicate coinbase transactions. + cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase), fCoinbase); + } +} + +void CCoinsViewCache::SpendCoin(const COutPoint &outpoint, Coin* moveout) { + CCoinsMap::iterator it = FetchCoins(outpoint.hash); + if (it == cacheCoins.end()) return; + cachedCoinsUsage -= it->second.coins.DynamicMemoryUsage(); + if (moveout && it->second.coins.IsAvailable(outpoint.n)) { + *moveout = Coin(it->second.coins.vout[outpoint.n], it->second.coins.nHeight, it->second.coins.fCoinBase); + } + it->second.coins.Spend(outpoint.n); // Ignore return value: SpendCoin has no effect if no UTXO found. + if (it->second.coins.IsPruned() && it->second.flags & CCoinsCacheEntry::FRESH) { + cacheCoins.erase(it); + } else { + cachedCoinsUsage += it->second.coins.DynamicMemoryUsage(); + it->second.flags |= CCoinsCacheEntry::DIRTY; + } +} + const CCoins* CCoinsViewCache::AccessCoins(const uint256 &txid) const { CCoinsMap::const_iterator it = FetchCoins(txid); if (it == cacheCoins.end()) { @@ -162,6 +215,18 @@ const CCoins* CCoinsViewCache::AccessCoins(const uint256 &txid) const { } } +static const Coin coinEmpty; + +const Coin CCoinsViewCache::AccessCoin(const COutPoint &outpoint) const { + CCoinsMap::const_iterator it = FetchCoins(outpoint.hash); + if (it == cacheCoins.end() || !it->second.coins.IsAvailable(outpoint.n)) { + return coinEmpty; + } else { + return Coin(it->second.coins.vout[outpoint.n], it->second.coins.nHeight, it->second.coins.fCoinBase); + } +} + + bool CCoinsViewCache::HaveCoins(const uint256 &txid) const { CCoinsMap::const_iterator it = FetchCoins(txid); // We're using vtx.empty() instead of IsPruned here for performance reasons, @@ -171,6 +236,11 @@ bool CCoinsViewCache::HaveCoins(const uint256 &txid) const { return (it != cacheCoins.end() && !it->second.coins.vout.empty()); } +bool CCoinsViewCache::HaveCoins(const COutPoint &outpoint) const { + CCoinsMap::const_iterator it = FetchCoins(outpoint.hash); + return (it != cacheCoins.end() && it->second.coins.IsAvailable(outpoint.n)); +} + bool CCoinsViewCache::HaveCoinsInCache(const uint256 &txid) const { CCoinsMap::const_iterator it = cacheCoins.find(txid); return it != cacheCoins.end(); @@ -318,3 +388,16 @@ CCoinsModifier::~CCoinsModifier() CCoinsViewCursor::~CCoinsViewCursor() { } + +static const size_t MAX_OUTPUTS_PER_BLOCK = MAX_BLOCK_BASE_SIZE / ::GetSerializeSize(CTxOut(), SER_NETWORK, PROTOCOL_VERSION); // TODO: merge with similar definition in undo.h. + +const Coin AccessByTxid(const CCoinsViewCache& view, const uint256& txid) +{ + COutPoint iter(txid, 0); + while (iter.n < MAX_OUTPUTS_PER_BLOCK) { + const Coin& alternate = view.AccessCoin(iter); + if (!alternate.IsPruned()) return alternate; + ++iter.n; + } + return coinEmpty; +} |